How not to escape an ampersand with python subprocess - python-3.x

I'd like to execute with subprocess.Popen() this command containing an ampersand to be interpreted as a batch concatenation operator:
COMMAND="c:\p\a.exe & python run.py"
subprocess.Popen(COMMAND,cwd=wd,shell=False)`
The ampersand & however is interpreted as an argument of a.exe and not as a batch operator.
Solution 1: Having seen the related question, a solution could be to set shell=True but that gives the error 'UNC path are not supported', since my working directory is remote. This solution does not work as it is.
Solution2: The executable a.exe should take a -inputDir parameter to specify the remote location of the files and use a local working directory. I think this solution could work but I may not have the source code of the executable.
Solution3: I could instead write c:\p\a.exe & python run.py into command.bat and then use
COMMAND="c:\p\command.bat"
subprocess.Popen(COMMAND,cwd=wd,shell=False)`
Could this approach work?
Solution4: I am trying to solve this changing only the subprocess.Popen() call. Is it possible to do it? Based on the python Popen doc I suspect is not possible. Please tell me I am wrong.
See also this related questions.
UPDATE:
Solution 5: #mata suggested to use Powershell Popen(['powershell', '-Command', r'C:\p\a.exe; python run.py']). This actually works, but now I have to deal with slightly different commands, and being lazy I've decided to use solution 3.
My favourite solution was Solution 3, to create a .bat file and call it
COMMAND="c:\p\command.bat"
subprocess.Popen(COMMAND,cwd=wd,shell=False)

I would use Solution 3
The & character is used to separate multiple commands on one command line.
Cmd.exe runs the first command, and then the second command.
In this case you could just write your batch file like this:
#echo off
c:\p\a.exe
python run.py
Also, it's worth noting when using cmd.exe:
The ampersand (&), pipe (|), and parentheses ( ) are special characters
that must be preceded by the escape character (^) or quotation marks
when you pass them as arguments.

Related

Multi line command to os.system

There may be something obvious that I'm missing here but searching google/so has not provided anything useful.
I'm writing a python script utilizes tkinter's filedialog.askopenfilename to open a file picker. Without getting into to much detail, I have the following line, which serves to bring the file picker to the front of the screen (taken directly from this helpful answer):
os.system('''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "Python" to true' ''')
As you can see from the above code snippet, this line is too long for pep8 guidelines and I'd like to break it down.
However, despite my best efforts I can't seem to get it to split. This is due (I think) to the fact that the line contains both single and double quotes, and unfortunately os.system seems to insist on it being a single line.
I've tried
Triple quotes
String literal patching (\ at end, and + at beginning of each line)
Triple quotes on a per line basis
If it's relevant: using OSX and running python 3.6.4.
What is the correct (and ideally, minimal) way to go about breaking this line down?
Using the much improved subprocess module is usually a much better, more powerful, and safer way to call an external executable.
You can of course pass variables with \n in them as the arguments as well.
Note, the double (()) are because the first parameter is a tuple.
import subprocess
subprocess.call((
'/usr/bin/osascript',
'-e',
'tell app "Finder" to set frontmost of process "Python" to true',
))
There are at times reasons to call through the shell, but not usually.
https://docs.python.org/3.6/library/subprocess.html

command with multiple argument on shell script

I am working with the graphing tool xmgrace and I am trying to plot multiple datasets. Rewriting the command with all the arguments over and over is becoming a waste of time, so I decided to make a shell script called xmgraceScript.
At the moment my shell script looks like this:
xmgrace dirA/argA dirB/argB dirC/argC
since the dir paths are pretty long I would like to have each argument in a new line, just to make the script more readable. I tried to do this by writing:
xmgrace << _XMARGS_
dirA/argA
dirB/argB
dirC/argC
_XMARGS_
this does not work. Can anyone recommend a different way of doing so?
thank you
Simply end all lines other than the last with a backslash character:
xmgrace \
dirA/argA \
dirB/argB \
dirC/argC
(The '<<' construct feeds the enclosed content to the standard input of the command.)

How to escape colon (:) in $PATH on UNIX?

I need to parse the $PATH environment variable in my application.
So I was wondering what escape characters would be valid in $PATH.
I created a test directory called /bin:d and created a test script called funny inside it. It runs if I call it with an absolute path.
I just can't figure out how to escape : in $PATH I tried escaping the colon with \ and wrapping it into single ' and double " quotes. But always when I run which funny it can't find it.
I'm running CentOS 6.
This is impossible according to the POSIX standard. This is not a function of a specific shell, PATH handling is done within the execvp function in the C library. There is no provision for any kind of quoting.
This is the reason why including certain characters (anything not in the "portable filename character set" - colon is specifically called out as an example.) is strongly recommended against.
From SUSv7:
Since <colon> is a separator in this context, directory names that might be used in PATH should not include a <colon> character.
See also source of GLIBC execvp. We can see it uses the strchrnul and memcpy functions for processing the PATH components, with absolutely no provision for skipping over or unescaping any kind of escape character.
Looking at the function
extract_colon_unit
it seems to me that this is impossible. The : is unconditionally and
inescapably used as the path separator.
Well, this is valid at least for bash. Other shells may vary.
You could try mounting it
mount /bin:d /bind
PATH=/bind
According to http://tldp.org/LDP/abs/html/special-chars.html single quotes should preserve all special characters, so without trying it, I would think that '/bin:d' would work (with)in $PATH.

How to write each line of the result of a a system() call into a list in Vimscript?

I'd like to loop over the files in a directory using Vimscript. Reading usr_41.txt and having searched around, the best I can come up with is something like let dir_contents = system('ls')
But since system() isn't returning a list, I can't loop over it. Is there either a way I can save the results of a system call as a list, or a Vim command or function that does so already?
You can get a list with split(system('ls'), '\n'), which will give you a list of files providing you don't have files with newlines in them.
Try something like
split(system('ls', nr2char(10))
I am currently not on a Unix system, so I can't try it myself, but it works on Windows (and dir), such as
for FILE in split(system('dir /b', nr2char(10))
echo 'File is: ' . FILE
endfor
There is a built-in glob() function. To get a list of files in current directory on windows you could use split(glob('.\\*'), "\n"). On *nix it is much more complicated as
POSIX allows everything except NULL to be in filename. Everything here means that newline ("\n") is also allowed.
glob() function does not return filenames starting with dot unless explicitely requested (using glob('dir/.*')).
When explicitely requested to list filenames starting with dot glob() also shows . (current directory) and .. (parent directory) special directories.
In order to solve this problems you have to use something like this (or use vim with python support and python's own os.listdir function).
If you don't mind having frawor in dependencies, you could do the following:
execute frawor#Setup('0.0', {'#/os': '0.0'})
<...>
let dir_contents=s:_r.os.listdir('.')
About getting list of lines from a shell command: if you know that command you launch won't output NULLs, you can use split(system(cmd), "\n", 1) (maybe without last argument if you don't care about empty lines). If you know that command may output NULLs and you want to keep them, you have to do more work:
noautocmd new
read !cmd
let s:shell_output=getline(2, line('$'))
noautocmd bwipeout!
Note that NULLs in this case will get replaced with newlines inside a s:shell_output list, while actual newlines will be represented as string ends.
I just discovered that there is a subtlety to these answers:
Currently (Jan 2018), vim has systemlist() that will answer your needs quite well:
let l:ls=systemlist("ls")
" or let dir_contents=etc etc
However, if you are doing something besides "ls" you might run into a weird bit of tricky behavior: trailing blank lines will get dropped (as of Jan 2018). If this is not a problem, don't worry, use systemlist() and be happy. If it is a problem, you probably want to do something like this:
let l:myvar=system("myprogram -that -returns -blank -lines")
l:mylist=split(l:myvar, "\n", 1) " the 1 is to keep empty lines
Hope that helps someone!
systemlist added in Vim 7.4:248 does exactly what you want:
:echo systemlist('printf "a\nb\n"')
output:
['a', 'b']
From the docs http://vimhelp.appspot.com/eval.txt.html#eval.txt
systemlist({expr} [, {input}]) systemlist()
Same as system(), but returns a List with lines (parts of
output separated by NL) with NULs transformed into NLs. Output
is the same as readfile() will output with {binary} argument
set to "b".
Returns an empty string on error.

exec() security

I am trying to add security of GET query to exec function.
If I remove escapeshellarg() function, it work fine. How to fix this issue?
ajax_command.php
<?php
$command = escapeshellarg($_GET['command']);
exec("/usr/bin/php-cli " . $command);
?>
Assume $_GET['command'] value is run.php -n 3
What security check I can also add?
You want escapeshellcmd (escape a whole command, or in your case, sequence of arguments) instead of escapeshellarg (escape just a single argument).
Notice that although you have taken special precautions, this code allows anyone to execute arbitrary commands on your server anyways, by specifying the whole php script in a -r option. Note that php.ini can not be used to restrict this, since the location of it can be overwritten with -c. In short (and with a very small error margin): This code creates a severe security vulnerability.
escapeshellarg returns a quoted value, so if it contains multiple arguments, it won't work, instead looking like a single stringesque argument. You should probably look at splitting the command up into several different parameters, then each can be escaped individually.
It will fail unless there's a file called run.php -n 3. You don't want to escape a single argument, you want to escape a filename and arguments.
This is not the proper way to do this. Have a single PHP script run all your commands for you, everything specified in command line arguments. Escape the arguments and worry about security inside that PHP file.
Or better yet, communicate through a pipe.

Resources