I want to use redirection from bash function return values to e.g. grep, and call it from python.
Even though I am using shell=True in subprocess, this does not seem to work.
Example that works in bash:
grep foo <(echo "fooline")
Example that gives the error /bin/sh: 1: Syntax error: "(" unexpected in python 3.7.3, Ubuntu 19.04:
#!/usr/bin/env python3
import subprocess
subprocess.call('grep foo <(echo "fooline")', shell=True)
According to answers like these, redirection should work with shell=True (and it does for redirecting actual files, but not return values).
EDIT:
Added shebang and python version.
The inconsistency that you observe is due to the fact that shell=True gives you a sh shell, not bash.
The following is valid in bash but not in sh.
grep foo <(echo "fooline")
Example output:
sh-3.2$ grep foo <(echo "fooline")
sh: syntax error near unexpected token `('
If you use a valid sh expression your approach will work. Alternatively you can specify which shell to use with executable='/bin/bash'. You can also use something like:
subprocess.Popen(['/bin/bash', '-c', cmd])
The error is a shell error, nothing to do with python. sh chokes on the parentheses of python syntax.
/bin/sh: 1: Syntax error: "(" unexpected
Let's add a shebang (reference: Should I put #! (shebang) in Python scripts, and what form should it take?)
And also stop using shell=True and all. Use real pipes and command lines with arguments (note: this has been tested & works on windows using a native grep command, so now this is portable)
#!/usr/bin/env python3
import subprocess
p = subprocess.Popen(['grep','foo'],stdin = subprocess.PIPE)
p.stdin.write(b"fooline\n")
p.stdin.close()
p.wait()
Related
When I run the same command in a bash script it works. When I want to pass it to shell through Python's os.system it complains because of the {and: characters.
import os
os.system('composer transaction submit -c admin#tutorial-network -d '{"$class": "org.acme.frame.auction.SetupDemo1"}'')
Shell Error:
os.system('composer transaction submit -c admin#tutorial-network -d '{"$class": "org.acme.frame.auction.SetupDemo1"}'')
^
SyntaxError: invalid syntax
You cannot nest single quotes. The string which starts with os.system(' ends at the next (unescaped) single quote.
Python offers triple quotes which provide for a trivial fix:
os.system(r"""composer transaction submit -c admin#tutorial-network -d '{"$class": "org.acme.frame.auction.SetupDemo1"}'""")
A better solution altogether is to use subprocess.run without shell=True so you don't have to understand both Python's and the shell's quoting mechanisms.
subprocess.run([
'composer', 'transaction',
'submit', '-c', 'admin#tutorial-network',
'-d', '{"$class": "org.acme.frame.auction.SetupDemo1"}'],
# probably a good idea
check=True)
For (much) more on this topic, see further https://stackoverflow.com/a/51950538/874188
I'm trying to execute a shell command from python. Here's the command :
(flock -n 200 || (echo no; exit 1) && (echo yes; cat /home/user/Desktop/instructions.json; >/home/user/Desktop/instructions.json)) 200>>/home/user/Desktop/instructions.json
I tried os.system() or subprocess.call(); However I always have the same error which is :
/bin/sh: 1: Syntax error: word unexpected
I think the problem is due to parentheses but I'm not sure.
I'm trying to read the file thanks to cat then delete its content IF it's not locked else just echo no and exit.
This command works in shell.
It seems you are trying to lock a file in Python.
In your case you have two opportunities :
You can use the python function fcntl.flock() to directly lock a file
You can also use a simple script sh in which you write #!/bin/sh, at the beginning
To execute a command from python ad it would in the shell, use shell=true as an argument:
subprocess.call("your command", shell=true)
see this post
I'm making a python script for Travis CI.
.travis.yml
...
script:
- support/travis-build.py
...
The python file travis-build.py is something like this:
#!/usr/bin/env python
from subprocess import check_call
...
check_call(r"mv !(my_project|cmake-3.0.2-Darwin64-universal) ./my_project/final_folder", shell=True)
...
When Travis building achieves that line, I'm getting an error:
/bin/sh: 1: Syntax error: "(" unexpected
I just tried a lot of different forms to write it, but I get the same result. Any idea?
Thanks in advance!
Edit
My current directory layout:
- my_project/final_folder/
- cmake-3.0.2-Darwin64-universal/
- fileA
- fileB
- fileC
I'm trying with this command to move all the current files fileA, fileB and fileC, excluding my_project and cmake-3.0.2-Darwin64-universal folders into ./my_project/final_folder. If I execute this command on Linux shell, I get my aim but not through check_call() command.
Note: I can't move the files one by one, because there are many others
I don't know which shell Travis are using by default because I don't specify it, I only know that if I write the command in my .travis.yml:
.travis.yml
...
script:
# Here is the previous Travis code
- mv !(my_project|cmake-3.0.2-Darwin64-universal) ./my_project/final_folder
...
It works. But If I use the script, it fails.
I found this command from the following issue:
How to use 'mv' command to move files except those in a specific directory?
You're using the bash feature extglob, to try to exclude the files that you're specifying. You'll need to enable it in order to have it exclude the two entries you're specifying.
The python subprocess module explicitly uses /bin/sh when you use shell=True, which doesn't enable the use of bash features like this by default (it's a compliance thing to make it more like original sh).
If you want to get bash to interpret the command; you have to pass it to bash explicitly, for example using:
subprocess.check_call(["bash", "-O", "extglob", "-c", "mv !(my_project|cmake-3.0.2-Darwin64-universal) ./my_project/final_folder"])
I would not choose to do the job in this manner, though.
Let me try again: in which shell do you expect your syntax !(...) to work? Is it bash? Is it ksh? I have never used it, and a quick search for a corresponding bash feature led nowhere. I suspect your syntax is just wrong, which is what the error message is telling you. In that case, your problem is entirely independent form python and the subprocess module.
If a special shell you have on your system supports this syntax, you need to make sure that Python is using the same shell when invoking your command. It tells you which shell it has been using: /bin/sh. This is usually just a link to the real shell executable. Does it point to the same shell you have tested your command in?
Edit: the SO solution you referenced contains the solution in the comments:
Tip: Note however that using this pattern relies on extglob. You can
enable it using shopt -s extglob (If you want extended globs to be
turned on by default you can add shopt -s extglob to .bashrc)
Just to demonstrate that different shells might deal with your syntax in different ways, first using bash:
$ !(uname)
-bash: !: event not found
And then, using /bin/dash:
$ !(uname)
Linux
The argument to a subprocess.something method must be a list of command line arguments. Use e.g. shlex.split() to make the string be split into correct command line arguments:
import shlex, subprocess
subprocess.check_call( shlex.split("mv !(...)") )
EDIT:
So, the goal is to move files/directories, with the exemption of some file(s)/directory(ies). By playing around with bash, I could get it to work like this:
mv `ls | grep -v -e '\(exclusion1\|exclusion2\)'` my_project
So in your situation that would be:
mv `ls | grep -v -e '\(myproject\|cmake-3.0.2-Darwin64-universal\)'` my_project
This could go into the subprocess.check_call(..., shell=True) and it should do what you expect it to do.
I am trying to include this line in a python script.
!#/bin/bash/env python
import os
os.system("paste <(awk '!/^($|[:space:]*#)/{print $0}' file1) <(awk '!/^($|[:space:]*#)/{print $0} file2) > out_file")
The command is perfectly fine when run from bash directly. However, inside the script, I get:
sh: -c: line0: syntax error near unexpected token `('
The problem persists when using simply:
os.system("paste <(cat file1) > output_file")
Any ideas?
The command is perfectly fine when run from bash directly. However, inside the script, I get:
sh: -c: line0: syntax error near unexpected token `('
That's because inside the script, you're running the command with sh rather than bash. Both this command, and the simpler one, use bash-specific features. Try running an sh shell and typing the same lines, and you'll get the same error.
The os.system call doesn't document what shell it uses, because it's:
implemented by calling the Standard C function system()
On most Unix-like systems, this calls sh. You probably shouldn't rely on that… but you definitely shouldn't rely on it calling bash!
If you want to run bash commands, use the subprocess module, and run bash explicitly:
subprocess.call(['bash', '-c', 'paste <(cat file1) > output_file'])
You could, I suppose, try to get the quoting right to run bash as a subshell within the shell system uses… but why bother?
This is one of the many reasons that the documentation repeatedly tells you that you should consider using subprocess instead of os.system.
Kill two birds with one awk script:
awk -v DELIM=' ' '!/^($|[[:space:]]*#)/{a[FNR]=a[FNR]DELIM$0}END{for(i=1;i<=FNR;i++)print substr(a[i],2)}' file1 file2
This eliminates the need for process substitution and is therefor sh compliant.
Assume I have a file at http://mysite.com/myscript.sh that contains:
#!/bin/bash
echo "Hello $1"
From the command line, I can execute my script (without downloading it) using the following command:
bash <(curl -s http://mysite.com/myscript.sh) World
Now, instead of executing the above command from the command line, I want to execute it from a python script. I tried doing the following:
import os
os.system('bash <(curl -s http://mysite.com/myscript.sh) World')
...but I get the following error:
sh: -c: line 0: syntax error near unexpected token `('
How do I make this execute correctly in python?
Evidently, os.system runs its command through /bin/sh, which usually causes whichever shell it's linked to to drop to a compatibility mode that doesn't include the <(...) construction. You can get around it by either storing the result in a temporary file or using another level of shell. Ugly, but it works.
os.system('bash -c "bash <(curl -s http://mysite.com/myscript.sh) World"')
There is a libcurl for python so you don't have to go the way around to command line behaviour. Here's the function list that should really do it - have never run remote scripts myself though. If you need installing the python binding, the instructions are here.
import curl