web-dev-qa-db-ja.com

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

私は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

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

Makefileに含めることができるように、これをワンライナーとして持つことが重要です。

168
user248237

あなたができる

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の答え

155
jspcal

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

python - <<EOF
import sys
for r in range(3): print 'rob'
EOF

または

python - <<-EOF
    import sys
    for r in range(3): print 'rob'
EOF

後者の場合、先頭のタブ文字も削除されます(そして、構造化されたOutlookを実現できます)

EOFの代わりに、ヒアドキュメントの行頭に表示されない任意のマーカーWordを表すことができます(bashのマンページまたは here のヒアドキュメントも参照してください)。

80
xorho

問題は実際には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、ビルトイン「マップ」、およびいくつかの創造的な文字列補間の間で、強力なワンライナーを実行できます。

問題は、どこまで行きたいのか、そしてどこでmakefileが代わりに実行する小さな.pyファイルを作成するほうがよいのではないかということです。

46
Crast

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

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

ヒアドキュメント

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

-<<)の後に<<-を追加すると、tabsを使用してインデントできます(Stackoverflowはタブを変換しますこれを強調するために、スペースを8つインデントしました)。先行タブは削除されます。

<<だけでタブなしでそれを行うことができます:

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

EOFを引用符で囲むと、 パラメーター および 算術展開 ができなくなります。これにより、ヒアドキュメントがより堅牢になります。

受け入れられた回答(およびその他)に対する批判

これは読みにくいです:

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

Forループを取得するためにmapを乱用したり、内包表記をリストしたりしないでください。

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

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

13
Aaron Hall

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

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

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

考えてみてください:pythonはすべてをインライン化できない可能性があります。インデントを使用して制御フローをグループ化します。

8
David Berger

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

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

7
Pierre Pericard

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

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

(10年11月23日19時48分に回答) 私は本当に大きなPythonerではありません-しかし、私は一度この構文を見つけて、どこから来たか忘れていたので、私はそれを文書化すると思いました:

printの代わりにsys.stdout.writeを使用する場合(違いは、sys.stdout.writeは関数として引数を括弧で取りますが、printは't)、1行の場合は、コマンドとforの順序を逆にして、セミコロンを削除し、コマンドを角かっこで囲むことで回避できます。 、つまり:

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

Pythonでこの構文がどのように呼び出されるかわかりません:)

お役に立てれば、

乾杯!


(2013年4月9日火曜日20:57:30編集) さて、私はついに、これらの1ライナーの角括弧が何であるかを見つけたと思います。それらは「リスト内包表記」です(明らかに)。最初に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()を呼び出す必要がないことに注意してください。ただし、後で検査すると、aNoneであることがわかります。

$ 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方向のみで反復処理できるオブジェクトであり、最後に到達すると再利用できません。

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

実際、それはリストです-実行されるとすぐに最初の要素はどれもなくなります:

$ 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。リスト内包表記— Python v2.7.4ドキュメンテーション as「リスト内包表記はリストを作成する簡潔な方法を提供する」;おそらく、それがリストの限られた「実行可能性」がワンライナーで作用するところです。

さて、私はここでひどく外れていないことを願っています...

EDIT2:これは、2つのネストされていないforループを持つ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]

2番目の「リスト」bには、forループが明示的に2回実行されたため、2つの要素があります。ただし、両方の場合のsys.stdout.write()の結果は(明らかに)Noneでした。

4
sdaau

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
kev

このバリアントは、パイプを使用せずに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"""
3
kxr

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

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

2
Drew Gulino

次のプロパティを持つソリューションが必要でした。

  1. 読みやすい
  2. 他のツールの出力を処理するための標準入力を読む

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

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

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

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

0
Thava

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

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

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

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

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

0
Devilholk