如何将字符串格式应用于 bash 命令(通过子进程合并到 Python 脚本中)?



我想在我的 Python 脚本中添加一个 bash 命令,该命令线性化 FASTA 序列文件,同时保持序列分离不变(因此命令的特定选择)。下面是命令,带有"inputfile.txt"的示例输入文件:

awk '/^>/ {printf("n%sn",$0);next; } { printf("%s",$0);}  END {printf("n");}' < inputfile.txt

目的是允许用户在命令行中指定要修改的文件,例如:

$ python3 program.py inputfile.txt

我尝试使用字符串格式(即%s)与sys.argv一起实现这一目标。但是,我已经尝试了许多不同的"'位置,但仍然无法使其工作并接受来自此处命令行的用户输入。 (该命令包含诸如n之类的转义,因此我试图通过添加额外的反斜杠以及命令中现有%s的额外%来抵消这一点。

import sys
import subprocess
path = sys.argv[1]
holder = subprocess.Popen("""awk '/^>/ {printf("\n%%s\n",$0);next; } { printf("%%s",$0);}  END {printf("\n");}' < %s""" % path , shell=True, stdout=subprocess.PIPE).stdout.read()
print(holder)

我非常感谢任何帮助识别这里的语法错误,或有关如何添加此用户输入的建议。

TL;大卫:不要掏钱去纠结!只需使用Python。但是让我们一步一步来...


在这里使用三引号的直觉很好,那么至少你不需要转义单引号和双引号,这是你在 shell 字符串中需要的。

您可以使用的下一个有用设备是原始字符串,使用r'...'r"..."r"""...""".原始字符串不会扩展反斜杠转义,因此在这种情况下,您可以保持n不变。

最后是%s,如果您使用%运算符,则需要对其进行转义,但在这里我建议不要使用 shell 重定向输入,只需使用 Python 的子进程从文件发送 stdin!简单得多,你最终没有替代品。

我还建议您使用subprocess.check_output()而不是Popen()。它使用起来要简单得多,而且更健壮,因为它将检查命令是否成功退出(退出状态为零)。

将它们放在一起(到目前为止),您可以得到:

with open(path) as inputfile:
holder = subprocess.check_output(
r"""awk '/^>/ {printf("n%sn",$0);next; } { printf("%s",$0);}  END {printf("n");}'""",
shell=True,
stdin=inputfile)

但是在这里你可以更进一步,因为你不再需要 shell,它只用于将命令行拆分为两个参数,所以只需在 Python 中进行此拆分(几乎总是可能且容易做到这一点,而且它更加健壮,因为你不必处理 shell 的单词拆分!

with open(path) as inputfile:
holder = subprocess.check_output(
['awk', r'/^>/ {printf("n%sn",$0);next; } { printf("%s",$0);}  END {printf("n");}'],
stdin=inputfile)

列表中的第二个字符串仍然是原始字符串,因为您希望保留 bacsklash 转义。

我可以介绍如何在 awk 中不使用printf()来代替使用print来做到这一点,这应该可以同时消除n%s,但相反,我会告诉你,直接在 Python 中做你正在做的事情要容易得多!

事实上,awk(或sed,tr,cut等)可以做的所有事情,Python都可以做得更好(或者至少以更具可读性和可维护性的方式)。

对于您的特定代码:

with open(path) as inputfile:
for line in inputfile:
if line.startswith('>'):
# Insert a blank line before this one.
print()
print(line)
if line.startswith('>'):
# Also insert a blank line after this.
print()
# And a blank line at the end.
print()

这不是更好吗?

你可以把它放到一个函数中,一个模块中,然后在任何你想要的地方重用它。将结果存储在字符串中很容易,如果您愿意,可以将其保存到变量中,更加灵活......

无论如何,如果您仍然想坚持炮击,请参阅我以前的代码,我认为这是您在仍在掏出时可以做的最好的事情,而无需显着更改外部命令。

最新更新