从标准输入流向Bash脚本中的Python代码进行管道传输。

12

我有一个名为 f 的 Bash 脚本,其中包含 Python 代码。该 Python 代码从标准输入读取内容。我希望能够按以下方式调用我的 Bash 脚本:

f input.txt > output.txt
在上面的示例中,Python代码将从input.txt读取并写入output.txt。 我不确定如何做到这一点。我知道如果我只想向文件写入,那么我的bash脚本会像这样。
#!/bin/bash
python << EOPYTHON > output.txt
#python code goes here
EOPYTHON

我尝试将上述代码中的第二行更改为以下内容,但没有成功

python << EOPYTHON $*

我不确定还有什么其他方式来做这件事。有任何建议吗?

编辑 我将给出一个更具体的示例。考虑以下bash脚本,f

#!/bin/bash
python << EOPYTHON 
import sys
import fileinput
for i in fileinput.input():
    sys.stdout.write(i + '\n')
EOPYTHON

我想用以下命令运行我的代码

f input.txt > output.txt

我该如何修改我的 Bash 脚本,以便将 "input.txt" 作为输入流使用?


“f < input.txt > output.txt”有什么问题吗? - rojomoke
1
我有一个bash脚本f,其中包含python代码。为什么?如果您在每个源文件中坚持使用一种语言,您可能会发现代码更易于维护。 - johnsyweb
所以我理解你的问题是想知道如何在脚本中直接重定向Python输出(即脚本被调用时将stdout重定向到output.txt)。我的答案应该可以解决这个问题,但我还是有点好奇为什么你不能只是将Python打印到stdout,然后让bash从脚本外部处理重定向呢? - Reinstate Monica Please
@Johnsyweb 你从未为另一种语言编写的脚本制作过包装器吗? - Reinstate Monica Please
1
@BroSlow:当然了,但是一个包装器可以调用外部文件而不是将其包含在内部。这种关注点的分离有助于测试和可维护性。 - johnsyweb
@BrowSlow 更新了问题。 - user2699231
6个回答

30

由于没有人提到这一点,以下是作者的要求。神奇之处在于将“-”作为参数传递给cpython(指令从stdin读取源代码):

输出到文件:

python - << EOF > out.txt
print("hello")
EOF

执行示例:

# python - << EOF
> print("hello")
> EOF
hello

由于数据不能再通过stdin传递,这里有另一个技巧:

data=`cat input.txt`

python - <<EOF

data="""${data}"""
print(data)

EOF

为什么需要“-”参数?在我看来,除非您需要将参数传递给“...”,否则“python << EOF ... EOF”同样有效。 - Big McLargeHuge
@BigMcLargeHuge,请检查您的 python --help 命令。在某些发行版中,它是默认参数,在某些发行版中则不是。省略它只是在支持的版本或二进制文件上默认使用它,而显式使用它可以保证结果。 - Reishin
这似乎比-c更准确。 - Jason Newton

9
更新的答案
如果您一定要按照您所要求的方式运行,您可以这样做:
#!/bin/bash
python -c 'import os
for i in range(3):
   for j in range(3):
     print(i + j)
'  < "$1"

将Python代码保存在名为`script.py`的文件中,然后将您的脚本`f`更改为以下内容:
#!/bin/bash
python script.py < "$1"

我想要一个解决方案,而不需要在我的目录中保存另一个文件。 - user2699231
请再看一下 - 我已经更新了,使用Bash的多行输入,当被单引号包围时。 - Mark Setchell
+1;在Python源代码字符串中使用“-c”确实很关键:它使得可以将stdin输入传递给Python代码(而不是像“<<EOPYTHON”语法一样将stdin输入解释为Python代码);它还使得可以向Python代码传递参数,后者可以通过“import sys”之后的“sys.argv[1]”等方式访问这些参数。 - mklement0

0
使用进程替换来传递Python代码。这将释放常规的IO重定向,可以像往常一样使用。
❯ seq 1 5 > input.txt
❯ python3 <input.txt <(<< EOS
import sys
print("Running script with args: ", sys.argv)
for line in sys.stdin:
  print("line is %s" % line, end='')
EOS
)
Running script with args:  ['/proc/self/fd/11']
line is 1
line is 2
line is 3
line is 4
line is 5

0

你可以通过进程的文件描述符列表来检查它,例如在 proc 文件系统中,你可以打印 stdout 的重定向位置。

readlink /proc/$$/fd/1

例如。
> cat test.sh
#!/bin/bash
readlink /proc/$$/fd/1
> ./test.sh
/dev/pts/3
> ./test.sh > out.txt
> cat out.txt 
/home/out.txt

0

@Mark Setchell's answer中的-c选项应该可以工作。

这里有一个替代方案,您可以在Python脚本中嵌入bash

#!/usr/bin/env python
import sys
import fileinput
from subprocess import call

# shell command before the python code
rc = call(r"""
some bash-specific commands here
...
""", shell=True, executable='/bin/bash')

for line in fileinput.input():
    sys.stdout.write(line) #NOTE: `line` already has a newline

# shell command after the python code
rc = call(r"""
some /bin/sh commands here
...
""", shell=True)

0
为了完整起见,您还可以使用bash进程替换来欺骗Python从替代文件描述符中读取。最近我使用的一个例子是:
IFUPDOWN_VERSION=$(apt-cache show ifupdown | grep -Po '(?<=^Version: ).*')

if ! python <(cat<<EOF
from distutils.version import LooseVersion, StrictVersion
if LooseVersion("${IFUPDOWN_VERSION}") < LooseVersion("0.8.36"):
  exit(1)
EOF
              ); then
    echo "ifupdown version doesn't support 'client no' option!"
    exit ${LINENO}
fi

这类似于调用python myscript.py

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接