Dlaczego pośrodku argumentów znajduje się EOF?


20

Chciałem napisać małą funkcję bash, tak że mogę powiedzieć bash, import osalbo from sys import stdoutspowoduje to pojawienie się nowego interpretera Pythona z zaimportowanym modułem.

Ta ostatnia fromfunkcja wygląda następująco:

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

Jeśli nazywam to:

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

Bajty w from systo

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

Nie ma tam EOF, ale interpreter Pythona zachowuje się tak, jakby czytał EOF. Na końcu strumienia znajduje się nowa linia, której należy się spodziewać.

fromSiostra, która importuje cały moduł Pythona, wygląda tak i rozwiązuje problem poprzez odkażanie i przetwarzanie łańcucha oraz przez awarię nieistniejących modułów.

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
}

To rozwiązuje problem niewyjaśnionego EOF w strumieniu, ale chciałbym zrozumieć, dlaczego Python uważa, że ​​istnieje EOF.

Odpowiedzi:


42

Tabela w tej odpowiedzi Przepełnienie stosu (która została pobrana z Wiki Bash Hackers ) wyjaśnia, w jaki sposób rozwijane są różne zmienne Bash:

Robisz python -i -c "from $@", co zmienia się w python -i -c "from sys" "import" "stdout", i -cbierze tylko jeden argument, więc uruchamia polecenie from sys. Chcesz użyć $*, który zostanie rozwinięty python -i -c "from sys import stdout"(zakładając, że $IFSjest rozbrojony lub zaczyna się od spacji).


2
Dziękujemy za cofnięcie usunięcia, ponieważ jest to cenna informacja :)
kot

1
Myślę, że powinna to być zaakceptowana odpowiedź, ponieważ tak naprawdę rozwiązuje problem, a druga z głosowaniem tylko wyjaśnia problem, ale nie daje rozwiązań ani alternatywnych obejść
Ferrybig

Dobra odpowiedź. Ta tabela faktycznie pochodzi z Wiki Bash Hackers. Czy możesz dodać prawidłowe przypisanie i zweryfikować, czy masz prawo do rozpowszechniania?
Lekkość ściga się z Moniką

22

strace, jak zawsze pokaże, co się dzieje:

bash-4.1$ echo $$
3458

I gdzie indziej (lub możesz dowiedzieć się, jak strace bash ...wywołać funkcję):

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

I z powrotem w tej pierwszej powłoce:

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

A potem z powrotem w stracepowłoce:

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

Tak więc faktyczny -cargument -c "from sys"wynika z tego, jak "$@"jest rozwinięty, lub z obciętego polecenia, które pythonpojawia się na pasku.


9

$@w podwójnych cudzysłowach rozwija się do listy elementów "$1" "$2" "$3"itp.

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

expand sys import stdout

Python oczekuje, że kod będzie składał się z jednego argumentu, a nie z szeregu argumentów.


6

Python jest wywoływany jako

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

(patrz odpowiedź thriga ).

Aby $@rozwinąć się jako pojedynczy ciąg (zakładając rozsądny $IFS), możesz użyć $*wewnątrz podwójnych cudzysłowów:

python3 -i -c "from $*"

Potwierdzony przez strace -e execve:

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

2

Strace pokazują, jakie są użyte argumenty. Ale najprostszą metodą sprawdzenia, co jest przetwarzane, jest dodanie printf '<%s> 'przed każdą odpowiednią linią i zamknięcie echo(aby wygenerować jako nową linię):

Tak więc funkcję można zmienić na:

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

A kiedy nazywa się:

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

Oczywiste jest, że „z sys” jest wysyłane do Pythona jako jeden argument.
To właśnie otrzymuje Python, a Python działa na „from sys”.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.