Subprocess command not finding files using ls command?

时间:2015-07-31 20:38:30

标签: python subprocess python-2.6

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.

4 个答案:

答案 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)

通过Python运行的

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?

Edit

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