I've been trying to run a script within another python script with no luck.
The script I am trying to run is garminbackup.py which can be found in the following repo.
https://github.com/petergardfjall/garminexport
When you run this script from the command line it asks you for some information, username, location to save files, ect...
command = 'python garminbackup.py --backup-dir=Name email --format tcx'
It then asks for a password, and once entered it starts to download the garmin files to the directory provided. My goal is to create a program which loops through multiple accounts and updates the data in each folder.
My issue is that I cannot seem to get the subprocess module in python to performance this.
I've tried the following command with no luck. It seems to get stuck on the enter password input screen and does not do anything else.
import subprocess,shlex
from subprocess import PIPE,call
import os
p = subprocess.Popen(command,stdout=subprocess.PIPE,stdin=subprocess.PIPE,shell=True)
p.stdin.write("password\n")
p.wait()
I've searched for hours with little luck. Any help is appreciated.
If you want to execute many commands one after the other in the same session/shell, you must start a shell and feed it with all the commands, one at a time followed by a new line, and close the pipe at the end.
Python allows you to execute shell commands, which you can use to start other programs or better manage shell scripts that you use for automation. Depending on our use case, we can use os. system() , subprocess. run() or subprocess.
Although the OP solved it by using the --password
option, others might stumble upon the same problem and as already pointed out by HuStmpHrr it is not a good idea to pass a password in the command line.
The called script, in this case garminbackup.py
uses getpass. As hinted in the documentation the implementaion of getpass is more complecated than just reading the stdin:
[...] On Unix, the prompt is written to the file-like object stream. stream defaults to the controlling terminal (/dev/tty) or if that is unavailable to sys.stderr (this argument is ignored on Windows).
If echo free input is unavailable getpass() falls back to printing a warning message to stream and reading from sys.stdin and issuing a GetPassWarning.
We could emulate a dummy terminal, but that is to complicated without a library (see next chapter).
Another way is to get rid of the information that there is a terminal ("If echo free input is unavailable"). This turned out harder than I expected, normally running a command as cat | command | cat
should get rid of the tty detection, but it seems getpass is either to clever or to ignorant to change its behaviour.
As already mentioned by LohmarASHAR, if you can use pexpect (pip install pexpect
) you should. It is made for exactly this and will cover all the corner cases.
For example:
child = pexpect.spawn('scp foo [email protected]:.') child.expect('Password:') child.sendline(mypassword)
This works even for commands that ask for passwords or other input outside of the normal stdio streams. For example, ssh reads input directly from the TTY device which bypasses stdin.
To stick with the OP's example, but using pexpect:
#!/usr/bin/env python
import pexpect
command = 'python garminbackup.py --backup-dir=Name email --format tcx'
p = pexpect.spawn(command) # instead of subprocess.Popen
p.expect(":") # This is waiting for the prompt "Enter password:", but I rather only use ":" to cope with internationalisation issues
p.sendline("password")
print p.read() # Output the sub-processes output
p.wait()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With