Shell command fails from python, ok from shell

2019-02-24 23:42发布

I have a python script that generates a number of shell commands from the given input. The problem is that when it tries to execute the generated commands, it fails, but when i run the generated commands myself (that is, from the command line), they are executed successfully.

Here is the generated command:

find /home/me/downloader/0-29/ -type f | grep -i .rpm$ | xargs -i cp {} /home/me/downloader/builds/0-29/

Here is the error message when it is run by the python script:

find: paths must precede expression: |
Usage: find [-H] [-L] [-P] [-Olevel] [-D help|tree|search|stat|rates|opt|exec] [path...] [expression]

Could you help me understand what the problem is?

UPD: Here is the function i use for executing the generated commands:

def exec_command(command):
        process = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
        output = process.communicate()[0]
        return output

2条回答
The star\"
2楼-- · 2019-02-25 00:07

I believe what you do is you start a single program called find with many parameters, incl. | and grep and xargs - and these are not arguments to find.

What you want to do is probably to ask bash to run find and then pipe the outcome to grep, etc. One way to do that is to execute a single command called bash with two parameters (-c) and a whole string incl. piped commands, e.g.

process = subprocess.Popen(["bash", "-c", "cat /etc/issue | grep a"], stdout=subprocess.PIPE)
output=process.communicate()[0]
print output
查看更多
我只想做你的唯一
3楼-- · 2019-02-25 00:09

Since your command is a pipeline, you must set shell=True so that subprocess will send the command, as is, to the shell:

command = 'find /home/me/downloader/0-29/ -type f | grep -i .rpm$ | xargs -i cp {} /home/me/downloader/builds/0-29/'
subprocess.call(command, shell=True)

Or,

process = subprocess.Popen(command, shell=True)
output = process.communicate()[0]
return output

Also, do not do splitting in python on a command with a pipeline. This will result in find being passed | as one of its arguments instead of as a shell operator.

It also appears that the command can be simplified:

command="find /home/me/downloader/0-29/ -type f -iname '*.rpm' -exec cp {} /home/me/downloader/builds/0-29/ \;"

Since the above is no longer a pipeline, it could, with a minor modification, be split and given to subprocess with shell=False. The modification is that the single-quotes around '*.rpm' are there to protect the glob from shell expansion. With shell=False, the shell doesn't remove them. So, we have to. For shell=False and for use with command.split():

command="find /home/me/downloader/0-29/ -type f -iname *.rpm -exec cp {} /home/me/downloader/builds/0-29/ \;"
查看更多
登录 后发表回答