ฉันจะกำหนดเอาต์พุตของคำสั่งให้กับตัวแปร shell ได้อย่างไร?


76

ฉันต้องการกำหนดผลลัพธ์ของนิพจน์ให้กับตัวแปรและเชื่อมโยงกับสตริงแล้วสะท้อนกลับ นี่คือสิ่งที่ฉันได้รับ:

#!/bin/bash
cd ~/Desktop;
thefile= ls -t -U | grep -m 1 "Screen Shot";
echo "Most recent screenshot is: "$thefile;

แต่ผลลัพธ์นั้น:

Screen Shot 2011-07-03 at 1.55.43 PM.png
Most recent screenshot is: 

ดังนั้นดูเหมือนว่าจะไม่ได้รับมอบหมาย$thefileและกำลังถูกพิมพ์ขณะดำเนินการ


คำตอบ:


108

การกำหนดเชลล์เป็นคำเดียวโดยไม่มีช่องว่างหลังเครื่องหมายเท่ากับ ดังนั้นสิ่งที่คุณเขียนกำหนดค่าว่างให้กับthefile; นอกจากนี้เนื่องจากการกำหนดค่าถูกจัดกลุ่มด้วยคำสั่งทำให้thefileตัวแปรสภาพแวดล้อมและการกำหนดเป็นแบบโลคัลสำหรับคำสั่งนั้นนั่นคือเฉพาะการเรียกเพื่อlsดูค่าที่กำหนด

คุณต้องการดักจับเอาต์พุตของคำสั่งดังนั้นคุณต้องใช้การทดแทนคำสั่ง :

thefile=$(ls -t -U | grep -m 1 "Screen Shot")

(บางวรรณกรรมแสดงไวยากรณ์ทางเลือกthefile=`ls …`; ไวยากรณ์backquote เทียบเท่ากับดอลลาร์ - วงเล็บไวยากรณ์ยกเว้นว่าการอ้างอิงภายใน backquotes บางครั้งก็แปลกดังนั้นใช้เพียง$(…))

ข้อสังเกตอื่น ๆ เกี่ยวกับสคริปต์ของคุณ:

  • การรวม-t(เรียงลำดับตามเวลา) กับ-U(ไม่เรียงลำดับ) ไม่สมเหตุสมผล -tเพียงแค่ใช้
  • แทนที่จะใช้grepจับคู่ภาพหน้าจอจะชัดเจนกว่าในการส่งสัญลักษณ์แทนlsและใช้headเพื่อจับภาพไฟล์แรก:

    thefile=$(ls -t *"Screen Shot"* | head -n 1)
  • มันเป็นโดยทั่วไปเป็นความคิดที่ดีที่จะแยกการส่งออกของ lsสิ่งนี้อาจล้มเหลวได้ไม่ดีนักหากคุณมีชื่อไฟล์ที่มีอักขระที่ไม่สามารถพิมพ์ได้ อย่างไรก็ตามการเรียงลำดับไฟล์ตามวันที่ทำได้ยากโดยไม่มีlsดังนั้นจึงเป็นทางออกที่ยอมรับได้หากคุณรู้ว่าคุณจะไม่มีตัวอักษรที่ไม่สามารถพิมพ์ได้หรือแบ็กสแลชในชื่อไฟล์

  • ใช้เครื่องหมายคำพูดคู่รอบ ๆ การแทนที่ตัวแปรเสมอเช่นที่นี่เขียน

    echo "Most recent screenshot is: $thefile"

    หากไม่มีเครื่องหมายอัญประกาศคู่ค่าของตัวแปรจะถูกขยายใหม่ซึ่งจะทำให้เกิดปัญหาหากมีช่องว่างหรืออักขระพิเศษอื่น ๆ

  • คุณไม่จำเป็นต้องใช้เครื่องหมายอัฒภาคในตอนท้ายของบรรทัด พวกมันซ้ำซ้อน แต่ไม่เป็นอันตราย
  • ในเชลล์สคริปต์มักจะเป็นความคิดที่ดีที่จะรวมset -eไว้ สิ่งนี้บอกให้เชลล์ออกหากคำสั่งใด ๆ ล้มเหลว (โดยส่งคืนสถานะที่ไม่ใช่ศูนย์)

หากคุณมี GNU พบ (โดยเฉพาะอย่างยิ่งถ้าคุณใช้ Linux หรือ Cygwin ที่ไม่ได้ฝังตัว) มีวิธีอื่นในการค้นหาไฟล์ล่าสุด: มีfindรายการไฟล์และวันที่และใช้sortและtailเพื่อแยกไฟล์ที่อายุน้อยที่สุด

thefile=$(find -maxdepth 1 -type f -name "*Screen Shot*" -printf "%T@ %p" |
          sort -k 1n | tail -n 1)

หากคุณยินดีที่จะเขียนสคริปต์นี้ใน zsh แทนที่จะทุบตีมีวิธีที่ง่ายกว่ามากในการจับไฟล์ใหม่ล่าสุดเนื่องจาก zsh มีตัวระบุแบบกลมที่อนุญาตให้ใช้สัญลักษณ์แทนการจับคู่ไม่เพียง แต่ในชื่อ แต่ยังรวมถึงข้อมูลเมตาของไฟล์ (om[1])ส่วนหนึ่งหลังจากแบบแผนคือบ่น glob นั้น omเรียงลำดับการแข่งขันตามอายุที่เพิ่มขึ้น (เช่นตามเวลาการแก้ไขใหม่ล่าสุดก่อน) และ[1]แยกการแข่งขันครั้งแรกเท่านั้น การจับคู่ทั้งหมดจะต้องอยู่ในวงเล็บเพราะเป็นอาร์เรย์ทางเทคนิคเนื่องจาก globbing ส่งคืนรายการไฟล์แม้ว่า[1]หมายความว่าในกรณีนี้รายการจะมี (มากที่สุด) หนึ่งไฟล์

#!/bin/zsh
set -e
cd ~/Desktop
thefile=(*"Screen Shot"*(om[1]))
echo "Most recent screenshot is: $thefile"

7
ว้าว! ข้อมูลมากกว่าที่ฉันคาดหวังไว้ ขอบคุณสำหรับคำตอบของคุณหลายครั้ง ฉันซาบซึ้งในความพยายามทั้งหมดของคุณ! คุณเพิ่งแสดงให้ฉันเห็นว่าฉันมีอะไรให้เรียนรู้มากมายจริงๆ ฉันออกไปซื้อหนังสือเกี่ยวกับ bash: P.
นาธานจี

3
นั่นคือสิ่งที่ฉันจะเรียกคำตอบที่ชัดเจน ! :)
alex

4

ถ้าคุณต้องการทำด้วยคำสั่งหลายบรรทัด / หลายคำสั่ง / s คุณสามารถทำได้:

output=$( bash <<EOF
#multiline/multiple command/s
EOF
)

หรือ:

output=$(
#multiline/multiple command/s
)

ตัวอย่าง:

#!/bin/bash
output="$( bash <<EOF
echo first
echo second
echo third
EOF
)"
echo "$output"

เอาท์พุท:

first
second
third

นี่เป็นคำตอบที่ดี มีวิธีที่ฉันสามารถระบุตัวแปรเป็นส่วนหนึ่งของคำสั่งหรือไม่ เช่นoutput=$(echo $someVariable)
Zach Smith
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.