Kod hybrydowy w skryptach powłoki. Udostępnianie zmiennych

10

Ta odpowiedź omawia sposób uruchamiania wielowierszowego fragmentu kodu Pythona z wiersza poleceń w terminalu. Zauważyłem, że odpowiedź działa świetnie w skryptach powłoki, nawet w zagnieżdżonych wcięciach, co jest bardzo miłe, np

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

drukuje:

0 
hello
hello
1
hello
hello
2
hello
hello

Jednak trudno mi udostępniać zmienne między skryptem powłoki a fragmentem kodu w języku Python.

  1. Jak mogę zebrać dane wyjściowe indeksu python w skrypcie bash? (np. w zmiennej takiej jak $output).

  2. Jak mogę przekazać zmienną bash (np. $some_text) Do skryptu Python?

Amelio Vazquez-Reina
źródło
1
Możesz python - <<EOFzamiast tego zrobić .
jftr imo to jest zły styl i powinieneś tego unikać
Ulrich Dangel
1
Dlaczego znacznik / zsh?
Stéphane Chazelas

Odpowiedzi:

12

Uzyskiwanie zmiennej do Pythona

Ponieważ (gdy EOFznacznik nie jest cytowany) podstawienie zmiennej następuje przed przekazaniem tekstu z heredoc do pythonstandardowego wejścia, można wrzucić zmienną bezpośrednio do skryptu.

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

Jeśli some_texttak test, python by to zobaczył some_text = "test". Należy jednak pamiętać, że można to postrzegać jako lukę w zabezpieczeniach polegającą na wstrzykiwaniu kodu. Jeśli some_textbyło "; import os; os.system("evil-command"); x = ", na przykład, pyton byłoby zobaczyć:

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

i wykonujcie to złe polecenie.

Jeśli chcesz mieć możliwość ściągnięcia kodu Pythona bezpośrednio do skryptu bez żadnych modyfikacji, możesz wyeksportować swoją zmienną.

export some_text

i użyj, os.environaby go odzyskać.

some_text = os.environ['some_text']

To znacznie zdrowsze / bezpieczniejsze podejście.


Pobieranie danych wyjściowych z Python

Możesz użyć podstawienia polecenia, aby zebrać dane wyjściowe skryptu.

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

(zwróć uwagę, że wszystkie końcowe znaki nowego wiersza są usuwane)

Stéphane Chazelas
źródło
To wygląda świetnie. Kiedy powiedziałeś "You could also pass the variable to the script as an argument", jak byś to zrobił, biorąc pod uwagę, że skrypt Pythona jest osadzony w skrypcie powłoki?
Amelio Vazquez-Reina,
Przepraszam, zapomniałem o tym na sekundę. Usunąłem to rozwiązanie i dodałem lepsze rozwiązanie.
Uważaj, jak nazwiesz znacznik heredoc. Jeśli nie jest cytowany, jak w twoim przykładzie, rozwinięcie powłoki nastąpi w ciągu heredoc. Na przykład, jeśli kod Pythona print '$SHELL'składałby się tylko z tego , wynik byłby taki, /bin/bashjakikolwiek jest twoja powłoka. Jeśli zmienisz pierwszy wiersz na python - <<'END', wynikiem będzie $SHELL. Jeśli kopiujesz i wklejasz istniejący kod, aby osadzić go w skrypcie bash, prawie na pewno nie chcesz zastępowania powłoki - chcesz, aby kod działał w taki sam sposób, jak nie jest osadzony w skrypcie bash, prawda?
Chris Johnson
8

Problem z twoim podejściem polega na tym, że osadzony skrypt Pythona nie ma już dostępu do oryginalnego standardowego wejścia (ponieważ jego standardowe wejście to ... samo).

Jeśli to jest problem, możesz napisać:

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

Lub jeśli skrypt Pythona może zawierać pojedyncze cudzysłowy:

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

Lub:

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

Użyj myślnika jako nazwy pliku:

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

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

Nie wiem, czy sys.argv[1:]jest to dobry sposób na zrobienie tego w Pythonie. Dla -e / -c możesz podać koniec argumentów za pomocą -:

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

Przechwytywanie danych wyjściowych i przekierowywanie STDERR:

x=$(ruby <<'END' 2> /dev/null
puts "a"
abort "b"
END
)
Lri
źródło
2

1) Możesz napisać przypisania zmiennych do pliku w Pythonie, a następnie pobrać je ze skryptu bash.

2) Ponieważ twoje słowo (EOF) nie jest cytowane, wszystkie wiersze dokumentu tutaj podlegają rozszerzeniu parametrów. Możesz użyć tego do przekazania rzeczy do skryptu python.

Roland Smith
źródło