I am creating a program that will pull in a list of account numbers and then run an ls -lh
command to find a file for each one. When I run my command on our Linux server without Python it pulls up the files no problem, but then when I do it through Python it says it can't find them.
import subprocess as sp
sp.call(['cd', input_dir])
for i, e in enumerate(piv_id_list):
proc_out = sp.Popen(['ls', '-lh', '*CSV*APP*{0}.zip'.format(e)])
proc_out_list.append(proc_out)
print(proc_out)
Here is some sample output when I run the commands through the Python interpreter:
>>> ls: cannot access *CSV1000*APP*: No such file or directory
But through Linux the same command:
ls -lh *CSV*APP*
It returns the output like it should.
答案 0 :(得分:5)
This is because the shell does the replacement of wildcards with existing files that match the pattern. For example, if you have a.txt
and b.txt
, then ls *.txt
will be expanded from the shell to ls a.txt b.txt
. With your command you actually ask for ls
to return info about a file containing an asterisk in its filename. Use the following if you want to verify:
sp.Popen(['bash', '-c', 'ls', '-lh', '*CSV*APP*{0}.zip'.format(e)])
Also you should use os.chdir
to change the directory, since sp.call(['cd', input_dir])
changes the current directory for the new process you created and not the parent one.
答案 1 :(得分:3)
ls
可能是正确的:我猜在当前目录中是没有名为*CSV*APP*
的文件。可能有一个名称与该glob模式匹配的文件。但是ls
并不关心整体。在shell上运行命令时会发生的情况是,shell将glob扩展为它在当前目录中可以看到的匹配文件名,这些扩展名是shell传递给ls
的。
要在shell中获得与Python相同的结果(为了演示,不是因为你想要那样),用单引号保护参数不受全局扩展的影响:
ls -lh '*CVS*APP*'${e}'.zip'
但是如何在Python中获得shell的行为?您可以使用shell=True
作为其他一些答案建议,但这是一个滑坡,因为在动态生成的字符串上调用实际的shell(可能依赖于更复杂的应用程序中的用户输入)会使您容易受到命令注入的攻击和其他的肮脏。
在这里,你只需要一个特定的shell行为,文件名为globbing。 Python碰巧能够做到all by itself:
import subprocess as sp
from glob import glob
sp.call(['cd', input_dir])
for i, e in enumerate(piv_id_list):
proc_out = sp.Popen(['ls', '-lh', glob('*CSV*APP*{0}.zip'.format(e))])
proc_out_list.append(proc_out)
print(proc_out)
作为JuniorCompressor has pointed out,这仍然会查找错误的目录,因为cd
只会影响 cd
调用的子进程,所以让我们修复那也是:
import subprocess as sp
from glob import glob
os.chdir(input_dir)
for i, e in enumerate(piv_id_list):
proc_out = sp.Popen(['ls', '-lh', glob('*CSV*APP*{0}.zip'.format(e))])
proc_out_list.append(proc_out)
print(proc_out)
注意
您可以直接使用slightly higher-level sp.check_output
代替underlying sp.Popen
。
答案 2 :(得分:2)
You should use cwd
argument to Popen
and shell=True
, then communicate
to get the output.
Your code would look like:
import subprocess as sp
for i, e in enumerate(piv_id_list):
proc = sp.Popen(['ls', '-lh', '*CSV*APP*{0}.zip'.format(e)], cwd=input_dir, stdout=sp.PIPE, shell=True)
proc_out_list.append(proc.communicate()[0])
print(proc_out_list[-1])
But why are you making a subprocess instead of using the standard lib?
Like @tripleee said, it replace a few functions only.
I think it's better to use builtins/stdlib when it's possible; in your case you "only" want to list files for a given pattern (glob
) and show ordered (sorted
) informations about their size (stat
).
Using the stdlib make your code more portable; even if you don't care about Microsoft Windows portability, you might want to avoid running into surprises running your code on computer without GNU binutils (ie: Mac OS, BSD, ...).
You want to use the subprocess
module for things that cannot (easily) be implemented in pure Python (ie.: encode videos with ffmpeg
, change the user passowrd with passwd
, escalating privileges with sudo
, ...).
答案 3 :(得分:1)
I believe you need to add shell=True
as a parameter to Popen and replacing the list with one string:
proc_out = sp.Popen('ls -lh *CSV*APP*{0}.zip'.format(e), shell=True)
See here for more information and possible usage of glob
: Python subprocess wildcard usage