เหตุใดจึงมี EOF กลางข้อโต้แย้ง?


20

ฉันต้องการเขียนฟังก์ชั่นทุบตีเล็ก ๆ น้อย ๆ เช่นที่ฉันสามารถบอก bash import osหรือfrom sys import stdoutมันจะวางไข่ล่าม Python ใหม่ด้วยโมดูลที่นำเข้า

fromฟังก์ชั่นหลังมีลักษณะเช่นนี้:

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

ถ้าฉันเรียกสิ่งนี้:

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

ไบต์ในfrom sysคือ

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

ไม่มี EOF อยู่ในนั้น แต่ตัวแปล Python ทำงานเหมือนอ่าน EOF มีการขึ้นบรรทัดใหม่ในตอนท้ายของสตรีมซึ่งคาดว่าจะได้

fromน้องสาวของที่นำเข้าโมดูล Python ทั้งหมดมีลักษณะเช่นนี้และซึ่งแก้ปัญหาโดย sanitizing และประมวลผลสตริงและโดยความล้มเหลวในโมดูลที่ไม่มีอยู่

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
}

นั่นช่วยแก้ปัญหา EOF ที่ไม่ได้อธิบายในกระแส แต่ฉันอยากจะเข้าใจว่าทำไม Python คิดว่ามี EOF

คำตอบ:


42

ตารางในคำตอบ Stack Overflow นี้ (ซึ่งได้มาจากBash แฮกเกอร์ Wiki ) จะอธิบายว่าตัวแปร Bash ที่แตกต่างกันนั้นได้ถูกขยายออกไปอย่างไร:

คุณกำลังทำpython -i -c "from $@"ซึ่งจะกลายเป็นpython -i -c "from sys" "import" "stdout"และใช้เวลาเพียงอาร์กิวเมนต์เดียวดังนั้นจึงใช้คำสั่ง-c from sysคุณต้องการใช้$*ซึ่งจะขยายไปสู่python -i -c "from sys import stdout"(สมมติว่า$IFSไม่มีการตั้งค่าหรือเริ่มต้นด้วยช่องว่าง)


2
ขอบคุณสำหรับการยกเลิกการลบเนื่องจากนี่เป็นข้อมูลที่มีค่า :)
cat

1
ฉันคิดว่านี่ควรเป็นคำตอบที่ได้รับการยอมรับเพราะนี่แก้ปัญหาได้จริงแล้ว upvoted หนึ่งเพิ่งอธิบายปัญหา แต่มันไม่ได้ให้ทางแก้ปัญหาหรือทางเลือกอื่น
Ferrybig

คำตอบที่ดี. ตารางนั้นมาจาก Bash Hackers Wiki คุณสามารถเพิ่มที่มาที่เหมาะสมและยืนยันว่าคุณมีสิทธิ์ในการเผยแพร่หรือไม่
การแข่งขัน Lightness กับโมนิก้า

22

straceและเช่นเคยจะแสดงสิ่งที่เกิดขึ้น:

bash-4.1$ echo $$
3458

และที่อื่น ๆ (หรือคุณสามารถหาวิธีstrace bash ...การเรียกใช้ฟังก์ชัน):

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

และย้อนกลับไปในเปลือกแรก:

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

แล้วกลับไปที่straceเปลือก:

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

ดังนั้น-cอาร์กิวเมนต์ที่เกิดขึ้นจริงเป็น-c "from sys"เพราะวิธีการ"$@"ขยายหรือคำสั่งที่ถูกตัดทอนpythonbarfs เมื่อ


9

$@ในเครื่องหมายคำพูดคู่ขยายไปยังรายการองค์ประกอบ"$1" "$2" "$3"ฯลฯ

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

expand sys import stdout

Python คาดว่ารหัสจะเป็นหนึ่งในอาร์กิวเมนต์ไม่ใช่ชุดของอาร์กิวเมนต์


6

Python กำลังถูกเรียกใช้เป็น

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

(ดูคำตอบของ thrig )

ที่จะได้รับ $@ขยายเป็นสตริงเดียว (สมมติว่ามีเหตุผล$IFS) คุณสามารถใช้$*ภายในเครื่องหมายคำพูดคู่:

python3 -i -c "from $*"

ยืนยันด้วยstrace -e execve:

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

2

Strace จะแสดงสิ่งที่เป็นข้อโต้แย้งที่ใช้ แต่วิธีที่ง่ายที่สุดในการดูสิ่งที่กำลังประมวลผลคือการเพิ่มprintf '<%s> 'ก่อนหน้าแต่ละบรรทัดที่เกี่ยวข้องและการปิดecho(เพื่อสร้างเป็นบรรทัดใหม่):

ดังนั้นฟังก์ชั่นสามารถเปลี่ยนเป็น:

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

และเมื่อเรียกว่า:

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

เป็นที่ชัดเจนว่า "from sys" กำลังถูกส่งไปยัง python เป็นหนึ่งอาร์กิวเมนต์
นั่นคือสิ่งที่หลามได้รับและหลามก็ทำหน้าที่ "จาก sys"

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.