Code hybride dans les scripts shell. Partage de variables

10

Cette réponse explique comment exécuter un extrait de code Python sur plusieurs lignes à partir de la ligne de commande dans un terminal. J'ai remarqué que la réponse fonctionne très bien dans les scripts shell, même avec une indentation imbriquée, ce qui est très bien, par exemple

#!/bin/bash
some_text="Hello world"
echo $some_text

cat <<EOF | python -
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF

impressions:

0 
hello
hello
1
hello
hello
2
hello
hello

Cependant, j'ai du mal à partager des variables entre le script shell et l'extrait de code Python.

  1. Comment puis-je collecter la sortie de l'indice python dans le script bash? (par exemple dans une variable telle que $output).

  2. Comment puis-je passer une variable bash (par exemple $some_text) au script Python?

Amelio Vazquez-Reina
la source
1
Vous pouvez faire à la python - <<EOFplace.
jftr imo c'est du mauvais style et vous devriez essayer d'éviter ça
Ulrich Dangel
1
Pourquoi la balise / zsh?
Stéphane Chazelas

Réponses:

12

Obtenir une variable pour Python

Puisque (lorsque le EOFmarqueur n'est pas cité) la substitution de variable se produit avant que le texte ne soit passé de l'hérédoc à pythonl'entrée standard de, vous pouvez lancer la variable directement dans le script.

python - <<EOF
some_text = "$some_text"
EOF

Si some_textc'était le cas test, python verrait some_text = "test". Notez cependant qu'il peut être vu comme une vulnérabilité d'injection de code. Si some_textétait "; import os; os.system("evil-command"); x = ", par exemple, python verrait:

some_text = ""; import os; os.system("evil-command"); x = ""

et exécutez ce mauvais commandement.

Si vous voulez pouvoir extraire votre code Python directement dans un script sans aucune modification, vous pouvez exporter votre variable.

export some_text

et utilisez-le os.environpour le récupérer.

some_text = os.environ['some_text']

C'est une approche beaucoup plus saine / sûre.


Obtenir la sortie de Python

Vous pouvez utiliser la substitution de commandes pour collecter la sortie du script.

output=$(
python - <<EOF
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF
)

(notez que tous les caractères de fin de ligne sont supprimés)

Stéphane Chazelas
la source
Ça a l'air génial. Quand vous avez dit "You could also pass the variable to the script as an argument", comment feriez-vous cela étant donné que le script Python est intégré dans le script shell?
Amelio Vazquez-Reina
Désolé, j'ai oublié ça une seconde. J'ai supprimé cette solution et ajouté une meilleure solution.
Faites attention à la manière dont vous nommez le marqueur hérédoc Quand il n'est pas cité comme dans votre exemple, l'expansion du shell se produira dans la chaîne hérédoc. Par exemple, si votre code python se composait de juste print '$SHELL', la sortie serait /bin/bashou quoi que soit votre shell. Si vous changez la première ligne en python - <<'END', la sortie serait $SHELL. Si vous copiez et collez du code existant pour l'incorporer dans un script bash, vous ne voulez certainement pas de substitutions de shell - vous voulez que le code s'exécute de la même manière qu'il n'est pas incorporé dans un script bash, non?
Chris Johnson
8

Le problème avec votre approche est que le script python intégré n'a plus accès au stdin d'origine (puisque son stdin est ... lui-même).

Si c'est un problème, vous pouvez écrire:

python -c '
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
'

Ou si le script python peut contenir des guillemets simples:

python -c "$(cat << 'EOF'
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF
)"

Ou:

python <(cat << 'EOF'
import sys;
for r in range(3):
  print r
  for a in range(2):
    print "hello"
EOF
)
Stéphane Chazelas
la source
6

Utilisez un tiret comme nom de fichier:

ruby - a b <<'END'
puts ARGV.join(",")
END

python - a b <<'END'
import sys
print ",".join(sys.argv[1:])
END

Je ne sais pas si sys.argv[1:]c'est la bonne façon de le faire en Python. Pour -e / -c, vous pouvez spécifier la fin des arguments avec -:

set -- -a -b -c
ruby -e 'puts ARGV.join(",")' -- "$@"
python -c 'import sys; print ",".join(sys.argv[2:])' -- "$@"

Capture de sortie et redirection de STDERR:

x=$(ruby <<'END' 2> /dev/null
puts "a"
abort "b"
END
)
Lri
la source
2

1) Vous pouvez écrire des affectations de variables dans un fichier en python, puis les sourcez dans votre script bash.

2) Puisque votre mot (EOF) n'est pas cité, toutes les lignes du document ici sont soumises à une expansion des paramètres. Vous pouvez l'utiliser pour transmettre des éléments au script python.

Roland Smith
la source