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

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

Quería escribir una pequeña función bash de modo que pueda distinguir 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 ningún EOF allí, pero el intérprete de Python se comporta como si leyera EOF. Hay una nueva línea al final de la transmisión, lo cual es de esperar.

fromLa hermana de, que importa un módulo Python completo, tiene este aspecto 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 secuencia, pero me gustaría entender por qué Python cree que hay un EOF.

Respuesta1

la mesa enesta respuesta de desbordamiento de pila(que lo obtuvo delWiki Hackers de Bash) explica cómo se expanden las diferentes variables de Bash:

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

Respuesta2

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

bash-4.1$ echo $$
3458

Y, en otro lugar (o podrías descubrir cómo realizar strace bash ...la llamada 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 al stracecaparazón:

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

-cPor lo tanto, el argumento real se -c "from sys"debe a cómo "$@"se expande o a un comando truncado que pythoncontinúa vomitando.

Respuesta3

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

Respuesta4

Strace muestra cuáles son los argumentos utilizados. Pero el método más sencillo para ver qué se está procesando es agregar un printf '<%s> 'antes de cada línea relevante y un cierre echo(para generar una 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 lo llaman:

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

Está claro que "from sys" se envía a Python como un argumento.
Eso es lo que recibe Python, y Python actúa "desde sys".

información relacionada