python -c vs python -<< heredoc

30

我想在Bash脚本中运行一些Python代码,因此我想了解以下两者之间的区别:

#!/bin/bash
#your bash code

python -c "
#your py code
"

对抗

python - <<DOC
#your py code
DOC

我查阅了网页,但无法整合与该主题相关的信息。您认为哪个更好?如果您想从Python代码块返回值到Bash脚本中,那么heredoc是唯一的选择吗?


3
在你的第一个例子中,如果你想使用带有双重双引号的字符串,你需要对双引号进行转义。 - user707650
好的,Evert,这是一个方面。还有其他方面可以让我们更喜欢其中之一吗? - Kashif
这实际上是一个关于文档字符串的问题,而不是关于Python的问题。 - shadowtalker
4个回答

26
使用here文档的主要缺点是脚本的标准输入将成为here文档。因此,如果你有一个需要处理标准输入的脚本,python -c几乎是你唯一的选择。
另一方面,使用python -c '...'会占用单引号,因此你只能在Python脚本中使用双引号字符串;如果使用双引号来保护脚本免受shell的影响,会引入额外的问题(双引号字符串会经历各种替换,而单引号字符串在shell中是字面的)。
顺便提一下,注意你可能也想要对here-doc分界符使用单引号,否则Python脚本也会受到类似的替换影响。
python - <<'____HERE'
print("""Look, we can have double quotes!""")
print('And single quotes! And `back ticks`!')
print("$(and what looks to the shell like command substitutions and $variables!)")
____HERE

作为一种替代方案,如果你更喜欢的话,可以使用转义分隔符来达到相同的效果(python - <<\____HERE)。

我不理解你关于将值返回给shell的评论。这两个结构都会打印到标准输出并返回退出代码,如果需要,shell可以检查它们。 - tripleee
同意。您需要读取shell以获取Python打印到标准输出的输出。例如。#!/bin/bash function current_datetime { python - <<'__COB' import datetime print datetime.datetime.now() __COB } dtm=$(current_datetime) echo 当前日期和时间:$dtm - Kashif
虽然 date 命令可能更适合那个特定的用例。同样,再次引用您的字符串。 - tripleee
你可以在 shell 中使用 '\'' 来“嵌入”单引号。例如:echo 'Say '\''Hello'\'', Fred' - jrw32982
是的,这是一个闭合单引号,一个未引用但反斜杠转义的单引号字面值,以及剩余单引号字符串的开头单引号。这可能对于常规使用来说过于复杂和难以阅读,但如果您只有一两个这样的情况,那么它是可行的。同义词地,您可以双引号孤立的单引号字面值- '"'"';我喜欢称之为“跷跷板引用”。 - tripleee
不错的回答!但是在使用 here-doc 时也可以使用输入,参见 https://dev59.com/fF0a5IYBdhLWcg3wHlek#72489691。 - heiner

15

如果您正在使用bash,可以通过应用更多的样板代码来避免heredoc问题:

python <(cat <<EoF

name = input()
print(f'hello, {name}!')

EoF
)

使用这种技术,您可以在不放弃标准输入的情况下运行嵌入的Python脚本。其开销与使用 cmda | cmdb 基本相同。 这种技术称为进程替换

如果希望能够验证脚本,建议将其转储到临时文件中:

#!/bin/bash

temp_file=$(mktemp my_generated_python_script.XXXXXX.py)

cat > $temp_file <<EoF
# embedded python script
EoF

python3 $temp_file && rm $temp_file

如果脚本无法运行,这将保留脚本。


+1 但是在完成后应该删除临时文件。另请参阅 https://dev59.com/93RB5IYBdhLWcg3wN08P - tripleee
在上述情况下,如果脚本正常工作,则文件将被删除。对于大多数情况而言,使用“trap”需要一些控制样板代码,我认为这是不必要的复杂性。 - PEdroArthur
是的,对于一个临时的 hack 来说这样做没问题。只是想指出如果有人想在生产脚本中这样做的话,这并不完全明显。 - tripleee
嗯,我在生产环境中使用这种方法。我额外使用的是find命令来设置文件年龄的限制。当我应用这种技术时,脚本是一个导入和函数调用。我认为真正的问题是,这种技术应该是一种缓解措施,用于当您需要在shell脚本中使用简单业务逻辑但没有时间或金钱来改进Python模块时。任何与此不同的操作,例如处理用户输入或公开实际功能,从安全角度来看都非常危险。副作用是不可取的,在我看来;您应该改进模块。 - PEdroArthur

13

如果您想使用python -c '...'而不必使用双引号进行转义,您可以使用here-documents首先将代码加载到bash变量中:

read -r -d '' CMD << '--END'
print ("'quoted'")
--END
python -c "$CMD"

Python代码会原样加载到CMD变量中,不需要转义双引号。


1

如何使用here-docs输入

tripleee的答案中有所有细节,但是有Unix技巧可以解决这个限制:

因此,如果您有一个想要处理其标准输入的脚本,则python -c基本上是您唯一的选择。

这个技巧适用于所有希望从重定向的stdin(例如,./script.py < myinputs)读取并同时接受用户输入的程序:

python - <<'____HERE'
import os

os.dup2(1, 0)
print(input("--> "))
____HERE

运行这个是有效的:

$ bash heredocpy.sh
--> Hello World!
Hello World!

如果您想获取原始的 stdin,请先运行 os.dup(0)这里 是一个真实世界的例子。
这个工作是因为只要stdout或stderr中有一个是tty,就可以从它们读取数据并写入数据。(否则,你可以直接打开/dev/tty。这就是less所做的。)
如果你想从文件中处理输入,也是可以的 - 你只需要使用一个新的文件描述符:
使用文件的示例:
cat <<'____HERE' > file.txt
With software there are only two possibilites:
either the users control the programme
or the programme controls the users.
____HERE

python - <<'____HERE' 4< file.txt
import os

for line in os.fdopen(4):
  print(line.rstrip().upper())
____HERE

带命令的示例

不幸的是,管道在这里不起作用 -- 但进程替换可以:

python - <<'____HERE' 4< <(fortune)
import os

for line in os.fdopen(4):
  print(line.rstrip().upper())
____HERE

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