从嵌入式Python在Bash中读取标准输入

6
有人能解释一下为什么这个简短的bash / python命令没有输出“hello”吗?
$ echo hello | python - <<END
import sys
for line in sys.stdin:
  print line
END

如果我将我的Python脚本保存到文件中,该命令会按预期工作。
在script.py文件中:
import sys
for line in sys.stdin:
  print line
END

$ echo "hello" | python script.py

"hello"

3个回答

7
原因是您有冲突的重定向。echo | python试图将标准输入绑定到来自echo的管道,而python - <<HERE试图将标准输入绑定到此处文档。两者不能同时存在。(此处文档获胜。)
但实际上没有理由希望在标准输入上对脚本进行管道传输。
bash$ echo hello | python -c '
> import sys
> for line in sys.stdin:
>   print line'
hello

另一个解决方案可能是将数据嵌入到脚本自身中的三引号字符串中。

python <<\____
import sys
 
data = """
hello
so long
"""
 
for line in data.strip('\n').split('\n'):
   print line
____

三重引号之间的换行符全部都是字面意思,但.strip('\n')会删除开头或结尾的任何换行符。(当然,如果结尾有重要的换行符,则无法这样做。)

在此文档分隔符之前的反斜杠表示不执行此处文档内的任何变量插值或命令替换。如果您想使用这些特性,请将其删除(但是,当然,请小心逐个转义 Python 代码中的任何文字美元符号或反引号)。


4

哦,太有道理了!我一直在为此苦苦挣扎,终于明白了,谢谢你们俩。

为了提供原始程序的可行替代方案,您可以像这样做:

$ echo hello | python <(cat <<END
import sys
for line in sys.stdin:
  print line
END
)

但要小心!如果你把这段代码放在一个文件中,最后的END需要与左边的边缘相接触(没有空格)。否则,它将无法解析并显示“找不到匹配字符的意外文件结尾”等错误(实际上,我回答这个问题时花了两个小时才弄明白)。
解释一下这段代码,发生的情况是<<END ... END<(cat ...)组合在一起创建了一个文件(在/dev/fd中),以便可以像真正的文件一样输入到Python中。引用关于<(...)的解释:

这是进程替换。它将命令的输出馈送到一个可以像普通文件一样读取的FIFO中。

关于这个主题还有另一个有趣的答案在这里

1
这是一种创新的但基本上是无用的cat使用方法(http://www.iki.fi/era/unix/award.html)。Shell不需要使用`cat`在进程替换中将here文档提供给Python,尽管该结构避免了绑定标准输入(Shell基本上将来自进程替换的输出作为命名临时文件可用)。 - tripleee
@tripleee 为什么无用?如果我想要 a) 在行内传递 Python 代码 b) 利用 Here Documents 的引用优势 c) 仍然需要传递任意标准输入到 Python,我应该如何编写上述代码?我尝试了 <(< <<END ...),但它不起作用。 - A_blop
点击链接,它会准确地解释问题。 - tripleee
是的,请阅读那个链接。但是在引用的部分中没有提到任何here doc?我担心,这对我来说仍然不清楚。 - A_blop
如果您真的需要脚本在此处文档中,那么很难避免这种情况,但仍然有点不太好。我用另一个想法更新了我的答案,但如果您需要输入来自另一个进程,则无法使用它。(它可以被改编,但那也会变得相当丑陋。) - tripleee
将“_other_”进程放入进程替换中可能更加优雅,例如:python <<EOF <(echo "hello"),但是你的Python代码需要重构以读取文件参数而不是标准输入。 - tripleee

4

因为python << END将标准输入重定向到读取程序,所以你不能同时从echo管道中读取行并在标准输入上读取。


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