Почему EOF в середине аргументов?


20

Я хотел написать небольшую функцию 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 существует.

Ответы:


42

Таблица в этом ответе Stack Overflow (полученная из Bash Hackers 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
Спасибо за восстановление, так как это ценная информация :)
— кошка

1
Я думаю, что это должен быть принятый ответ, так как на самом деле это решает проблему, другой, голосующий против, просто объясняет проблему, но не дает решений или альтернативных обходных путей
— Ferrybig

Хороший ответ. Эта таблица на самом деле взята из Bash Hackers Wiki. Не могли бы вы добавить правильную атрибуцию и убедиться, что у вас есть право на распространение?
— Легкость гонок с Моникой

22

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действует barfs.


9

$@в двойных кавычках расширяется до списка элементов "$1" "$2" "$3"и т. д.

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

expand sys import stdout

Python ожидает, что код будет в одном аргументе, а не в серии аргументов.


6

Python вызывается как

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

(см . ответ Трига ).

Чтобы $@раскрыть как одну строку (при условии здравого смысла $IFS), вы можете использовать $*внутри двойных кавычек:

python3 -i -c "from $*"

Подтверждено с strace -e execve:

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

2

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>

Понятно, что «от sys» отправляется на python как один аргумент.
Это то, что получает Python, и Python действует «от sys».

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.