我需要有一个直接可执行的python脚本,所以我用#!/usr/bin/env python启动了该文件。但是,我也需要无缓冲的输出,所以我尝试了#!/usr/bin/env python -u,但在python -u: no such file or directory中失败了。
我发现#/usr/bin/python -u可以工作,但我需要它让PATH中的python支持虚拟env环境。
我有什么选择?
- 你可以看看这个问题,了解一些关于如何进行无缓冲输出的信息。
在某些环境中,env不拆分参数。所以您的env在您的路径中寻找"python-u"。我们可以用sh工作。用以下代码行替换您的shebang,一切都会好起来的。
1 2 3
| #!/bin/sh
''''exec python -u --"$0" ${1+"$@"} # '''
# vi: syntax=python |
另外,我们不必担心去上海的路,对吧?
- 狡猾!我想知道它有多轻便。
- 对于那些想知道这是如何工作的人来说:为什么这段代码会工作?
- 至少20年来,${1+"$@"}黑客攻击可能是不必要的。
- @用户4815162342感谢您提供信息。下一次我要试试"$@"。:)
- 也许黑客是不必要的,但它不会造成任何伤害,是吗?知道这件事很有趣:-)我今天才知道。不管怎样,我认为"exec""python""-u""--""$0""$@"可能更容易理解——它有什么缺陷吗?(我认为它与1+黑客不兼容?)
- @aaronmcdaid由于没有必要进行黑客攻击,所以您的版本可以很好地工作,而且可读性更高!伟大的!
- 我的方法有缺点。如果您想传递一些复杂的bash,比如一个嵌套'或"的字符串,那么您的方法更可靠。这是一个有趣的问题!也许我的更容易理解,但你的更健壮。也许您的回答应该澄清,它必须以''''exec开头,字符串必须以# '''结尾(在#之前有一个空格)。只要我们遵循这些规则,并且没有任何额外的三重引号''',您的方法是完美和灵活的。
- 不幸的是,这种方式与模块的docstring冲突。有没有办法把黑客和模块的docstring分开?
- @用户4815162342在${1+"$@"}上有更多的上下文。因此,在大多数情况下,"$@"应该可以自己工作。
- 总之,这个技巧是首先使用python的module doc字符串来运行一个迷你shell脚本。
最好使用环境变量来启用它。参见python文档:http://docs.python.org/2/using/cmdline.html
对于你的情况:
1 2
| export PYTHONUNBUFFERED=1
script.py |
在Linux上使用shebang时,解释器名称后面的整个行的其余部分将被解释为单个参数。python -u被传递给env,就好像你输入了:/usr/bin/env 'python -u'一样。/usr/bin/env搜索一个名为python -u的二进制文件,但没有。
将参数传递到shebang行是不标准的,而且正如您所试验的那样,在Linux中不能与env结合使用。使用bash的解决方案是使用内置命令"set"来设置所需的选项。我认为可以用python命令来设置stdin的未缓冲输出。
MY2C
这里有一个/usr/bin/env的脚本替代方案,它允许基于/bin/bash在hash bang行上传递参数,并限制在可执行路径中不允许使用空格。我称之为"envns"(env no spaces):
1 2 3 4 5 6 7 8 9 10
| #!/bin/bash
ARGS=( $1 ) # separate $1 into multiple space-delimited arguments.
shift # consume $1
PROG=`which ${ARGS[0]}`
unset ARGS[0] # discard executable name
ARGS+=("$@" ) # remainder of arguments preserved"as-is".
exec $PROG"${ARGS[@]}" |
假设这个脚本位于/usr/local/bin/envns,下面是您的shebang行:
1
| #!/usr/local/bin/envns python -u |
在Ubuntu 13.10和Cygwin X64上测试。
- 这应该捆绑在一起:)
- 注意:大多数unix-ish!实现不允许出于安全原因使用脚本。我很惊讶这在Ubunut 13.10上起了作用。
- 也在Ubuntu 16.04上工作,从2019-01-20开始,不确定其他人。
这可能有点过时,但env(1)手册告诉人们可以使用"-s"来解决这个问题。
1
| #!/usr/bin/env -S python -u |
在freebsd上似乎很管用。
- 在Ubuntu不工作:invalid option。
- 也不适用于Debian SID
- 也不适用于Rhel 7。
- 哇,如果这能广泛应用,但也不能在Cygwin上使用,那就太好了:(
- 似乎-S选项是特定于env(1)的BSD变体,但很好地知道
- 似乎可以在MacOS上使用。
- Linux现在也有env -S—从coreutils 8.301开始(这可能需要一段时间才能出现在您附近的发行版上)。与Freebsd的env(1)相同的语义,因为它具有良好功能的可移植性。
- 在Solaris(11.3)上也没有运气…:(
这是一个拼凑,需要bash,但它可以工作:
1 2 3 4 5 6 7
| #!/bin/bash
python -u <(cat <<"EOF"
# Your script here
print"Hello world"
EOF
) |
- 谢谢,这对我的另一个问题很有效(使用单CSharp shell和参数)
基于Larry Cai的答案,env允许您直接在命令行中设置变量。也就是说,在python之前,可以用等效的PYTHONUNBUFFERED设置来代替-u:
1
| #!/usr/bin/env PYTHONUNBUFFERED="YESSSSS" python |
在RHEL 6.5上工作。我很肯定,env的特性是关于通用的。
- 仅供参考,这在Debian不起作用。我不知道它为什么不起作用(看看ps的输出应该没有什么区别),但它永远不会回来。在Debian中执行此操作时,还不清楚python本身是否真正在运行。我在一些地方尝试过这种方法——与等效的命令行相比,它肯定不能按预期工作。
- @马蒂马盖弗。很可能与您使用的env或甚至python版本有关。
- 可能是env的版本,但到目前为止它还没有与任何现代的debian变体一起工作。似乎python实际上并没有在debian上运行在这个场景中,这使得它在某些平台和/或配置之外的使用受到限制。
我最近为env的gnu coreutils版本编写了一个补丁来解决这个问题:
http://lists.gnu.org/archive/html/coreutils/2017-05/msg00018.html
如果你有这个,你可以做:
1
| #!/usr/bin/env :lang:--foo:bar |
env将:lang:foo:--bar分成lang字段、foo字段和--bar字段。它将在PATH中搜索解释器lang,然后用参数--foo和bar调用它,再加上脚本的路径和该脚本的参数。
还有一个特性可以在选项中间传递脚本的名称。假设您想运行lang -f other-arg,然后是其余的参数。有了这个补丁env,它是这样做的:
1
| #!/usr/bin/env :lang:-f:{}:other-arg |
等价于{}的最左边字段替换为后面的第一个参数,在hash bang调用下,该参数是脚本名。然后删除该参数。
在这里,other-arg可以是lang处理的东西,也可以是脚本处理的东西。
为了更好地理解,请参阅补丁中的众多echo测试案例。
我选择了:字符,因为它是一个现有的分隔符,用于在posix系统上使用PATH。由于env进行PATH搜索,它几乎不可能用于名称中包含冒号的程序。{}标记来自find实用程序,该实用程序使用它来表示将路径插入-exec命令行。
- 对于任何读到这篇文章并满怀希望的人来说,这片土地被遗弃了。