ดำเนินการคำสั่งที่เก็บไว้ในตัวแปร


11

ฉันมีคำสั่งเก็บไว้ในตัวแปร สมมติว่าตัวแปร$iมีค่า:

cat -nT index.php |grep 'someregex'

เมื่อฉันพยายามที่จะดำเนินการตัวแปรข้างต้นโดยการพิมพ์$iมันล้มเหลวเพราะเชลล์พยายามที่จะดำเนินการตัวแปรทั้งหมดเป็นคำสั่งเดียว ฉันยังลองใช้eval($i)และใส่$ibackticks

ฉันจะทำให้เชลล์ดำเนินการ$iราวกับว่ามันเป็นคำสั่งได้อย่างไร และทำไมมันไม่ทำงานเหมือนกัน

$i='echo hi'; $i

เป็นเพราะฉันต้องแฮ็คคำพูดเดียว? (เพราะคุณไม่สามารถซ้อนได้) ปัจจุบันโซลูชันของฉันคือ

echo $i > /foo;  . /foo

แต่ฉันไม่ต้องการสร้างไฟล์สำหรับสิ่งนี้เพียงเพื่อลบในภายหลัง

สิ่งที่ฉันหมายถึงโดย "ฉันแฮ็คในคำพูดเดียวที่ฉันทำ:

$i='cat index.php | grep -P '"'"'MYREGEXHERE'"'"

4
คำถามคือทำไมคำสั่งเก็บไว้ในตัวแปรในสถานที่แรก? คุณควรรวบรวมคำสั่งเมื่อคุณเรียกใช้เก็บอาร์กิวเมนต์ตัวเลือกในตัวแปรหรือในอาร์เรย์ คุณสามารถแสดงสคริปต์ทั้งหมดที่คุณมีได้หรือไม่
slhck

คำตอบ:


18

คำตอบสั้น ๆ : ดูBashAQ # 50: ฉันพยายามใส่คำสั่งในตัวแปร แต่กรณีที่ซับซ้อนล้มเหลวเสมอ! .

คำตอบยาว: เป็นเพราะลำดับที่ bash วิเคราะห์คำสั่ง โดยเฉพาะจะมองหาสิ่งต่าง ๆ เช่นไปป์และเปลี่ยนเส้นทางก่อนที่จะขยายค่าตัวแปรและจะไม่ย้อนกลับและมองหาไพพ์ ฯลฯ ในค่าที่ขยาย โดยพื้นฐานแล้วตัวแปรจะถูกแทนที่ประมาณครึ่งทางผ่านกระบวนการแยกวิเคราะห์ดังนั้นค่าจะถูกแยกวิเคราะห์ครึ่งทางก่อนที่จะถูกดำเนินการ

ในการแก้ปัญหานี้คุณต้องตอบคำถามของ @ slhck: ทำไมคำสั่งถูกเก็บไว้ในตัวแปรตั้งแต่แรก ปัญหาจริงที่คุณพยายามแก้ไขคืออะไร มีวิธีแก้ไขที่เป็นไปได้หลายอย่างขึ้นอยู่กับเป้าหมายที่แท้จริง:

  • อย่าเก็บไว้ในตัวแปรเพียงดำเนินการโดยตรง การจัดเก็บคำสั่งเพื่อใช้ในภายหลังนั้นยุ่งยากและหากคุณไม่ต้องการจริงๆ

  • ใช้ฟังก์ชั่นแทนตัวแปร นั่นคือสิ่งที่พวกเขามีไว้เพื่อ:

    i() { cat -nT index.php |grep 'someregex'; }
    i

    ข้อเสียเปรียบหลักของเรื่องนี้คือคุณไม่สามารถสร้างฟังก์ชั่นแบบไดนามิก - คุณไม่สามารถรวมหรือแยกรหัสจากฟังก์ชั่นตามเงื่อนไข (แม้ว่าฟังก์ชั่นเองนั้นอาจมีองค์ประกอบตามเงื่อนไข

  • evalใช้ นี่ควรจะเป็นทางเลือกสุดท้ายเนื่องจากง่ายต่อการรับพฤติกรรมที่ไม่คาดคิด โดยพื้นฐานแล้วมันจะรันคำสั่งผ่านการแยกวิเคราะห์เต็มรูปแบบอื่นดังนั้นท่อทั้งหมดจึงมีความหมายเต็ม - แต่ก็หมายความว่าบางส่วนของคำสั่งที่คุณคิดว่าเป็นเพียงข้อมูลก็จะถูกแจงและอาจถูกดำเนินการ ชื่อไฟล์ที่ประกอบด้วยอักขระเมตาของเชลล์ (ไปป์, อัฒภาค, อัญประกาศ / apostrophe, ฯลฯ ) อาจมีผลกระทบที่แปลกและอันตรายบางครั้ง หากคุณใช้evalอย่างน้อยสองข้อความอ้างถึงสตริงมิฉะนั้นเนื้อหาของมันเป็นหลักได้รับการแยกวิเคราะห์ครั้งหนึ่งและครึ่งกับผลลัพธ์ที่แม้แต่แปลก

    i="cat -nT index.php |grep 'someregex'"
    eval "$i"

แก้ไข: แนวทางการเก็บคำสั่งในตัวแปรอีกวิธีหนึ่งคือการใช้อาร์เรย์แทนตัวแปรง่ายๆ สิ่งนี้ช่วยให้คุณสามารถเก็บคำสั่งด้วยอาร์กิวเมนต์ที่ซับซ้อน (เช่นมีช่องว่าง) โดยไม่มีปัญหา แต่จะไม่เก็บสิ่งต่าง ๆ เช่นไปป์และเปลี่ยนเส้นทาง ดังนั้นวิธีการอาเรย์จะไม่เป็นประโยชน์ในกรณีนี้โดยเฉพาะ


+1 สำหรับevalวิธีการ สิ่งนี้ทำให้ฉันหยุดถามคำถามเดียวกัน :) ฉันมีบางอย่างเช่นsudo rsync -aAHXi -n --delete-excluded --exclude={"/dev/*","/proc/*","/sys/*","/tmp/*","/run/*","/lost+found","/mnt/DATA/*","/var/log/*","/var/swap","/var/cache/apt/archives/*.deb"} -e ssh root@piac_wireless:/ /home/mrx/Docs/RPi/backup/piac/piac_usb-rootที่ไม่ได้ดำเนินการแยกอย่างถูกต้อง
dentex

@dentex ฉันขอแนะนำไม่ให้ใช้evalสิ่งนี้ - มีหลายวิธีที่ผิดพลาด อาเรย์จะเป็นวิธีที่ดีกว่าในการทำเช่นนี้ ดูที่นี่ , ที่นี่และที่นี่เพื่อดูตัวอย่าง
Gordon Davisson

ขอบคุณสำหรับคำแนะนำ ฉันจะพยายามใช้โซลูชันกับอาร์เรย์เพื่อส่งรายการยกเว้นไปยัง rsync
dentex

4

นี่ควรใช้งานได้:

a="cat -nT index.php |grep 'someregex'"
eval "$a"

ระวังว่าevalอาจก่อให้เกิดช่องโหว่และสถานการณ์พฤติกรรมที่ไม่คาดคิดดังนั้นจะต้องใช้อย่างระมัดระวังเป็นพิเศษ


หากคุณใช้งานevalคุณจำเป็นต้องใช้เครื่องหมายคำพูดคู่ ( eval "$i") เพื่อหลีกเลี่ยงเอฟเฟกต์พิเศษแปลก ๆ ตัวอย่างเช่นลองด้วยa="cat -nT index.php |grep ' .* '"(เช่น regex ควรจับคู่สองช่องว่างกับลำดับของอักขระระหว่างพวกเขา) และดูว่าเกิดอะไรขึ้นจริง สำหรับเครดิตเพิ่มเติมอธิบายว่าทำไมมันจึงเกิดขึ้น
Gordon Davisson

เพิ่มเครื่องหมายคำพูดคู่ @GordonDavisson แล้ว
jlliagre

2

เมื่อประกาศตัวแปรคุณไม่ควรใช้เครื่องหมายดอลลาร์เป็นคำนำหน้า

ลองสิ่งนี้:

i='echo hi'; $i

1
ถูกต้อง แต่มันไม่ใช่คำตอบสำหรับคำถามของ OP จริงๆ
slhck

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