Hybridcode in Shell-Skripten. Variablen teilen

10

In dieser Antwort wird erläutert, wie ein mehrzeiliges Python-Snippet über die Befehlszeile in einem Terminal ausgeführt wird. Mir ist aufgefallen, dass die Antwort in Shell-Skripten auch bei verschachtelten Einrückungen hervorragend funktioniert, was sehr schön ist, z

#!/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

Drucke:

0 
hello
hello
1
hello
hello
2
hello
hello

Es fällt mir jedoch schwer, Variablen zwischen dem Shell-Skript und dem Python-Snippet zu teilen.

  1. Wie kann ich die Ausgabe des Python-Index im Bash-Skript erfassen? (zB in einer Variablen wie $output).

  2. Wie kann ich eine Bash-Variable (z. B. $some_text) an das Python-Skript übergeben?

Amelio Vazquez-Reina
quelle
1
Sie können python - <<EOFstattdessen tun .
jftr imo das ist schlechter Stil und Sie sollten versuchen, das zu vermeiden
Ulrich Dangel
1
Warum das / zsh-Tag?
Stéphane Chazelas

Antworten:

12

Eine Variable nach Python bringen

Da (wenn der EOFMarker nicht in Anführungszeichen steht) die Variablensubstitution erfolgt, bevor Text vom Heredoc an pythondie Standardeingabe übergeben wird, können Sie die Variable direkt in das Skript werfen.

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

Wenn some_textwar test, würde Python sehen some_text = "test". Beachten Sie jedoch, dass dies als Sicherheitsanfälligkeit durch Code-Injection angesehen werden kann. Wenn some_textwar "; import os; os.system("evil-command"); x = ", zum Beispiel, würde Python sehen:

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

und führe diesen bösen Befehl aus.

Wenn Sie Ihren Python-Code ohne Änderungen direkt in ein Skript ziehen möchten, können Sie Ihre Variable exportieren.

export some_text

und verwenden os.environ, um es abzurufen.

some_text = os.environ['some_text']

Das ist ein viel vernünftigerer / sicherer Ansatz.


Ausgabe von Python

Sie können die Befehlsersetzung verwenden, um die Ausgabe des Skripts zu erfassen.

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

(Beachten Sie, dass alle nachgestellten Zeilenumbrüche entfernt werden.)

Stéphane Chazelas
quelle
Das sieht gut aus. "You could also pass the variable to the script as an argument"Wie würden Sie das tun, wenn Sie sagen , dass das Python-Skript in das Shell-Skript eingebettet ist?
Amelio Vazquez-Reina
Entschuldigung, das habe ich für eine Sekunde vergessen. Ich habe diese Lösung entfernt und eine bessere Lösung hinzugefügt.
Achten Sie darauf, wie Sie den Heredoc-Marker benennen. Wenn es nicht wie in Ihrem Beispiel in Anführungszeichen gesetzt ist, erfolgt die Shell-Erweiterung innerhalb der Heredoc-Zeichenfolge. Wenn Ihr Python-Code beispielsweise nur aus besteht print '$SHELL', ist die Ausgabe /bin/bashoder was auch immer Ihre Shell ist. Wenn Sie die erste Zeile in ändern python - <<'END', lautet die Ausgabe $SHELL. Wenn Sie vorhandenen Code kopieren und einfügen, um ihn in ein Bash-Skript einzubetten, möchten Sie mit ziemlicher Sicherheit keine Shell-Ersetzungen - Sie möchten, dass der Code genauso ausgeführt wird, wie er ausgeführt wird, wenn er nicht in ein Bash-Skript eingebettet ist, oder?
Chris Johnson
8

Das Problem bei Ihrem Ansatz ist, dass das eingebettete Python-Skript keinen Zugriff mehr auf das ursprüngliche stdin hat (da sein stdin ... selbst ist).

Wenn das ein Problem ist, können Sie schreiben:

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

Oder wenn das Python-Skript einfache Anführungszeichen enthält:

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

Oder:

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

Verwenden Sie einen Strich als Dateinamen:

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

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

Ich weiß nicht, ob sys.argv[1:]dies in Python der richtige Weg ist. Für -e / -c können Sie das Ende der Argumente mit - angeben:

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

Ausgabe erfassen und STDERR umleiten:

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

1) Sie können Variablenzuweisungen in eine Datei in Python schreiben und diese dann in Ihrem Bash-Skript als Quelle angeben.

2) Da Ihr Wort (EOF) nicht in Anführungszeichen steht, werden alle Zeilen des hier beschriebenen Dokuments einer Parametererweiterung unterzogen. Sie können dies verwenden, um Dinge an das Python-Skript zu übergeben.

Roland Smith
quelle