How to add an append symbol in a subprocess in python - python

I have the following code, which runs a command with arguments
subprocess.call(["schtasks.exe", "/Query","/V","/FO","csv",">>", r"D:/temp/fo3.csv"])
However I am having problems with the >> part of the argument, the command does not seem to like it, it gives the following message
ERROR Invalid argument /option - >>
So my question is how do i get the >> argument to work?

when you pass arguments as a list like that, subprocess passes each argument to the program. What is probably happening is that your program (schtasks.exe) is seeing the argument >> and it doesn't know what to do with it. When you run this in the shell, >> is interpreted by the shell as redirection and so your program never sees it.
You have 2 options, pass the arguments as a string and use shell = True. This is not recommended if you are accepting user input which can modify the string you're passing to subprocess as it would lead to a security risk in your program.
Your second option is to pass an open file object to stdout, e.g.
f = open(r"D:/temp/fo3.csv",'a')
subprocess.call(["schtasks.exe", "/Query","/V","/FO","csv"], stdout = f)

Output redirection (the >>) is a shell feature and won't work if you call the program directly. Use shell=True in the call.

Related

Python to call shell script with arguments

I have a shell script I need to call from a Python script. Only thing is that is needs to pass 7 parameters, that are basically variables, and some of those contain spaces.
I see a lot of references to os.system, and to subprocess, but what about the parameters with spaces? I think those will cause an issue.
Edit 2:
subject = "Proposal"
to = "LandonStatis#stackoverflow.com"
from = "Joshua#stackoverflow.com"
REPORT_DIRECTORY = "/home/reports"
file = "/home/files/proposal.txt"
message = "Please accept me as the best answer"
#i didnt include the "flag" because i dont know the flag format
os.system('./home/scripts/send_email.pl "'+subject+'" "'+to+'" "'+from+'" "'+REPORT_DIRECTORY+'" "'+file+'" "'+message+'"')
Edit:
If you want to add a variable, you can do so like this
os.system('ls -al '+str(x))
For example, if your username is "landon statis" with a space,
you can do this: os.system('ls -al "/home/landon statis/Desktop"')
(notice the space in the command above?)
This works for both Windows and Linux.
Use double quotes to prevent word splitting. An argument enclosed in
double quotes presents itself as a single word, even if it contains
whitespace separators.
This is the source, which is a link to Linux Documentation Project. Check them out, it's very useful: Advanced Bash Scripting Guide: Chapter 5 - Quoting
Please do not use os.system or subprocess with shell=True. Both execute a shell and are potentially vulnerable to shell injections. See https://docs.python.org/3/library/subprocess.html#security-considerations for more details.
Best way is to use subprocess and hand it a list of arguments. Example: Let's just use a Shell script that print its arguments. myscript:
#!/bin/sh
for arg in "$#"; do
echo "argument: $arg"
done
Then you can call this script using a list as argument:
subprocess.run(["./myscript", "first", "argument with space", "third"])
Output:
argument: first
argument: argument with space
argument: third

Python: Subprocess works different to terminal. What I have to change?

I have to Python scripts: Tester1.py and Tester2.py.
Within Tester1 I want to start from time to time Tester2.py. I also want to pass Tester2.py some arguments. At the moment my code looks like this:
Tester1:
subprocess.call(['python3 Tester2.py testString'])
Tester2:
def start():
message = sys.argv[1]
print(message)
start()
Now my problem: If I run with my terminal Tester2 like 'python3 Tester2.py testString'my console prints out testString. But if I run Tester1 and Tester1 tries to start Tester2, I get an IndexError: "list index out of range".
How do I need to change my code to get everything working?
EDIT:
niemmi told me that I have to change my code to:
subprocess.call(['python3', 'Tester2.py', 'testString'])
but now I get a No such file or directory Error although both scripts are in the same directory. Someone knows why?
You need to provide the arguments either as separate elements on a list or as a string:
subprocess.call(['python3', 'Tester2.py', 'testString'])
# or
subprocess.call('python3 Tester2.py testString')
Python documentation has following description:
args is required for all calls and should be a string, or a sequence of program arguments. Providing a sequence of arguments is generally preferred, as it allows the module to take care of any required escaping and quoting of arguments (e.g. to permit spaces in file names). If passing a single string, either shell must be True (see below) or else the string must simply name the program to be executed without specifying any arguments.

Argparse Arg with > flag

I have a commandline script that works perfectly fine. Now I want to make my tool more intuitive.
I have:
parser.add_argument("-s",help = "'*.sam','*.fasta','*.fastq'", required=True)
right now, python script.py -s savefile.sam works but I would like it to be python script.py > savefile.sam
parser.add_argument("->",help = "'*.sam','*.fasta','*.fastq'", required=True)
does not work as it gives: error: unrecognized arguments: -
can I do this with argparse or should I settle for -s?
> savefile.sam is shell syntax and means "send output to the file savefile.sam". Argparse won't even see this part of the command because the shell will interpret it first (assuming you issue this command from a suitable shell).
While your command does make sense, you shouldn't try to use argparse to implement it. Instead, if an -s isn't detected, simply send the script's output to stdout. You can achieve this by setting the default for -s:
parser.add_argument("-s",
type=argparse.FileType("w"),
help="'*.sam','*.fasta','*.fastq'",
default=sys.stdout)
This way, you can run python script.py > savefile.sam, and the following will happen:
The shell will evaluate python script.py.
argparse will see no additional arguments, and will use the default sys.stdout.
Your script will send output to stdout.
The shell will redirect the script's output from stdout to savefile.sam.
Of course, you can also send the stdout of the script into the stdin the another process using a pipe.
Note that, using FileType, it's also legal to use -s - to specify stdout. See here for details.
In a sense your argparse works
import argparse
import sys
print sys.argv
parser=argparse.ArgumentParser()
parser.add_argument('->')
print parser.parse_args('-> test'.split())
print parser.parse_args()
with no arguments, it just assigns None to the > attribute. Note though that you can't access this attribute as args.>. You'd have to use getattr(args,'>') (which is what argparse uses internally). Better yet, assign this argument a proper long name or dest.
1401:~/mypy$ python stack29233375.py
['stack29233375.py']
Namespace(>='test')
Namespace(>=None)
But if I give a -> test argument, the shell redirection consumes the >, as shown below:
1405:~/mypy$ python stack29233375.py -> test
usage: stack29233375.py [-h] [-> >]
stack29233375.py: error: unrecognized arguments: -
1408:~/mypy$ cat test
['stack29233375.py', '-']
Namespace(>='test')
Only the - passes through in argv, and on to the parser. So it complains about unrecognized arguments. An optional positional argument could have consumed this string, resulting in no errors.
Notice that I had to look at the test file to see the rest of the output - because the shell redirected stdout to test. The error message goes to stderr, so it doesn't get redirected.
You can change the prefix char from - (or in addition to it) with an ArgumentParser parameter. But you can't use such a character alone. The flag must be prefix + char (i.e. 2 characters).
Finally, since this argument is required, do you even need a flag? Just make it a positional argument. It's quite common for scripts to take input and/or output file names as positional arguments.

command line arguments with subprocess.Popen

I've GUI where if i press a button "process" calls a python script which needs one command line argument. so i used subprocess.Popen like this:
subprocess.Popen(['chbif /home/vnandak/Work/VS30_Cradle/SV000_sv000/cradle_vs30_dkaplus01_fwd_dl140606_fem140704_v00_mod1.bif'],shell=True)
chbif is the alias for the .py script
This works fine but now i want to choose a file of my choice by browsing using askfilename() func from tkFileDailog() module. how can i do this?
I thought of doing it like this:
def bifcall():
name13= askopenfilename()
subprocess.Popen(['chbif', name13],shell=True)
But if i use this it throws an error saying that it does'nt have one more command line argument which is the file name
If you pass shell=True, the first argument should be a string, and it will be interpreted as the shell would interpret it. If shell=False, then the first argument should be a list, which sort of bypasses the shell.
Here's an answer to another question that explains it well: https://stackoverflow.com/a/15109975/451201
Because if you use shell=True, you can only use one string as the first argument.
Try to change
subprocess.Popen(['chbif', name13],shell=True)
to
subprocess.Popen('chbif ' + name13,shell=True)
But it is not recommended to use shell=True for security reasons (See https://docs.python.org/2/library/subprocess.html#frequently-used-arguments for details).

python subprocess module to call shell (Bash) script

I am trying to call a shell (Bash) script from python. The script is in my /home/user/bin directory with execute permission for group & user, i.e., -rwxr-xr--. I am using subprocess.check_call(["/home/user/bin/script.sh %s %s" % (subj,-6)],shell=True) and this is generating an exit status 127 code. Adding stderr=subprocess.STDOUT to the command does nothing to elucidate. Here is the exact output:
CalledProcessError: Command
'['/home/.../bin/MNE_setup_source_space.sh kubi_td104 -6']'
returned non-zero exit status 127`
I believe this might be a PATH related issue, is that correct? I don't know how to resolve this. If I am already passing in the absolute path to the executable how can there be a PATH issue?
Thanks in advance
Do not use shell=True. Do not pass arguments as part of argv[0]. Pass your argument vector as a vector -- which is to say, in Python, a list:
subprocess.check_call(["/home/user/bin/script.sh", str(subj), "-6"])
If you were going to use shell=True, you would do it like so:
subprocess.check_call("/home/user/bin/script.sh %s %s" % (subj,-6), shell=True)
...which is to say, you wouldn't use a list form at all.
To clarify why what you're currently trying is failing -- because you're using shell=True, it's trying to pass only the first list element as a script, and additional arguments as extra argv elements which would only be read or interpreted if the script passed in the first argument chose to look at them (as by referring to "$1", "$2", or the like).
shell=True is only needed in very rare circumstances where you need a shell to perform redirections or logic before starting the program you're trying to run, and comes with serious security concerns if any unvetted input is incorporated into the command being run. Do not use it unless you're very, very sure you need to.

Categories

Resources