Kode hibrid dalam skrip shell. Variabel berbagi

10

Jawaban ini membahas cara menjalankan cuplikan Python multi-baris dari baris perintah di terminal. Saya perhatikan bahwa jawabannya bekerja sangat baik di dalam skrip shell, bahkan dengan lekukan bersarang, yang sangat bagus, misalnya

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

cetakan:

0 
hello
hello
1
hello
hello
2
hello
hello

Namun, saya mengalami kesulitan berbagi variabel antara skrip shell dan potongan Python.

  1. Bagaimana saya bisa mengumpulkan output dari skrip python di skrip bash? (misalnya dalam variabel seperti $output).

  2. Bagaimana saya bisa meneruskan variabel bash (misalnya $some_text) ke skrip Python?

Amelio Vazquez-Reina
sumber
1
Anda bisa melakukannya python - <<EOF.
jftr imo ini gaya yang buruk dan Anda harus berusaha menghindarinya
Ulrich Dangel
1
Mengapa tag / zsh?
Stéphane Chazelas

Jawaban:

12

Mendapatkan variabel ke Python

Karena (ketika EOFmarker tidak dikutip) substitusi variabel terjadi sebelum teks dilewatkan dari pythoninput standar ke heredoc , Anda dapat melempar variabel ke kanan dalam skrip.

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

Jika some_textsudah test, python akan melihat some_text = "test". Namun perlu dicatat bahwa ini dapat dilihat sebagai kerentanan injeksi kode. Jika some_textitu "; import os; os.system("evil-command"); x = ", misalnya, python akan melihat:

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

dan jalankan perintah jahat itu.

Jika Anda ingin dapat menarik kode Python langsung ke skrip tanpa modifikasi apa pun, Anda dapat mengekspor variabel Anda.

export some_text

dan gunakan os.environuntuk mengambilnya.

some_text = os.environ['some_text']

Itu pendekatan yang jauh lebih waras / aman.


Mendapatkan output dari Python

Anda dapat menggunakan substitusi perintah untuk mengumpulkan output skrip.

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

(perhatikan bahwa semua karakter baris baru yang dihapus dihapus)

Stéphane Chazelas
sumber
Ini terlihat hebat. Ketika Anda berkata "You could also pass the variable to the script as an argument", bagaimana Anda melakukannya mengingat skrip Python tertanam dalam skrip shell?
Amelio Vazquez-Reina
Maaf, saya lupa tentang itu sebentar. Saya telah menghapus solusi itu dan menambahkan solusi yang lebih baik.
Berhati-hatilah bagaimana Anda memberi nama penanda heredoc. Ketika tidak dikutip seperti pada contoh Anda, ekspansi shell akan terjadi dalam string heredoc. Misalnya, jika kode python Anda terdiri dari hanya print '$SHELL', output akan /bin/bashatau apa pun shell Anda. Jika Anda mengubah baris pertama python - <<'END', hasilnya akan menjadi $SHELL. Jika Anda menyalin dan menempelkan kode yang ada untuk disematkan ke skrip bash, Anda hampir pasti tidak ingin penggantian shell - Anda ingin kodenya dijalankan dengan cara yang sama ketika tidak disematkan dalam skrip bash, bukan?
Chris Johnson
8

Masalah dengan pendekatan Anda adalah bahwa skrip python tertanam tidak lagi memiliki akses ke stdin asli (karena stdin adalah ... itu sendiri).

Jika itu masalah Anda bisa menulis:

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

Atau jika skrip python dapat berisi tanda kutip tunggal:

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

Atau:

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

Gunakan tanda hubung sebagai nama file:

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

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

Saya tidak tahu apakah sys.argv[1:]ini cara yang tepat untuk melakukan ini dengan Python. Untuk -e / -c Anda bisa menentukan akhir argumen dengan -:

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

Menangkap keluaran dan mengarahkan STDERR:

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

1) Anda bisa menulis tugas variabel ke file dengan python, dan kemudian sumber itu dalam skrip bash Anda.

2) Karena kata Anda (EOF) tidak dikutip, semua baris dokumen di sini mengalami ekspansi parameter. Anda dapat menggunakan ini untuk meneruskan barang ke skrip python.

Roland Smith
sumber