IPython不会捕获某些命令输出(例如,ack)

时间:2018-12-24 02:59:02

标签: shell ipython

我不明白为什么IPython不将某些系统命令的结果分配给python变量。 ackag可执行文件对我来说似乎一直在发生这种情况

例如,以下命令产生输出:

In [1]: !ack --nocolor foo
bar
1:foo

但是,每当将结果保存到变量中时,我都会得到一个空输出

In [2]: out=!ack --nocolor foo

In [3]: out
Out[3]: []

即使尝试各种技巧,我仍然会遇到此问题:

In [4]: out=!ack --nocolor foo > tmp; sleep 1; cat tmp

In [5]: out
Out[5]: []

实际上,tmp在最后一种情况下为空,这表明输出捕获将这些命令弄乱了。

有人问这是IPython或ack / ag的问题,还是我对IPython在这里的行为的误解?

1 个答案:

答案 0 :(得分:2)

我推断out = !cmd使用%sx。这与!cmd的运行方式不同(请参见%sw%system的文档)。

%sx经历了几层函数,最终调用了

# import IPython
IPython.utils._process_common.process_handler

其代码类似于@Elliott Frisch在删除的答案中使用的subprocess调用:

p = subprocess.Popen("ack --nocolor foo", stdout=subprocess.PIPE, shell=True)
(output, err) = p.communicate()

我在以下位置提取了process_handler代码:

def cmd1(astr='ack --nocolor 15 *.txt'):
    callback = lambda p: p.communicate()
    stderr = subprocess.PIPE
    stderr = subprocess.STDOUT
    shell = True
    close_fds = True
    executable = None
    p = subprocess.Popen(astr, 
                         shell=shell,
                         executable=executable,
                         #stdin=subprocess.PIPE,
                         stdout=subprocess.PIPE,
                         stderr=stderr,
                         close_fds=close_fds,
                         )
    out = callback(p)
    return out, p.returncode

这有效:

In [40]: cmd1()
Out[40]: 
((b'stack53269737.txt:2:11 12 13 14 15 16\ntest.txt:3:11\t12 13 14 15\ntest1.txt:5:  0.054181,  0.506962,  0.315159,  0.653104\n',
  None),
 0)

但是如果我取消注释stdin行,它将失败:

In [42]: cmd1()
Out[42]: ((b'', None), 1)

所以是

stdin=subprocess.PIPE,
导致ack调用失败的

参数。不会对其他常见的Shell命令(例如lsgrep)造成问题。


ack帮助包括:

 --[no]filter               Force ack to treat standard input as a pipe
                            (--filter) or tty (--nofilter)

--nofilter添加到我的命令中(此重定向不需要--nocolor

In [50]: cmd1('ack --nofilter 15 *.txt')
Out[50]: 
((b'stack53269737.txt:2:11 12 13 14 15 16\ntest.txt:3:11\t12 13 14 15\ntest1.txt:5:  0.054181,  0.506962,  0.315159,  0.653104\n',
  None),
 0)

In [51]: out = !ack --nofilter 15 *.txt
In [52]: out
Out[52]: 
['stack53269737.txt:2:11 12 13 14 15 16',
 'test1.txt:5:  0.054181,  0.506962,  0.315159,  0.653104',
 'test.txt:3:11\t12 13 14 15']

这就是关键-强制ack忽略管道输入(尽管我不完全了解细节)。