Pourquoi y a-t-il un EOF au milieu des arguments?

20

Je voulais écrire une petite fonction bash pour que je puisse dire bash, import osou from sys import stdoutelle engendrera un nouvel interpréteur Python avec le module importé.

La dernière fromfonction ressemble à ceci:

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

Si j'appelle cela:

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

Les octets en from syssont

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

Il n'y a pas d'EOF là-dedans, mais l'interpréteur Python se comporte comme s'il lisait EOF. Il y a une nouvelle ligne à la fin du flux, ce qui est à prévoir.

fromLa sœur de, qui importe un module Python entier, ressemble à ceci, et qui résout le problème en nettoyant et en traitant la chaîne, et en échouant sur des modules inexistants.

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
}

Cela résout le problème d'un EOF inexpliqué dans le flux, mais j'aimerais comprendre pourquoi Python pense qu'il y a un EOF.

chat
la source

Réponses:

42

Le tableau de cette réponse Stack Overflow (qui provient du wiki Bash Hackers ) explique comment les différentes variables Bash sont développées:

Vous faites python -i -c "from $@", qui se transforme en python -i -c "from sys" "import" "stdout"et -cne prend qu'un seul argument, il exécute donc la commande from sys. Vous souhaitez utiliser $*, qui se développera en python -i -c "from sys import stdout"(en supposant qu'il $IFSn'est pas défini ou commence par un espace).

Michael Mrozek
la source
2
Merci de la suppression, car il s'agit d'informations précieuses :)
cat
1
Je pense que cela devrait être la réponse acceptée car cela résout réellement le problème, l'autre voté explique simplement le problème, mais il ne donne pas les solutions ou les solutions alternatives
Ferrybig
Bonne réponse. Ce tableau provient en fait du wiki Bash Hackers. Pourriez-vous ajouter l'attribution appropriée et vérifier que vous avez le droit de distribuer?
Courses de légèreté avec Monica
22

strace, comme toujours, montrera ce qui se passe:

bash-4.1$ echo $$
3458

Et ailleurs (ou vous pourriez comprendre comment strace bash ...appeler la fonction):

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

Et de retour dans ce premier shell:

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

Et puis de retour dans la stracecoque:

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

Ainsi, l' -cargument réel est -c "from sys"dû à la façon dont "$@"est développé, ou à une commande tronquée qui se pythondérobe.

branler
la source
9

$@entre guillemets se développe en une liste d'éléments, "$1" "$2" "$3"etc.

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

expand sys import stdout

Python s'attend à ce que le code soit dans un seul argument, pas dans une série d'arguments.

choroba
la source
6

Python est invoqué en tant que

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

(voir la réponse de Thrig ).

Pour être $@développé en une seule chaîne (en supposant un sens $IFS), vous pouvez utiliser $*des guillemets doubles:

python3 -i -c "from $*"

Confirmé avec strace -e execve:

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

Strace montre quels sont les arguments utilisés. Mais la méthode la plus simple pour voir ce qui est en cours de traitement est d'ajouter un printf '<%s> 'avant chaque ligne pertinente, et une fermeture echo(pour générer comme nouvelle ligne):

Ainsi, la fonction pourrait être modifiée comme suit:

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

Et quand on l'appelle:

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

Il est clair que "from sys" est envoyé à python comme un argument.
C'est ce que python reçoit et python agit sur "from sys".


la source