python - 如何从 python 中传输许多 bash 命令?

标签 python bash subprocess pipe

您好,我正在尝试从 python 调用以下命令:

comm -3 <(awk '{print $1}' File1.txt | sort | uniq) <(awk '{print $1}' File2.txt | sort | uniq) | grep -v "#" | sed "s/\t//g"

当 comm 命令的输入也通过管道传输时,我该如何进行调用?

是否有简单直接的方法来做到这一点?

我尝试了 subprocess 模块:

subprocess.call("comm -3 <(awk '{print $1}' File1.txt | sort | uniq) <(awk '{print $1}' File2.txt | sort | uniq) | grep -v '#' | sed 's/\t//g'")

没有成功,它说: OSError: [Errno 2] 没有那个文件或目录

或者我是否必须单独创建不同的调用,然后使用 PIPE 传递它们,如子流程文档中所述:

p1 = Popen(["dmesg"], stdout=PIPE)
p2 = Popen(["grep", "hda"], stdin=p1.stdout, stdout=PIPE)
p1.stdout.close()  # Allow p1 to receive a SIGPIPE if p2 exits.
output = p2.communicate()[0]

最佳答案

进程替换 ( <() ) 是仅限 bash 的功能。因此,您需要一个 shell,但它不能只是任何 shell(例如 /bin/sh,在非 Windows 平台上由 shell=True 使用)——它需要是 bash

subprocess.call(['bash', '-c', "comm -3 <(awk '{print $1}' File1.txt | sort | uniq) <(awk '{print $1}' File2.txt | sort | uniq) | grep -v '#' | sed 's/\t//g'"])

顺便说一下,如果你打算使用任意文件名走这条路线,请将它们带外传递(如下所示:传递 _ 作为 $0File1.txt 作为 $1 ,和File2.txt 作为 $2 ):

subprocess.call(['bash', '-c',
  '''comm -3 <(awk '{print $1}' "$1" | sort | uniq) '''
  '''        <(awk '{print $1}' "$2" | sort | uniq) '''
  '''        | grep -v '#' | tr -d "\t"''',
  '_', "File1.txt", "File2.txt"])

也就是说,最佳实践方法确实是自己设置链。下面是用 Python 3.6 测试的(注意需要 pass_fdssubprocess.Popen 的参数才能使通过 /dev/fd/## 链接引用的文件描述符可用):

awk_filter='''! /#/ && !seen[$1]++ { print $1 }'''

p1 = subprocess.Popen(['awk', awk_filter],
                      stdin=open('File1.txt', 'r'),
                      stdout=subprocess.PIPE)
p2 = subprocess.Popen(['sort', '-u'],
                      stdin=p1.stdout,
                      stdout=subprocess.PIPE)
p3 = subprocess.Popen(['awk', awk_filter],
                      stdin=open('File2.txt', 'r'),
                      stdout=subprocess.PIPE)
p4 = subprocess.Popen(['sort', '-u'],
                      stdin=p3.stdout,
                      stdout=subprocess.PIPE)
p5 = subprocess.Popen(['comm', '-3',
                       ('/dev/fd/%d' % (p2.stdout.fileno(),)),
                       ('/dev/fd/%d' % (p4.stdout.fileno(),))],
                      pass_fds=(p2.stdout.fileno(), p4.stdout.fileno()),
                      stdout=subprocess.PIPE)
p6 = subprocess.Popen(['tr', '-d', '\t'],
                      stdin=p5.stdout,
                      stdout=subprocess.PIPE)
result = p6.communicate()

这是更多的代码,但是(假设文件名在现实世界中被参数化)它也是更安全的代码——你不容易受到像 ShellShock 这样由启动 shell 的简单操作,无需担心带外传递变量以避免注入(inject)攻击(除了在命令参数的上下文中——比如 awk——它们本身就是脚本语言解释器) .


也就是说,另一件需要考虑的事情就是用原生 Python 实现整个事情。

lines_1 = set(line.split()[0] for line in open('File1.txt', 'r') if not '#' in line)
lines_2 = set(line.split()[0] for line in open('File2.txt', 'r') if not '#' in line)
not_common = (lines_1 - lines_2) | (lines_2 - lines_1)
for line in sorted(not_common):
  print line

关于python - 如何从 python 中传输许多 bash 命令?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43812939/

相关文章:

python - 使用列表理解根据字符长度从系列中删除字符串Python

python - 为什么我的 TimedRotatingFileHandler 不在午夜轮换?

python - 如何在python中对目录进行tar文件备份

Python subprocess.Popen 作为 Windows 上的不同用户

python - 在Python中将密码发送到sftp子进程

python - 使用 subprocess 模块会释放 python GIL 吗?

python - 使用 xlsxwriter 编码的 pandas 导致数据帧文本出现 'u' 前缀

bash - Awk 搜索并附加来自其他 csv 文件的匹配名称

bash - 在bash中,如何批量显示文件中某行的文本?

regex - 如何使用sed分割字符串?