¿Por qué hay un EOF en medio de los argumentos?

20

Quería escribir una pequeña función bash para poder decirle a bash import oso from sys import stdoutgenerará un nuevo intérprete de Python con el módulo importado.

La última fromfunción se ve así:

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

Si llamo a esto:

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

Los bytes en from sysson

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

No hay EOF allí, pero el intérprete de Python se comporta como si leyera EOF. Hay una nueva línea al final de la secuencia, que es de esperar.

fromLa hermana, que importa un módulo completo de Python, se ve así, y resuelve el problema desinfectando y procesando la cadena, y fallando en módulos inexistentes.

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
}

Eso resuelve el problema de un EOF inexplicable en la transmisión, pero me gustaría entender por qué Python cree que hay un EOF.

gato
fuente

Respuestas:

42

La tabla en esta respuesta de Desbordamiento de pila (que se obtuvo de Bash Hackers Wiki ) explica cómo se expanden las diferentes variables de Bash:

Lo estás haciendo python -i -c "from $@", que se convierte en python -i -c "from sys" "import" "stdout", y -csolo toma un solo argumento, por lo que ejecuta el comando from sys. Desea usar $*, que se expandirá en python -i -c "from sys import stdout"(suponiendo que no $IFSesté configurado o comience con un espacio).

Michael Mrozek
fuente
2
Gracias por recuperar, ya que esta es información valiosa :)
gato
1
Creo que esta debería ser la respuesta aceptada, ya que esto realmente resuelve el problema, el otro votado simplemente explica el problema, pero no da las soluciones o soluciones alternativas
Ferrybig
Buena respuesta. Esa tabla en realidad proviene de Bash Hackers Wiki. ¿Podría agregar la atribución adecuada y verificar que tiene derecho a distribuir?
ligereza corre con Mónica
22

strace, como siempre, mostrará lo que está sucediendo:

bash-4.1$ echo $$
3458

Y, en otro lugar (o podría descubrir cómo strace bash ...llamar a la función):

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

Y de vuelta en ese primer caparazón:

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

Y luego de vuelta en la stracecáscara:

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

Por lo tanto, el -cargumento real se -c "from sys"debe a cómo "$@"se expande o a un comando truncado que pythonirrita.

thrig
fuente
9

$@entre comillas dobles se expande a una lista de elementos, "$1" "$2" "$3"etc.

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

expand sys import stdout

Python espera que el código esté en un argumento, no en una serie de argumentos.

choroba
fuente
6

Python se invoca como

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

(Ver la respuesta de thrig ).

Para $@expandirse como una sola cadena (suponiendo que sea cuerda $IFS), puede usar $*comillas dobles internas:

python3 -i -c "from $*"

Confirmado con strace -e execve:

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

Strace muestra cuáles son los argumentos utilizados. Pero el método más simple para ver qué se está procesando es agregar un printf '<%s> 'antes de cada línea relevante y un cierre echo(para generar como nueva línea):

Entonces, la función podría cambiarse a esto:

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

Y cuando se llama:

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

Está claro que "de sys" se está enviando a Python como un argumento.
Eso es lo que recibe Python, y Python actúa "desde sys".


fuente