![](/img/trans.png)
[英]Executing Python multi-line statements from command-line, while inside python
[英]Executing multi-line statements in the one-line command-line?
我正在使用帶有-c
Python 來執行單行循環,即:
$ python -c "for r in range(10): print 'rob'"
這工作正常。 但是,如果在 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
知道如何解決這個問題嗎?
將它作為單行代碼對我來說很重要,這樣我就可以將它包含在 Makefile 中。
你可以
echo -e "import sys\nfor r in range(10): print 'rob'" | python
或帶出管道:
python -c "exec(\"import sys\nfor r in range(10): print 'rob'\")"
或者
(echo "import sys" ; echo "for r in range(10): print 'rob'") | python
這種樣式也可以在 makefile 中使用(實際上它經常使用)。
python - <<EOF
import sys
for r in range(3): print 'rob'
EOF
或者
python - <<-EOF
import sys
for r in range(3): print 'rob'
EOF
在后一種情況下,前導制表符也被刪除(並且可以實現一些結構化的外觀)
而不是 EOF 可以代表任何未出現在 here 文檔中的任何標記詞的行首(另請參閱 bash 聯機幫助頁中的 here 文檔或此處)。
問題實際上不在於 import 語句,而在於 for 循環之前的任何內容。 或者更具體地說,任何出現在內聯塊之前的東西。
例如,這些都有效:
python -c "import sys; print 'rob'"
python -c "import sys; sys.stdout.write('rob\n')"
如果 import 作為一個語句是一個問題,這會起作用,但它不會:
python -c "__import__('sys'); for r in range(10): print 'rob'"
對於您非常基本的示例,您可以將其重寫為:
python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"
但是,lambdas 只能執行表達式,不能執行語句或多個語句,因此您可能仍然無法做您想做的事情。 但是,在生成器表達式、列表推導式、lambdas、sys.stdout.write、“map”內置和一些創造性的字符串插值之間,你可以做一些強大的單行。
問題是,你想走多遠,在什么時候編寫一個由你的 makefile 執行的小.py
文件不是更好?
- 為了使這個答案也適用於 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")'
注意import
和for
語句之間的\\n
以實現換行。
要將shell 變量值傳遞給這樣的命令,最安全的是使用參數並通過 Python 腳本中的sys.argv
訪問它們:
name='rob' # value to pass to the Python script
python -c $'import sys\nfor r in range(10): print(sys.argv[1])' "$name"
有關使用(轉義序列預處理)雙引號命令字符串與嵌入式shell 變量引用的利弊的討論,請參見下文。
要安全地使用$'...'
字符串:
\\
實例。
\\<char>
序列 - 例如在這種情況下的\\n
,但通常的嫌疑人,例如\\t
, \\r
, \\b
- 由$'...'
擴展(有關支持的轉義,請參閱man printf
)'
實例轉義為\\'
。如果您必須保持POSIX 兼容:
將printf
與命令替換一起使用:
python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"
要安全地使用這種類型的字符串:
\\
實例。
\\<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 傳遞值,並通過 Python 中的sys.argv
訪問它們; 上面命令的等價物是:
python -c "$(printf %b 'import sys\\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
雖然使用雙引號字符串更方便- 它允許您使用未轉義的嵌入式單引號和嵌入式雙引號作為\\"
- 它還使字符串受到shell 的解釋,這可能是也可能不是意圖; $
源代碼中不用於 shell 的和`
字符可能會導致語法錯誤或意外更改字符串。
\\
處理可能會妨礙; 例如,要讓Python生成文字輸出ro\\b
,您必須將ro\\\\b
傳遞給它; 使用'...'
shell 字符串和加倍的\\
實例,我們得到:python -c "$(printf %b 'import sys\\nprint("ro\\\\\\\\bs")')" # ok: 'ro\\bs'
"..."
shell字符串: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 的回答顯示了如何使用多行here-document - 但是一定要引用分隔符; 例如, <<'EOF'
,除非您明確希望 shell 在前面擴展字符串(帶有上述注意事項)。
在bash
、 ksh
或zsh
:
將ANSI C 引用的字符串( $'...'
) 與此處的字符串( <<<...
) 組合在一起:
python - <<<$'import sys\nfor r in range(10): print("rob")'
-
明確告訴python
從 stdin 讀取(默認情況下它會這樣做)。 -
在這種情況下是可選的,但如果您還想將參數傳遞給腳本,則確實需要它來消除腳本文件名中的參數的歧義:
python - 'rob' <<<$'import sys\nfor r in range(10): print(sys.argv[1])'
如果您必須保持POSIX 兼容:
如上所述使用printf
,但使用管道以便通過 stdin 傳遞其輸出:
printf %b 'import sys\nfor r in range(10): print("rob")' | python
有一個論點:
printf %b 'import sys\nfor r in range(10): print(sys.argv[1])' | python - 'rob'
知道如何解決這個問題嗎?
您的問題是由 Python 語句造成的,由;
分隔;
, 只允許是“小報表”,都是單行的。 從Python 文檔中的語法文件:
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(posix shell)。
使用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
在<<
( <<-
)之后添加-
允許您使用制表符進行縮進(Stackoverflow 將制表符轉換為空格,因此我縮進了 8 個空格以強調這一點)。 前導標簽將被剝離。
您可以在沒有標簽的情況下使用<<
:
$ python - << "EOF"
import sys
for r in range(10):
print('rob')
EOF
在EOF
周圍加上引號可以防止參數和算術擴展。 這使得heredoc更加健壯。
如果你使用雙引號,你會得到 shell-expansion:
$ python -c "
> import sys
> for p in '$PATH'.split(':'):
> print(p)
> "
/usr/sbin
/usr/bin
/sbin
/bin
...
為避免 shell 擴展,請使用單引號:
$ python -c '
> import sys
> for p in "$PATH".split(":"):
> print(p)
> '
$PATH
請注意,我們需要在 Python 中交換文字上的引號字符 - 我們基本上不能使用 BASH 解釋的引號字符。 我們可以交替使用它們,就像我們在 Python 中一樣 - 但這看起來已經很混亂了,這就是為什么我不推薦這樣做:
$ python -c '
import sys
for p in "'"$PATH"'".split(":"):
print(p)
'
/usr/sbin
/usr/bin
/sbin
/bin
...
這不是很可讀:
echo -e "import sys\\nfor r in range(10): print 'rob'" | python
不太可讀,而且在出現錯誤的情況下也很難調試:
python -c "exec(\\"import sys\\\\nfor r in range(10): print 'rob'\\")"
也許更具可讀性,但仍然很丑陋:
(echo "import sys" ; echo "for r in range(10): print 'rob'") | python
如果你的 python 中有"
你會過得很糟糕:
$ python -c "import sys > for r in range(10): print 'rob'"
不要濫用map
或 list comprehensions 來獲取 for 循環:
python -c "import sys; map(lambda x: sys.stdout.write('rob%d\\n' % x), range(10))"
這些都是可悲和壞的。 不要做他們。
只需使用 return 並在下一行輸入:
user@host:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...
$ python2.6 -c "import sys; [sys.stdout.write('rob\\n') for r in range(10)]"
工作正常。 使用“[]”來內聯你的 for 循環。
問題不在於import
語句。 問題是控制流語句不能在 python 命令中內聯工作。 將該import
語句替換為任何其他語句,您將看到相同的問題。
想一想:python 不可能內聯所有內容。 它使用縮進對控制流進行分組。
如果您的系統符合 Posix.2,它應該提供printf
實用程序:
$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob
此變體最適合將多行腳本放在 Windows 和 *nix、py2/3 上的命令行上,無需管道:
python -c "exec(\"import sys \nfor r in range(10): print('rob') \")"
(到目前為止,這里看到的其他示例都沒有這樣做)
Windows 上的整潔是:
python -c exec"""import sys \nfor r in range(10): print 'rob' """
python -c exec("""import sys \nfor r in range(10): print('rob') """)
bash/*nix 上的整潔是:
python -c $'import sys \nfor r in range(10): print("rob")'
此函數將任何多行腳本轉換為可移植的命令行:
def py2cmdline(script):
exs = 'exec(%r)' % re.sub('\r\n|\r', '\n', script.rstrip())
print('python -c "%s"' % exs.replace('"', r'\"'))
用法:
>>> py2cmdline(getcliptext())
python -c "exec('print \'AA\tA\'\ntry:\n for i in 1, 2, 3:\n print i / 0\nexcept:\n print \"\"\"longer\nmessage\"\"\"')"
輸入是:
print 'AA A'
try:
for i in 1, 2, 3:
print i / 0
except:
print """longer
message"""
(在 10 年 11 月 23 日 19:48 回答)我並不是一個真正的 Pythoner - 但我曾經發現過這個語法,忘了出處,所以我想我會記錄它:
如果您使用sys.stdout.write
而不是print
(區別在於, sys.stdout.write
將參數作為函數,在括號中 - 而print
沒有),那么對於單行,您可以擺脫反轉命令和for
的順序,刪除分號,並將命令括在方括號中,即:
python -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"
不知道如何在 Python 中調用此語法:)
希望這可以幫助,
干杯!
(EDIT Tue Apr 9 20:57:30 2013)好吧,我想我終於找到了單行中的這些方括號的含義; 它們是“列表推導式”(顯然); 首先在 Python 2.7 中注意這一點:
$ STR=abc
$ echo $STR | python -c "import sys,re; a=(sys.stdout.write(line) for line in sys.stdin); print a"
<generator object <genexpr> at 0xb771461c>
所以圓括號/圓括號中的命令被視為“生成器對象”; 如果我們通過調用next()
來“迭代”它——那么括號內的命令將被執行(注意輸出中的“abc”):
$ echo $STR | python -c "import sys,re; a=(sys.stdout.write(line) for line in sys.stdin); a.next() ; print a"
abc
<generator object <genexpr> at 0xb777b734>
如果我們現在使用方括號 - 請注意,我們不需要調用next()
來執行命令,它會在賦值后立即執行; 然而,后來的檢查顯示a
是None
:
$ echo $STR | python -c "import sys,re; a=[sys.stdout.write(line) for line in sys.stdin]; print a"
abc
[None]
對於方括號案例,這並沒有留下太多信息需要查找 - 但我偶然發現了這個頁面,我認為它解釋了:
Python 技巧與竅門——第一版——Python 教程 | Dream.In.Code :
如果您還記得,單行生成器的標准格式是括號內的一種單行“for”循環。 這將產生一個“一次性”可迭代對象,它是一個只能在一個方向上迭代的對象,並且一旦到達終點就不能重復使用。
“列表推導式”看起來與常規的單行生成器幾乎相同,除了常規括號 - ( ) - 被方括號 - [ ] 替換。 alist comprehension 的主要優點是產生一個“列表”,而不是一個“一次性”的可迭代對象,這樣你就可以來回遍歷它,添加元素,排序等。
事實上,它是一個列表——它只是它的第一個元素在執行后立即變為 none:
$ echo $STR | python -c "import sys,re; print [sys.stdout.write(line) for line in sys.stdin].__class__"
abc
<type 'list'>
$ echo $STR | python -c "import sys,re; print [sys.stdout.write(line) for line in sys.stdin][0]"
abc
None
列表推導式在5. 數據結構:5.1.4中另有記錄。 List Comprehensions — Python v2.7.4 文檔為“ List Comprehensions提供了一種創建列表的簡潔方法”; 據推測,這就是列表的有限“可執行性”在單行中發揮作用的地方。
好吧,希望我在這里不是太離譜......
EDIT2:這是一個帶有兩個非嵌套 for 循環的單行命令行; 都包含在“列表理解”方括號中:
$ echo $STR | python -c "import sys,re; a=[sys.stdout.write(line) for line in sys.stdin]; b=[sys.stdout.write(str(x)) for x in range(2)] ; print a ; print b"
abc
01[None]
[None, None]
請注意,第二個“列表” b
現在有兩個元素,因為它的 for 循環顯式運行了兩次; 然而, sys.stdout.write()
在這兩種情況下的結果(顯然)都是None
。
single/double quotes
和backslash
無處不在:
$ python -c 'exec("import sys\nfor i in range(10): print \"bob\"")'
好多了:
$ python -c '
> import sys
> for i in range(10):
> print "bob"
> '
我想要一個具有以下屬性的解決方案:
其他答案中沒有提供這兩個要求,因此這里是在命令行上執行所有操作時讀取 stdin 的方法:
grep special_string -r | sort | python3 <(cat <<EOF
import sys
for line in sys.stdin:
tokens = line.split()
if len(tokens) == 4:
print("%-45s %7.3f %s %s" % (tokens[0], float(tokens[1]), tokens[2], tokens[3]))
EOF
)
這個腳本提供了一個類似於 Perl 的命令行界面:
當我需要這樣做時,我使用
python -c "$(echo -e "import sys\nsys.stdout.write('Hello World!\\\n')")"
請注意 sys.stdout.write 語句中換行符的三重反斜杠。
使用帶有三引號的 python -c
python -c """
import os
os.system('pwd')
os.system('ls -l')
print('Hello World!')
for _ in range(5):
print(_)
"""
如果您不想像通過“python cmdfile.py”一樣觸摸 stdin 和模擬,您可以從 bash shell 執行以下操作:
$ python <(printf "word=raw_input('Enter word: ')\nimport sys\nfor i in range(5):\n print(word)")
如您所見,它允許您使用 stdin 來讀取輸入數據。 shell 在內部為輸入命令內容創建臨時文件。
還有一個選項,sys.stdout.write 返回 None,它使列表保持為空
cat somefile.log|python -c "import sys;[line for line in sys.stdin if sys.stdout.write(line*2)]"
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.