1行のコマンドラインで複数行のステートメントを実行しますか?


197

私はPythonを使っ-cてワンライナーループを実行しています、すなわち:

$ 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

これを修正する方法はありますか?

Makelineに含めることができるように、これを1行にすることが重要です。


これはあなたの質問に答えますか?辞書を使用して実行する関数を選択
pfabri

回答:


182

あなたはできる

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

または@ SilentGhostの回答 / @Crastの回答


最後のオプションは、Windowsのpython3でうまく機能します
Ian Ellis

1
@RudolfOlahの希望は、あなたは、今ではそれを知っているが、あくまでも参考のために、次のようなのpython3 +バージョンのprint文をラップする必要がありますpython -c "exec(\"import sys\nfor r in range(10): print('rob')\")"
SYSTRIGGER

@systriggerおかげで、私は急いでいると思っていて、それを理解していなかったのを逃しました

89

このスタイルはメイクファイルでも使用できます(実際、かなり頻繁に使用されます)。

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の hereドキュメントも参照してください)。


9
いいね。引数も渡すには、引数を後に置きます<<EOF。ただし、ヒアドキュメントの内容を事前のシェル展開から保護するために、区切り文字を引用する方がよいことに注意してください。<<'EOF'
mklement0 2015

56

問題は実際にはimportステートメントではなく、forループの前にあるものにあります。より具体的には、インラインブロックの前に現れるもの。

たとえば、これらはすべて機能します。

python -c "import sys; print 'rob'"
python -c "import sys; sys.stdout.write('rob\n')"

インポートがステートメントであることが問題である場合、これは機能しますが、機能しません。

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))"

ただし、ラムダは式のみを実行でき、ステートメントや複数のステートメントは実行できないため、実行したいことを実行できない場合があります。ただし、ジェネレータ式、リスト内包表記、ラムダ、sys.stdout.write、組み込みの「マップ」、およびいくつかの創造的な文字列補間の間では、強力なワンライナーを実行できます。

問題は、どこまで行きたいか、そして.pyいつmakefileが実行する小さなファイルを書く方が良いかということです。


31


- Pythonの持つこの回答動作させるために3.xのをも、printと呼ばれている機能:3.xの中で、唯一 print('foo') 2.xはまた受け入れる一方で、動作しますprint 'foo'
-含まクロスプラットフォームの展望についてのWindowsを参照してくださいKXRの役に立つ答えを

bashkshまたはzsh

使用ANSI C-引用符で囲まれた文字列$'...'使用可能)、\n文字列が渡される前に、実際の改行に拡張される改行を表すようにしますpython

python -c $'import sys\nfor r in range(10): print("rob")'

なお、\nimportfor改行を行うために文を。

するには、シェル変数の値を渡し、そのようなコマンドに、それは使用するのが最も安全である引数とを経由してアクセスしてsys.argvPythonスクリプトの内部:

name='rob' # value to pass to the Python script
python -c $'import sys\nfor r in range(10): print(sys.argv[1])' "$name"

シェル変数の参照が埋め込まれた(エスケープシーケンスが前処理された)二重引用符で囲まれたコマンド文字列を使用する場合の長所と短所については、以下を参照してください。

$'...'文字列を安全に操作するには:

  • 元のソースコードの\インスタンスを2つにします。
    • \<char>配列-のような\nこの場合、ほかのような通常の容疑者\t\r\b-により展開され$'...'(参照man printfサポートエスケープ用)
  • 'インスタンスをとしてエスケープします\'

POSIX準拠を維持する必要がある場合:

使用printfしてコマンド置換

python -c "$(printf %b 'import sys\nfor r in range(10): print("rob")')"

このタイプの文字列を安全に使用するには:

  • 元のソースコードの\インスタンスを2つにします。
    • \<char>シーケンス-など、\nこのケースではなく、このような通常の容疑者\t\r\b-によって展開されているprintf(参照man printfサポートエスケープシーケンスのため)。
  • パスシングルクォートに文字列をprintf %bし、として埋め込ま単一引用符をエスケープ '\''(原文のまま)。

    • 一重引用符を使用すると、文字列の内容がシェルによる解釈から保護されます

      • つまり、(この場合のように)短い Pythonスクリプトの場合、二重引用符で囲まれた文字列を使用して、シェル変数の値をスクリプトに組み込むことができます-関連する落とし穴を知っている限り(次のポイントを参照)。たとえば、シェル$HOMEは現在のユーザーのホームディレクトリに展開されます。次のコマンドで:

        • python -c "$(printf %b "import sys\nfor r in range(10): print('rob is $HOME')")"
      • ただし、一般的に推奨されるアプローチは、引数を介してシェルから値を渡しsys.argv、Pythonでそれらにアクセスすることです。上記のコマンドと同等のものは次のとおりです。

        • python -c "$(printf %b 'import sys\nfor r in range(10): print("rob is " + sys.argv[1])')" "$HOME"
    • 二重引用符で囲まれた文字列を使用する方が便利ですが、エスケープなしの埋め込み単一引用符と埋め込み二重引用符を使用できます。\"また、文字列がシェルによる解釈の対象になります。これは意図されている場合とされていない場合があります。$そして`シェルのために意図されていないソースコード内の文字は、構文エラーが発生したり、予期せずに文字列を変更することができます。

      • さらに、\二重引用符で囲まれた文字列でのシェル自体の処理が邪魔になる場合があります。たとえば、Pythonでリテラル出力を生成するにはro\b、それに渡す必要がro\\bあります。'...'シェルの文字列と倍増 \インスタンスを、我々が得る:
        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の回答は、複数行のhere-documentの使用方法を示しています。ただし、区切り文字は必ず引用してください。たとえば、<<'EOF'シェルが文字列を前に明示的に展開することを明示的に要求しない限り(上記の警告に付属しています)。


bashkshまたはzsh

ANSI Cで引用された文字列$'...')とhere-string<<<...)を組み合わせる:

python - <<<$'import sys\nfor r in range(10): print("rob")'

-pythonstdinから読み取るように明示的に指示します(デフォルトで読み取ります)。 -この場合はオプションですが、スクリプトに引数を渡す場合も、スクリプトのファイル名から引数を明確にするために必要です。

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'

2
これが当選したはずです!
1

1
また、これは動作し、おそらく最高のブラボー、それは十分な説明が含まれているので答え!

22

これを修正する方法はありますか?

あなたの問題は、で区切られたPythonステートメントが、;すべてが1行である「小さなステートメント」のみに許可されているという事実によって引き起こされます。Python docsの文法ファイルから:

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シェル環境でPythonをデモする場合、複合ステートメントを含めると非常に便利です。これを確実に行う唯一の簡単な方法は、ヒアドキュメント(posixシェルのもの)を使用することです。

ヒアドキュメント

使用ヒアドキュメント(で作成<<)と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パラメータ算術展開ができなくなります。これにより、ヒアドキュメントがより堅牢になります。

複数行の文字列をバッシュする

二重引用符を使用すると、シェル展開が得られます。

$ python -c "
> import sys
> for p in '$PATH'.split(':'):
>     print(p)
> "
/usr/sbin
/usr/bin
/sbin
/bin
...

シェル展開を回避するには、単一引用符を使用します。

$ 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'"

mapforループを取得するために理解を乱用したりリストしたりしないでください。

python -c "import sys; map(lambda x: sys.stdout.write('rob%d\n' % x), range(10))"

これらはすべて悲しくて悪いです。それらをしないでください。


3
++文法情報; (非拡張)ヒアドキュメントは便利で最も堅牢なソリューションですが、明らかにワンライナーではありません。シングルラインソリューションは、ANSI C-引用符で囲まれた文字列(使用して、絶対必要である場合bashkshまたはzsh)することは合理的な解決策である:python -c $'import sys\nfor r in range(10): print(\'rob\')'(あなただけが二重引用符を使用することによって回避することができ、単一引用符()エスケープを心配し、する必要がありますバックスラッシュ)。
mklement0 2016

14

returnを使用して、次の行に入力します。

user@host:~$ python -c "import sys
> for r in range(10): print 'rob'"
rob
rob
...

6
真剣に、これを続ければ何かを捻挫するでしょう。python $(srcdir)/myscript.py偉大な正義のために。
Jason Orendorff、2010年

10

$ python2.6 -c "import sys; [sys.stdout.write('rob\n') for r in range(10)]"

正常に動作します。「[]」を使用してforループをインライン化します。


9

問題はimportステートメントにありません。問題は、制御フローステートメントがPythonコマンドでインラインで機能しないことです。それを交換してくださいimportステートメントを他のステートメントに置き換えれば、同じ問題が発生します。

考えてみてください。Pythonはおそらくすべてをインライン化することはできません。インデントを使用して制御フローをグループ化します。


7

システムがPosix.2に準拠している場合、printfユーティリティを提供する必要があります。

$ printf "print 'zap'\nfor r in range(3): print 'rob'" | python
zap
rob
rob
rob

2
良い解決策ですが、入力文字列内の(形式指定子)などのシーケンスを解釈printf %b '...' | pythonできないため、堅牢性を高めるために使用することをお勧めします。また、Pythonコマンド文字列にシェル拡張を明示的に適用したい場合(混乱を招く可能性があります)を除いて、外部引用には(単一引用符)を使用して、拡張とシェルが適用するバックラッシュ処理の両方を回避することをお勧めします。二重引用符で囲まれた文字列に。printf%d'
mklement0 2016

5

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"
> '

そう。たくさん。より良い。
マーク

4

(10月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でどのように呼び出されるかわかりません:)

お役に立てれば、

乾杯!


(2013年4月9日20:57:30編集EDIT)さて、私はようやく、これらのワンライナーの角かっこが何であるかを見つけたと思います。それらは「リスト内包表記」(どうやら)です。最初に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

覚えていると思いますが、単一行ジェネレーターの標準形式は、括弧内の一種の1行の「for」ループです。これにより、「一発」の反復可能なオブジェクトが生成されます。このオブジェクトは、一方向にのみ反復でき、最後に到達すると再利用できません。

「リスト内包表記」は、通常の大括弧-()-が大括弧-[]に置き換えられていることを除いて、通常の1行ジェネレーターとほとんど同じです。連想リストの理解の主な利点は、「ワンショット」の反復可能オブジェクトではなく「リスト」を生成することです。これにより、前後に移動したり、要素を追加したり、ソートしたりできます。

そして確かにそれはリストです-それは実行されるとすぐにその最初の要素が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。データ構造に記載されています。リスト内包表記—「リスト内包表記はリストを作成する簡潔な方法を提供する」としてのPython v2.7.4のドキュメント。おそらく、ここでリストの制限された「実行可能性」がワンライナーで機能します。

まあ、私がここであまりにもマークを外れていないことを願っています...

EDIT2:ネストされていないforループが2つある1行のコマンドラインを次に示します。どちらも「リスト内包」角括弧で囲まれています。

$ 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]

bforループが明示的に2回実行されたため、2番目の「リスト」には2つの要素があることに注意してください。ただし、sys.stdout.write()どちらの場合も結果は(どうやら)Noneでした。


4

このバリアントは、パイプなしで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"""

++は、クロスプラットフォームの角度とコンバーターです。最初のコマンドは移植性の点で優れています(PowerShellは別として)。ただし、二重引用符を使用する必要があるため、不要なシェル展開のリスクがあるため、最終的には単一の完全に堅牢なクロスプラットフォーム構文はありません。Windowsでは、POSIXのようなシェルとは異なる文字をエスケープする必要があります。PowerShell v3以降で--%は、コマンド文字列引数の前に「stop-parsing」オプションを挿入することで、コマンドラインを機能させることができます。
mklement0 2016

@ mklement0 " 不要なシェル展開 ":まあ、print "path is %%PATH%%"respのようなものへのシェル展開の挿入。print "path is $PATH"通常は、スクリプトまたはコマンドラインで必要なオプションです-プラットフォームで通常のようにエスケープしない限り。他の言語と同じです。(Python構文自体は、競合する方法での%と$の使用を定期的に提案していません。)
kxr

シェル変数参照をPythonソースコードに直接挿入すると、定義上、移植性がなくなります。私の要点は、単一の「シェルフリー」の Pythonコマンドに引数として渡す個別の変数でプラットフォーム固有の参照を構築したとして、二重引用符で囲まれた文字列をポータブルに保護できないため、常に機能するとは限らないということです。たとえば、Pythonコードでリテラルが必要な場合はどうなりますか?POSIXのようなシェルの利点については、それをエスケープした場合でも、追加のが表示されます。まれかもしれませんが、知っておく価値はあります。 $foo\$foocmd.exe\
mklement0

これをWindows PowerShellで実行しようとすると、問題はpython -c exec( "" "..." "")が...のコードを実行できるかどうかに関係なく、何も出力を生成しないことです。か否か; 意味不明なものをそこに置くことができ、結果は同じになります。シェルがstdoutストリームとstderrストリームの両方を「食べている」ように感じます-どうすればそれらを吐き出させることができますか?
ユーリー

2

このスクリプトは、Perlのようなコマンドラインインターフェイスを提供します。

Pyliner-コマンドラインで任意のPythonコードを実行するスクリプト(Pythonレシピ)


別のツールを使用するのではなく、それを修正するための何かが欲しかったと思います。とにかくいいヒント
enrico.bacis

@ enrico.bacisに同意しますが、この回答を追加していただいてうれしいです。このページをグーグルで検索したときの質問答えます。
tbc0 2013

1

これを行う必要があるときは、

python -c "$(echo -e "import sys\nsys.stdout.write('Hello World!\\\n')")"

sys.stdout.writeステートメントの改行のトリプルバックスラッシュに注意してください。


この作品が、あなたは使用しているため、echo -e非標準であると必要とするbashkshまたはzsh、あなたが同様に使用すること$'...'もエスケープ簡略化され、直接文字列を:python -c $'import sys\nsys.stdout.write("Hello World!\\n")'
mklement0

この回答は機能しますが、他の回答はPython3では機能しません

1

私は次の特性を持つ解決策を求めていました:

  1. 読みやすい
  2. 他のツールの出力を処理するためにstdinを読み取ります

他の回答では両方の要件が提供されていなかったため、コマンドラインですべてを実行しながら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
)

0

もう1つのオプションがあります。sys.stdout.writeはNoneを返し、リストを空のままにします

cat somefile.log | python -c "import sys; [sys.stdout.write(line * 2)の場合、sys.stdinの行の行]]


0

stdinに触れて「python cmdfile.py」を渡したかのようにシミュレートしたくない場合は、bashシェルから以下を実行できます。

$ python  <(printf "word=raw_input('Enter word: ')\nimport sys\nfor i in range(5):\n    print(word)")

ご覧のとおり、stdinを使用して入力データを読み取ることができます。内部的に、シェルは入力コマンドの内容の一時ファイルを作成します。


++スクリプト自体でstdinを「使い果たす」ことはありません(-c "$(...)"同じことを行い、POSIXに準拠しています)。<(...)構成に名前を付けるには:プロセス置換 ; kshおよびでも動作しzshます。
mklement0 2016
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.