Por que existe um EOF no meio dos argumentos?


20

Eu queria escrever uma pequena função bash para que eu possa dizer ao bash, import osou from sys import stdoutisso gerará um novo interpretador Python com o módulo importado.

A última fromfunção é assim:

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

Se eu chamar isso:

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

Os bytes em from syssão

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

Não há EOF lá, mas o interpretador Python está se comportando como se lesse EOF. Há uma nova linha no final do fluxo, que é de se esperar.

fromA irmã de, que importa um módulo Python inteiro, se parece com isso, e que resolve o problema limpando e processando a string e falhando em 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
}

Isso resolve o problema de um EOF inexplicável no fluxo, mas eu gostaria de entender por que o Python acha que existe um EOF.

Respostas:


42

A tabela nesta resposta de estouro de pilha (obtida no Wiki do Bash Hackers ) explica como as diferentes variáveis ​​do Bash são expandidas:

Você está fazendo python -i -c "from $@", que se transforma python -i -c "from sys" "import" "stdout", e -cleva apenas um único argumento, por isso está executando o comando from sys. Você deseja usar $*, que será expandido para python -i -c "from sys import stdout"(assumindo que $IFSestá desativado ou começa com um espaço).


2
Obrigado por undeleting, já que esta é uma informação valiosa :)
cat

11
Eu acho que isso deve ser a resposta aceita como isso realmente resolve o problema, o outro upvoted apenas explica o problema, mas não dar as soluções ou soluções alternativas
Ferrybig

Boa resposta. Na verdade, essa tabela vem do Wiki do Bash Hackers. Você poderia adicionar a atribuição adequada e verificar se tem o direito de distribuir?
Lightness Races com Monica

22

strace, como sempre, mostrará o que está acontecendo:

bash-4.1$ echo $$
3458

E, em outro lugar (ou você pode descobrir como a strace bash ...chamada de função):

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

E de volta nessa primeira concha:

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

E depois de volta no straceshell:

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

Assim, o -cargumento real é -c "from sys"por causa de como "$@"é expandido, ou um comando truncado que pythonvomita.


9

$@entre aspas duplas se expande para uma lista de elementos "$1" "$2" "$3"etc.

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

expand sys import stdout

O Python espera que o código esteja em um argumento, não em uma série de argumentos.


6

Python está sendo chamado como

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

(veja a resposta do thrig ).

Para $@expandir como uma única sequência (assumindo um são $IFS), você pode usar $*aspas duplas:

python3 -i -c "from $*"

Confirmado com strace -e execve:

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

2

Strace mostra quais são os argumentos usados. Mas o método mais simples de ver o que está sendo processado é adicionar um printf '<%s> 'antes de cada linha relevante e um fechamento echo(para gerar como nova linha):

Portanto, a função pode ser alterada para isso:

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

E quando chamado:

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

É claro que "from sys" está sendo enviado para python como um argumento.
É isso que o python recebe e o python atua sobre "from sys".

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.