Warum gibt es eine EOF in der Mitte der Argumente?

20

Ich wollte eine kleine Bash-Funktion schreiben, die Bash erkennt import osoder from sys import stdouteinen neuen Python-Interpreter mit dem importierten Modul erzeugt.

Die letztere fromFunktion sieht folgendermaßen aus:

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

Wenn ich das nenne:

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

Die Bytes in from syssind

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

Es gibt kein EOF, aber der Python-Interpreter verhält sich so, als würde er EOF lesen. Es gibt eine neue Zeile am Ende des Streams, was zu erwarten ist.

fromDie Schwester von Python, die ein ganzes Python-Modul importiert, sieht so aus und löst das Problem, indem sie die Zeichenfolge bereinigt und verarbeitet und bei nicht vorhandenen Modulen Fehler verursacht.

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
}

Das löst das Problem eines ungeklärten EOF im Stream, aber ich würde gerne verstehen, warum Python denkt, dass es einen EOF gibt.

Katze
quelle

Antworten:

42

Die Tabelle in dieser Stack Overflow-Antwort (die aus dem Bash Hackers Wiki stammt ) erklärt, wie die verschiedenen Bash-Variablen erweitert werden:

Sie tun python -i -c "from $@", was sich in ein einziges Argument verwandelt python -i -c "from sys" "import" "stdout", und -cführen den Befehl aus from sys. Sie möchten verwenden $*, in das expandiert wird python -i -c "from sys import stdout"(vorausgesetzt, es $IFSist nicht gesetzt oder beginnt mit einem Leerzeichen).

Michael Mrozek
quelle
2
Vielen Dank für das Wiederherstellen, da dies wertvolle Informationen sind :)
Katze
1
Ich denke, dies sollte die akzeptierte Antwort sein, da dies das Problem tatsächlich löst, der andere hochgeladene erklärt nur das Problem, aber es gibt keine Lösungen oder alternative
Problemumgehungen
Gute Antwort. Diese Tabelle stammt tatsächlich aus dem Bash Hackers Wiki. Könnten Sie die richtige Namensnennung hinzufügen und überprüfen, ob Sie das Recht zum Verteilen haben?
Leichtigkeit Rennen mit Monica
22

stracewird wie immer zeigen, was los ist:

bash-4.1$ echo $$
3458

Und an anderer Stelle (oder Sie könnten herausfinden, wie strace bash ...der Funktionsaufruf funktioniert):

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

Und zurück in dieser ersten Schale:

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

Und dann zurück in die straceSchale:

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

Das eigentliche -cArgument liegt also -c "from sys"daran, wie der "$@"Befehl erweitert oder abgeschnitten wird python.

Thrig
quelle
9

$@in doppelten Anführungszeichen wird zu einer Liste von Elementen "$1" "$2" "$3"usw. erweitert.

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

expand sys import stdout

Python erwartet, dass sich der Code in einem Argument befindet, nicht in einer Reihe von Argumenten.

Choroba
quelle
6

Python wird aufgerufen als

execve("/usr/bin/python", ["python", "-i", "-c", "from sys", "import", "stdout"], [/* 54 vars */])

(Siehe Thrigs Antwort ).

Wenn Sie $@als einzelne Zeichenfolge erweitert werden möchten (vorausgesetzt, es handelt sich um eine vernünftige Zeichenfolge $IFS), können Sie $*doppelte Anführungszeichen verwenden:

python3 -i -c "from $*"

Bestätigt mit strace -e execve:

execve("/usr/bin/python", ["python", "-i", "-c", "from sys import stdout"], [/* 54 vars */]) = 0
Toby Speight
quelle
2

Strace zeigen, was sind die Argumente verwendet. Die einfachste Methode, um zu sehen, was verarbeitet wird, ist das Hinzufügen eines printf '<%s> 'vor jeder relevanten Zeile und eines Abschlusses echo(um als neue Zeile zu generieren):

Die Funktion könnte also folgendermaßen geändert werden:

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

Und wenn angerufen:

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

Es ist klar, dass "from sys" als ein Argument an Python gesendet wird.
Das ist es, was Python empfängt, und Python wirkt auf "from sys".


quelle