引数の途中に EOF があるのはなぜですか?

引数の途中に EOF があるのはなぜですか?

私は、bash に指示して、モジュールがインポートされた新しい Python インタープリターを生成するような小さな bash 関数を書きたかったのimport osですfrom sys import stdout

後者のfrom関数は次のようになります。

from () {
    echo "from $@" | xxd
    python3 -i -c "from $@"
}

これを呼び出した場合:

$ from sys import stdout
00000000: 6672 6f6d 2073 7973 2069 6d70 6f72 7420  from sys import 
00000010: 7374 646f 7574 0a                        stdout.
  File "<string>", line 1
    from sys
           ^
SyntaxError: invalid syntax
>>> 

バイトfrom sys

66 72 6f 6d 20 73 79 73 20
f  r  o  m     s  y  s    

そこには EOF はありませんが、Python インタープリターは EOF を読み取ったかのように動作します。ストリームの最後には改行がありますが、これは予想どおりです。

fromの姉妹版で、Python モジュール全体をインポートするものは次のようになります。これは、文字列をサニタイズして処理し、存在しないモジュールでは失敗することで問題を解決します。

import () {
  ARGS=$@
  ARGS=$(python3 -c "import re;print(', '.join(re.findall(r'([\w]+)[\s|,]*', '$ARGS')))")
  echo -ne '\0x04' | python3 -i
  python3 -c "import $ARGS" &> /dev/null
  if [ $? != 0 ]; then
    echo "sorry, junk module in list"
  else
    echo "imported $ARGS"
    python3 -i -c "import $ARGS"
  fi
}

これにより、ストリーム内の説明されていない EOF の問題は解決されますが、Python が EOF があると判断する理由を理解したいと思います。

答え1

テーブルこのStack Overflowの回答(これはBash ハッカー Wiki) は、さまざまな Bash 変数がどのように展開されるかを説明しています。

を実行していますpython -i -c "from $@"が、これは に変換されpython -i -c "from sys" "import" "stdout"-c引数を 1 つだけ取るため、コマンド が実行されていますfrom sys。 を使用する必要がありますが$*、これは に展開されますpython -i -c "from sys import stdout"($IFSが設定されていないか、スペースで始まっていると想定)。

答え2

straceいつものように、何が起こっているのかを示します。

bash-4.1$ echo $$
3458

strace bash ...また、他の場所では(または関数呼び出しの方法を理解することもできます):

bash-4.1$ strace -ff -o blah -p 3458

そして最初のシェルに戻ります:

bash-4.1$ from sys import stdout
  File "<string>", line 1
    from sys
           ^
SyntaxError: invalid syntax
>>> 
bash-4.1$ 

そしてstraceシェルに戻ります:

Process 3458 attached
Process 25224 attached
^CProcess 3458 detached
bash-4.1$ grep exec blah.*
blah.25224:execve("/usr/bin/python", ["python", "-i", "-c", "from sys", "import", "stdout"], [/* 54 vars */]) = 0

したがって、実際の-c引数は、展開された方法、または切り捨てられたコマンド-c "from sys"によって発生します。"$@"python

答え3

$@二重引用符で囲むと、要素のリスト"$1" "$2" "$3"などに展開されます。

#!/bin/bash
expand () {
    for string in "from $@" ; do
        echo "$string"
    done
}

expand sys import stdout

Python では、コードが一連の引数ではなく 1 つの引数で指定されることを想定しています。

答え4

Strace は、使用されている引数を表示します。ただし、処理されている内容を確認する最も簡単な方法は、printf '<%s> '関連する各行の前に を追加し、閉じるecho(新しい行として生成するため) ことです。

したがって、関数は次のように変更できます。

from () {
    printf '<%s> ' "from $@"; echo
    printf '<%s> ' python3 -i -c "from $@"; echo
}

そして呼び出されたとき:

$ from sys import stdout
<from sys> <import> <stdout> 
<python3> <-i> <-c> <from sys> <import> <stdout>

「from sys」が 1 つの引数として Python に送信されていることは明らかです。
これが Python が受信するもので、Python は「from sys」に基づいて動作します。

関連情報