178 python shell command-line heredoc
我正在使用Python -c
来执行单行循环,即:
$ python -c "for r in range(10): print 'rob'"
Run Code Online (Sandbox Code Playgroud)
这很好用.但是,如果我在for循环之前导入模块,则会出现语法错误:
$ python -c "import sys; for r in range(10): print 'rob'"
File "<string>", line 1
import sys; for r in range(10): print 'rob'
^
SyntaxError: invalid syntax
Run Code Online (Sandbox Code Playgroud)
知道如何解决这个问题吗?
将此作为单行程序对我来说很重要,这样我就可以将它包含在Makefile中.
jsp*_*cal 162
你能做到的
echo -e "import sys\nfor r in range(10): print 'rob'" | python
Run Code Online (Sandbox Code Playgroud)
或没有管道:
python -c "exec(\"import sys\nfor r in range(10): print 'rob'\")"
Run Code Online (Sandbox Code Playgroud)
要么
(echo "import sys" ; echo "for r in range(10): print 'rob'") | python
Run Code Online (Sandbox Code Playgroud)
小智 84
这种风格也可以在makefile中使用(实际上它经常被使用).
python - <<EOF
import sys
for r in range(3): print 'rob'
EOF
Run Code Online (Sandbox Code Playgroud)
要么
python - <<-EOF
import sys
for r in range(3): print 'rob'
EOF
Run Code Online (Sandbox Code Playgroud)
在后一种情况下,前导制表符也被删除(并且可以实现一些结构化的前景)
而不是EOF可以代表在行的开头不出现在此处文档中的任何标记词(另请参阅bash联机帮助页或此处的文档).
Cra*_*ast 49
实际上问题不在于import语句,而是在for循环之前的任何事情.或者更具体地说,在内联块之前出现的任何内容.
例如,这些都有效:
python -c "import sys; print 'rob'"
python -c "import sys; sys.stdout.write('rob\n')"
Run Code Online (Sandbox Code Playgroud)
如果导入是一个声明是一个问题,这将有效,但它不会:
python -c "__import__('sys'); for r in range(10): print 'rob'"
Run Code Online (Sandbox Code Playgroud)
对于您的基本示例,您可以将其重写为:
python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"
Run Code Online (Sandbox Code Playgroud)
但是,lambdas只能执行表达式,而不能执行语句或多个语句,因此您可能仍然无法执行您想要执行的操作.但是,在生成器表达式,列表推导,lambdas,sys.stdout.write,内置的"map"和一些创造性的字符串插值之间,你可以做一些强大的单行.
问题是,你想走多远,在什么时候写一个.py
你的makefile执行的小文件不是更好?
mkl*_*nt0 23
- 为了使这个答案也适用于Python 3.x,print
被称为函数:在3.x中,只有 print('foo')
作用,而2.x也接受print 'foo'
.
- 对于包含Windows的跨平台视角,请参阅kxr的有用答案.
在bash
,ksh
或zsh
:
使用ANSI C引用的字符串($'...'
),它允许\n
在字符串传递到之前用于表示扩展到实际换行符的换行符python
:
python -c $'import sys\nfor r in range(10): print("rob")'
Run Code Online (Sandbox Code Playgroud)
注意\n
在import
和for
语句之间实现换行符.
要将shell变量值传递给这样的命令,最安全的方法是使用参数并通过sys.argv
Python脚本内部访问它们:
name='rob' # value to pass to the Python script
python -c $'import sys\nfor r in range(10): print(sys.argv[1])' "$name"
Run Code Online (Sandbox Code Playgroud)
请参阅下文,了解使用带有嵌入式 shell变量引用的(转义序列 - 预处理)双引号命令字符串的优缺点.
要安全地使用$'...'
字符串:
\
实例.
\<char>
序列-例如\n
在此情况下,也是通常的嫌疑人如\t
,\r
,\b
-由被扩展$'...'
(参见man printf
用于所支持逃逸)'
实例转义为\'
.如果您必须保持POSIX兼容:
使用printf
带有命令替换:
python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"
Run Code Online (Sandbox Code Playgroud)
要安全地使用这种类型的字符串:
\
实例.
\<char>
序列-例如\n
在此情况下,也是通常的嫌疑人如\t
,\r
,\b
-通过扩展printf
(见man printf
所支持的转义序列).将单引号字符串传递给printf %b
并将嵌入的单引号转义为 '\''
(原文如此).
使用单引号可以保护字符串的内容免受shell的解释.
也就是说,对于简短的 Python脚本(如本例所示),您可以使用双引号字符串将shell变量值合并到脚本中 - 只要您知道相关的陷阱(参见下一点); 例如,shell扩展$HOME
到当前用户的主目录.在以下命令中:
python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
但是,通常首选的方法是通过参数从shell传递值,并通过sys.argv
Python 访问它们; 相当于上面的命令是:
python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
虽然使用双引号字符串更方便 - 它允许您使用未转义和嵌入双引号的嵌入式单引号\"
- 它还使字符串受shell的解释,这可能是也可能不是意图; $
并`
在源代码字符不是用于外壳可能会导致语法错误或意外改变的字符串.
\
在双引号字符串中的处理可能会妨碍它; 例如,要让Python生成文字输出ro\b
,你必须传递ro\\b
给它; 使用'...'
shell字符串和doubled \
实例,我们得到:python -c "$(printf %b 'import sys\nprint("ro\\\\bs")')" # ok: 'ro\bs'
"..."
:python -c "$(printf %b "import sys\nprint('ro\\\\bs')")" # !! INCORRECT: 'rs'
"\b"
和"\\b"
文字\b
,需要一些令人眼花缭乱的额外\
实例来实现所需的效果:python -c "$(printf %b "import sys\nprint('ro\\\\\\\\bs')")"
要通过代码stdin
而不是-c
:
注意:我在这里专注于单线解决方案; xorho的答案显示了如何在这里使用多行文档 - 请务必引用分隔符; 例如,<<'EOF'
除非你明确要求shell预先扩展字符串(这与上面提到的警告一起).
在bash
,ksh
或zsh
:
将ANSI C引用的字符串($'...'
)与here-string(<<<...
)组合在一起:
python - <<<$'import sys\nfor r in range(10): print("rob")'
Run Code Online (Sandbox Code Playgroud)
-
python
明确告诉从stdin读取(默认情况下它).
-
在这种情况下是可选的,但是如果你还想将参数传递给脚本,你需要它来消除脚本文件名中的参数:
python - 'rob' <<<$'import sys\nfor r in range(10): print(sys.argv[1])'
Run Code Online (Sandbox Code Playgroud)
如果您必须保持POSIX兼容:
使用printf
如上所述,但使用管道以便通过stdin传递其输出:
printf %b 'import sys\nfor r in range(10): print("rob")' | python
Run Code Online (Sandbox Code Playgroud)
有一个论点:
printf %b 'import sys\nfor r in range(10): print(sys.argv[1])' | python - 'rob'
Run Code Online (Sandbox Code Playgroud)
Aar*_*all 16
知道如何解决这个问题吗?
你的问题是由以下事实造成的:Python语句;
只能被称为"小语句",它们都是单行语句.从Python文档中的语法文件:
Run Code Online (Sandbox Code Playgroud)stmt: simple_stmt | compound_stmt simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE small_stmt: (expr_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt)
复合语句不能通过分号与其他语句包含在同一行 - 因此使用该-c
标志执行此操作会变得非常不方便.
在bash shell环境中演示Python时,我发现包含复合语句非常有用.这样做的唯一简单方法是使用heredocs.
使用heredoc(使用<<
)和Python的命令行界面选项,-
:
$ python - <<-"EOF"
import sys # 1 tab indent
for r in range(10): # 1 tab indent
print('rob') # 1 tab indent and 4 spaces
EOF
Run Code Online (Sandbox Code Playgroud)
添加-
after <<
(the <<-
)允许你使用制表符缩进(Stackoverflow将制表符转换为空格,所以我缩进了8个空格来强调这一点).前导标签将被剥离.
您可以在没有选项卡的情况下执行此操作<<
:
$ python - << "EOF"
import sys
for r in range(10):
print('rob')
EOF
Run Code Online (Sandbox Code Playgroud)
放置引号EOF
可防止参数和算术扩展.这使得heredoc更加强大.
这不是很可读:
Run Code Online (Sandbox Code Playgroud)$ python -c " > import sys > for p in '$PATH'.split(':'): > print(p) > " /usr/sbin /usr/bin /sbin /bin ...
不太可读,并且在出现错误的情况下另外难以调试:
Run Code Online (Sandbox Code Playgroud)$ python -c ' > import sys > for p in "$PATH".split(":"): > print(p) > ' $PATH
也许更具可读性,但仍然相当难看:
Run Code Online (Sandbox Code Playgroud)$ python -c ' import sys for p in "'"$PATH"'".split(":"): print(p) ' /usr/sbin /usr/bin /sbin /bin ...
如果你有"
你的python,你将度过一段美好的时光:
Run Code Online (Sandbox Code Playgroud)echo -e "import sys\nfor r in range(10): print 'rob'" | python
不要滥用map
或列出理解来获得for循环:
Run Code Online (Sandbox Code Playgroud)python -c "exec(\"import sys\\nfor r in range(10): print 'rob'\")"
这些都是悲伤和坏事.不要这样做.
Sil*_*ost 12
只需使用return并在下一行输入:
user@host:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...
Run Code Online (Sandbox Code Playgroud)
kxr*_*kxr 10
此变体最适合在 Windows 和类 Unix 系统(Python\xc2\xa02 和 Python\xc2\xa03)的命令行上放置多行脚本,无需使用管道:
\npython -c "exec(\\"import sys \\nfor r in range(10): print('rob') \\")"\n
Run Code Online (Sandbox Code Playgroud)\n(到目前为止,这里看到的其他示例都没有这样做。)
\nWindows 上的整洁是:
\npython -c exec"""import sys \\nfor r in range(10): print 'rob' """\npython -c exec("""import sys \\nfor r in range(10): print('rob') """)\n
Run Code Online (Sandbox Code Playgroud)\n在类 Unix 系统上的 Bash 上,Neat 是:
\npython -c $'import sys \\nfor r in range(10): print("rob")'\n
Run Code Online (Sandbox Code Playgroud)\n此函数将任何多行脚本转换为可移植的单行命令:
\ndef py2cmdline(script):\n exs = 'exec(%r)' % re.sub('\\r\\n|\\r', '\\n', script.rstrip())\n print('python -c "%s"' % exs.replace('"', r'\\"'))\n
Run Code Online (Sandbox Code Playgroud)\n用法:
\n>>> py2cmdline(getcliptext())\npython -c "exec('print \\'AA\\tA\\'\\ntry:\\n for i in 1, 2, 3:\\n print i / 0\\nexcept:\\n print \\"\\"\\"longer\\nmessage\\"\\"\\"')"\n
Run Code Online (Sandbox Code Playgroud)\n输入是:
\nprint 'AA A'\ntry:\n for i in 1, 2, 3:\n print i / 0\nexcept:\n print """longer\nmessage"""\n
Run Code Online (Sandbox Code Playgroud)\n
问题不在于import
声明.问题是控制流语句不能在python命令中内联工作.将该import
语句替换为任何其他语句,您将看到相同的问题.
想想看:python不可能内联一切.它使用缩进来分组控制流.
如果您的系统符合Posix.2,则应提供该printf
实用程序:
$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob
Run Code Online (Sandbox Code Playgroud)
小智 7
$ python2.6 -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"
工作良好.使用"[]"来内联你的for循环.