
Quería escribir una pequeña función bash de modo que pueda distinguir bash import os
o from sys import stdout
generará un nuevo intérprete de Python con el módulo importado.
La última from
funció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 sys
son
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.
from
La 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 -c
solo 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 $IFS
no 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 strace
caparazó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
-c
Por lo tanto, el argumento real se -c "from sys"
debe a cómo "$@"
se expande o a un comando truncado que python
continú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".