Por que há um EOF no meio dos argumentos?

20

Eu queria escrever uma pequena função bash de tal forma que eu possa dizer bash, import os ou from sys import stdout e ela irá gerar um novo interpretador Python com o módulo importado.

A última função from tem esta aparência:

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

Se eu ligar para 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 sys são

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

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

A irmã de

from , 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 '
from () {
    echo "from $@" | xxd
    python3 -i -c "from $@"
}
x04' | 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 gostaria de entender por que o Python acha que existe um EOF.

    
por cat 18.02.2016 / 17:53

5 respostas

42

A tabela em esta resposta de estouro de pilha (que obteve do Wiki do Bash Hackers ) explica como as diferentes variáveis do Bash são expandidas:

Vocêestáfazendopython-i-c"from $@" , que se transforma em python -i -c "from sys" "import" "stdout" , e -c usa apenas um único argumento, por isso está executando o comando from sys . Você deseja usar $* , que será expandido em python -i -c "from sys import stdout" (assumindo que $IFS não está definido ou começa com um espaço).

    
por 18.02.2016 / 18:14
22

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

bash-4.1$ echo $$
3458

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

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

E de volta ao primeiro shell:

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

E, em seguida, de volta ao shell 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

Assim, o argumento -c real é -c "from sys" devido a como "$@" é expandido ou um comando truncado em que python barfs.

    
por 18.02.2016 / 18:09
9

$@ nas 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.

    
por 18.02.2016 / 18:08
6

Python está sendo chamado como

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

(veja resposta do thrig ).

Para obter $@ expandido como uma única string (assumindo um sane $IFS ), você pode usar $* entre 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
    
por 18.02.2016 / 18:58
2

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

Então, 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 "de sys" está sendo enviado para python como um argumento.
Isso é o que python recebe, e python atua em "from sys".

    
por 19.02.2016 / 05:06

Tags