
Я хотел написать небольшую функцию bash, которую я мог бы использовать для вызова bash import os
или , from sys import stdout
и она бы создала новый интерпретатор Python с импортированным модулем.
Последняя 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
принимает только один аргумент, поэтому он запускает команду 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 ожидает, что код будет содержать один аргумент, а не серию аргументов.
решение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" отправляется в python как один аргумент.
Это то, что python получает, и python действует на "from sys".