Mengapa ada EOF di tengah pertengkaran?


20

Saya ingin menulis fungsi bash kecil sehingga saya dapat memberitahu bash, import osatau from sys import stdoutdan itu akan menelurkan juru bahasa Python baru dengan modul yang diimpor.

Fungsi yang terakhir fromterlihat seperti ini:

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

Jika saya menyebutnya:

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

Bytes dalam from sysadalah

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

Tidak ada EOF di sana, namun interpreter Python bersikap seolah-olah itu membaca EOF. Ada baris baru di ujung sungai, yang diharapkan.

fromSaudari kita, yang mengimpor seluruh modul Python, terlihat seperti ini, dan yang menyelesaikan masalah dengan membersihkan dan memproses string, dan dengan gagal pada modul yang tidak ada.

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
}

Itu memecahkan masalah EOF yang tidak dapat dijelaskan dalam aliran, tapi saya ingin memahami mengapa Python berpikir ada EOF.

Jawaban:


42

Tabel dalam jawaban Stack Overflow ini (yang mendapatkannya dari Bash Hackers Wiki ) menjelaskan bagaimana berbagai variabel Bash diperluas:

Anda lakukan python -i -c "from $@", yang berubah menjadi python -i -c "from sys" "import" "stdout", dan -chanya membutuhkan satu argumen, jadi itu menjalankan perintah from sys. Anda ingin menggunakan $*, yang akan diperluas python -i -c "from sys import stdout"(dengan asumsi $IFStidak disetel atau dimulai dengan spasi).


2
Terima kasih telah membatalkan penghapusan, karena ini informasi yang berharga :)
kucing

1
Saya pikir ini harus menjadi jawaban yang diterima karena ini benar-benar memecahkan masalah, yang lain tervvotifikasi hanya menjelaskan masalah, tetapi tidak memberikan solusi atau solusi alternatif
Ferrybig

Jawaban yang bagus. Tabel itu sebenarnya berasal dari Bash Hackers Wiki. Bisakah Anda menambahkan atribusi yang tepat dan memverifikasi bahwa Anda memiliki hak untuk mendistribusikan?
Lightness Races dengan Monica

22

strace, seperti biasa, akan menunjukkan apa yang sedang terjadi:

bash-4.1$ echo $$
3458

Dan, di tempat lain (atau Anda bisa mengetahui cara strace bash ...panggilan fungsi):

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

Dan kembali ke shell pertama:

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

Dan kemudian kembali ke straceshell:

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

Dengan demikian, -cargumen yang sebenarnya adalah -c "from sys"karena bagaimana "$@"diperluas, atau perintah terpotong yang pythonmuntah.


9

$@dalam tanda kutip ganda diperluas ke daftar elemen "$1" "$2" "$3"dll.

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

expand sys import stdout

Python mengharapkan kode berada dalam satu argumen, bukan serangkaian argumen.


6

Python sedang dipanggil sebagai

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

(lihat jawaban thrig ).

Untuk $@diperluas sebagai string tunggal (dengan asumsi waras $IFS), Anda dapat menggunakan $*di dalam tanda kutip ganda:

python3 -i -c "from $*"

Dikonfirmasi dengan strace -e execve:

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

2

Strace menunjukkan argumen apa yang digunakan. Tetapi metode paling sederhana untuk melihat apa yang sedang diproses adalah menambahkan printf '<%s> 'sebelum setiap baris yang relevan, dan penutup echo(untuk menghasilkan sebagai baris baru):

Jadi, fungsinya dapat diubah menjadi ini:

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

Dan ketika dipanggil:

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

Jelas bahwa "from sys" dikirim ke python sebagai satu argumen.
Itulah yang diterima python, dan python bekerja pada "from sys".

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.