ฉันจะทดสอบว่าคำสั่งแสดงผลสตริงว่างเปล่าได้อย่างไร?
ifne
สำหรับสิ่งนี้ joeyh.name/code/moreutils
ฉันจะทดสอบว่าคำสั่งแสดงผลสตริงว่างเปล่าได้อย่างไร?
ifne
สำหรับสิ่งนี้ joeyh.name/code/moreutils
คำตอบ:
ก่อนหน้านี้คำถามถามว่าจะตรวจสอบว่ามีไฟล์ในไดเรกทอรีหรือไม่ รหัสต่อไปนี้ประสบความสำเร็จ แต่ดูคำตอบของ rspสำหรับการแก้ปัญหาที่ดีกว่า
คำสั่งไม่ส่งคืนค่า - พวกเขาส่งออกพวกเขา คุณสามารถจับเอาท์พุทนี้โดยใช้การทดแทนคำสั่ง ; $(ls -A)
เช่น คุณสามารถทดสอบสตริงที่ไม่ว่างใน Bash ดังนี้:
if [[ $(ls -A) ]]; then
echo "there are files"
else
echo "no files found"
fi
โปรดทราบว่าฉันใช้-A
มากกว่า-a
เพราะจะไม่ใช้รายการไดเรกทอรีสัญลักษณ์ ( .
) และ parent ( ..
)
หมายเหตุ:ในฐานะที่ชี้ให้เห็นในความคิดเห็นที่คำสั่งเปลี่ยนตัวไม่จับท้ายบรรทัดใหม่ ดังนั้นหากคำสั่งให้ผลลัพธ์บรรทัดใหม่เท่านั้นการแทนที่จะไม่จับอะไรเลยและการทดสอบจะส่งกลับค่าเท็จ ในขณะที่ไม่น่าเป็นไปได้มากสิ่งนี้อาจเป็นไปได้ในตัวอย่างด้านบนเนื่องจากบรรทัดใหม่หนึ่งบรรทัดเป็นชื่อไฟล์ที่ถูกต้อง! ข้อมูลเพิ่มเติมในคำตอบนี้
หากคุณต้องการตรวจสอบว่าคำสั่งเสร็จสมบูรณ์แล้วคุณสามารถตรวจสอบ$?
ซึ่งมีรหัสทางออกของคำสั่งสุดท้าย (ศูนย์สำหรับความสำเร็จไม่ใช่ศูนย์สำหรับความล้มเหลว) ตัวอย่างเช่น:
files=$(ls -A)
if [[ $? != 0 ]]; then
echo "Command failed."
elif [[ $files ]]; then
echo "Files found."
else
echo "No files found."
fi
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then ...; fi
ขอบคุณnetj
สำหรับคำแนะนำในการปรับปรุงต้นฉบับของฉัน:if [[ $(ls -A | wc -c) -ne 0 ]]; then ...; fi
นี่เป็นคำถามเก่า แต่ฉันเห็นอย่างน้อยสองสิ่งที่ต้องการการปรับปรุงหรือการชี้แจงอย่างน้อย
ปัญหาแรกที่ผมเห็นก็คือว่าส่วนใหญ่ของตัวอย่างที่ให้ไว้ที่นี่ก็ไม่ได้ทำงาน พวกเขาใช้ls -al
และls -Al
คำสั่ง - ซึ่งทั้งสองออกสตริงที่ไม่ว่างเปล่าในไดเรกทอรีว่าง ตัวอย่างเหล่านั้นมักจะรายงานว่ามีไฟล์แม้ว่าจะไม่มีก็ตาม
ด้วยเหตุนี้คุณควรใช้เพียงls -A
- ทำไมทุกคนต้องการใช้-l
สวิตช์ซึ่งหมายถึง "ใช้รูปแบบรายการแบบยาว" เมื่อสิ่งที่คุณต้องการคือการทดสอบว่ามีผลลัพธ์หรือไม่?
ดังนั้นคำตอบส่วนใหญ่ที่นี่จึงไม่ถูกต้อง
ปัญหาที่สองคือในขณะที่บางคำตอบที่ดีทำงาน (ผู้ที่ไม่ได้ใช้ls -al
หรือls -Al
แต่ls -A
แทน) พวกเขาทั้งหมดทำอะไรเช่นนี้:
สิ่งที่ฉันอยากจะแนะนำให้ทำคือ:
using head -c1
ตัวอย่างเช่นแทนที่จะเป็น:
if [[ $(ls -A) ]]
ฉันจะใช้:
if [[ $(ls -A | wc -c) -ne 0 ]]
# or:
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]
แทน:
if [ -z "$(ls -lA)" ]
ฉันจะใช้:
if [ $(ls -lA | wc -c) -eq 0 ]
# or:
if [ $(ls -lA | head -c1 | wc -c) -eq 0 ]
และอื่น ๆ
สำหรับเอาต์พุตขนาดเล็กอาจไม่เป็นปัญหา แต่สำหรับเอาต์พุตขนาดใหญ่ความแตกต่างอาจมีความสำคัญ:
$ time [ -z "$(seq 1 10000000)" ]
real 0m2.703s
user 0m2.485s
sys 0m0.347s
เปรียบเทียบกับ:
$ time [ $(seq 1 10000000 | wc -c) -eq 0 ]
real 0m0.128s
user 0m0.081s
sys 0m0.105s
และยิ่งดีกว่า:
$ time [ $(seq 1 10000000 | head -c1 | wc -c) -eq 0 ]
real 0m0.004s
user 0m0.000s
sys 0m0.007s
อัปเดตตัวอย่างจากคำตอบโดย Will Vousden:
if [[ $(ls -A | wc -c) -ne 0 ]]; then
echo "there are files"
else
echo "no files found"
fi
อัปเดตอีกครั้งหลังจากคำแนะนำโดยnetj :
if [[ $(ls -A | head -c1 | wc -c) -ne 0 ]]; then
echo "there are files"
else
echo "no files found"
fi
อัปเดตเพิ่มเติมโดยjakeonfire :
grep
จะออกด้วยความล้มเหลวหากไม่มีการแข่งขัน เราสามารถใช้ประโยชน์จากสิ่งนี้เพื่อทำให้ไวยากรณ์ง่ายขึ้นเล็กน้อย:
if ls -A | head -c1 | grep -E '.'; then
echo "there are files"
fi
if ! ls -A | head -c1 | grep -E '.'; then
echo "no files found"
fi
หากคำสั่งที่คุณกำลังทดสอบสามารถส่งช่องว่างบางส่วนที่คุณต้องการที่จะถือว่าเป็นสตริงที่ว่างเปล่าแล้วแทนที่จะเป็น:
| wc -c
คุณสามารถใช้:
| tr -d ' \n\r\t ' | wc -c
หรือด้วยhead -c1
:
| tr -d ' \n\r\t ' | head -c1 | wc -c
หรืออะไรทำนองนั้น
ก่อนอื่นให้ใช้คำสั่งที่ใช้งานได้
ประการที่สองหลีกเลี่ยงการจัดเก็บใน RAM ที่ไม่จำเป็นและการประมวลผลข้อมูลขนาดใหญ่
คำตอบไม่ได้ระบุว่าเอาต์พุตมีขนาดเล็กเสมอดังนั้นจึงจำเป็นต้องพิจารณาถึงเอาต์พุตที่มีขนาดใหญ่เช่นกัน
[[ $(... | head -c | wc -c) -gt 0 ]]
หรือ[[ -n $(... | head -c1) ]]
ดีกว่าเพราะwc -c
จะต้องใช้เอาต์พุตทั้งหมดของคำสั่ง
if [ -z "$(ls -lA)" ]; then
echo "no files found"
else
echo "There are files"
fi
นี่จะรันคำสั่งและตรวจสอบว่าเอาต์พุตที่ส่งคืน (สตริง) มีความยาวเป็นศูนย์หรือไม่ คุณอาจต้องการตรวจสอบหน้า'ทดสอบ' ด้วยตนเองสำหรับธงอื่น ๆ
ใช้ "" รอบ ๆ อาร์กิวเมนต์ที่ตรวจสอบแล้วมิฉะนั้นผลลัพธ์ที่ว่างเปล่าจะส่งผลให้เกิดข้อผิดพลาดทางไวยากรณ์เนื่องจากไม่มีอาร์กิวเมนต์ที่สอง (ให้ตรวจสอบ) ที่ได้รับ!
หมายเหตุ: ที่ls -la
มักจะให้ผลตอบแทน.
และ..
ดังนั้นการใช้ที่จะไม่ทำงานให้ดูคำสั่ง ls หน้าคู่มือ นอกจากนี้ในขณะนี้อาจดูสะดวกและง่าย แต่ฉันคิดว่ามันจะแตกง่าย การเขียนสคริปต์ / แอปพลิเคชันขนาดเล็กที่ส่งคืนค่า 0 หรือ 1 ขึ้นอยู่กับผลลัพธ์นั้นมีความน่าเชื่อถือมากขึ้น!
$(
แทน backquote เพราะ$(
ทำรังได้ดีกว่า
สำหรับผู้ที่ต้องการวิธีแก้ปัญหาที่ไม่ขึ้นต่อกันของเวอร์ชันที่ใช้งานได้จริง (ในความเป็นจริงควรทำงานกับเชลล์สมัยใหม่อื่น ๆ ) และผู้ที่ชื่นชอบการใช้งานแบบตอร์ปิโดสำหรับงานที่รวดเร็ว ไปเลย!
ls | grep . && echo 'files found' || echo 'files not found'
(บันทึกเป็นหนึ่งในความคิดเห็นที่กล่าวถึงls -al
และในความเป็นจริงเพียง-l
และ-a
จะกลับบางสิ่งบางอย่างดังนั้นในคำตอบของฉันฉันใช้ง่ายls
grep -q ^
แทนอักขระขึ้นบรรทัดใหม่จะถูกจับคู่และ grep จะไม่พิมพ์อะไรไปยังเอาต์พุตมาตรฐาน นอกจากนี้มันจะออกทันทีที่ได้รับอินพุตใด ๆ แทนที่จะรอให้อินพุตสตรีมสิ้นสุด
ls -A
ถ้าคุณต้องการที่จะรวมจุดไฟล์ (หรือไดเรกทอรี)
-z string
True if the length of string is zero.
-n string
string
True if the length of string is non-zero.
คุณสามารถใช้ชวเลขรุ่น:
if [[ $(ls -A) ]]; then
echo "there are files"
else
echo "no files found"
fi
string
-n string
จอนหลินแสดงความคิดเห็นls -al
จะส่งออกเสมอ (สำหรับ.
และ..
) คุณต้องการls -Al
หลีกเลี่ยงไดเรกทอรีทั้งสองนี้
ตัวอย่างเช่นคุณสามารถวางผลลัพธ์ของคำสั่งลงในตัวแปรเชลล์:
v=$(ls -Al)
รูปแบบที่เก่ากว่าและไม่สามารถซ้อนได้คือ
v=`ls -Al`
แต่ฉันชอบสัญลักษณ์ที่ซ่อนอยู่$(
...)
คุณสามารถทดสอบว่าตัวแปรนั้นไม่ว่างเปล่าหรือไม่
if [ -n "$v" ]; then
echo there are files
else
echo no files
fi
และคุณสามารถรวมทั้งสองเป็น if [ -n "$(ls -Al)" ]; then
ฉันเดาว่าคุณต้องการผลลัพธ์ของls -al
คำสั่งดังนั้นในทุบตีคุณมีสิ่งที่ชอบ:
LS=`ls -la`
if [ -n "$LS" ]; then
echo "there are files"
else
echo "no files found"
fi
ls
ไม่เคยถูกดำเนินการ คุณตรวจสอบว่าการขยายตัวของตัวแปร LS
ไม่ว่างเปล่าหรือไม่
-a
สวิทช์จะไม่กลับไม่มีเนื้อหา (เช่นก็จะกลับเนื้อหาว่ามีไฟล์หรือไม่) เนื่องจากมีอยู่เสมอโดยปริยาย.
และ..
ไดเรกทอรีปัจจุบัน
นี่คือวิธีแก้ปัญหาสำหรับกรณีที่รุนแรงมากขึ้น:
if [ `command | head -c1 | wc -c` -gt 0 ]; then ...; fi
สิ่งนี้จะได้ผล
อย่างไรก็ตาม
คำตอบทั้งหมดที่ได้รับจนถึงการจัดการกับคำสั่งที่ยุติและส่งออกสตริงที่ไม่ว่างเปล่า
ส่วนใหญ่จะหักในความรู้สึกต่อไปนี้:
yes
)ดังนั้นเพื่อแก้ไขปัญหาเหล่านี้ทั้งหมดและตอบคำถามต่อไปนี้อย่างมีประสิทธิภาพ
ฉันจะทดสอบว่าคำสั่งแสดงผลสตริงว่างเปล่าได้อย่างไร?
คุณสามารถใช้ได้:
if read -n1 -d '' < <(command_here); then
echo "Command outputs something"
else
echo "Command doesn't output anything"
fi
นอกจากนี้คุณยังอาจเพิ่มหมดเวลาบางอย่างเพื่อที่จะทดสอบว่าคำสั่ง outputs สตริงไม่ว่างเปล่าภายในเวลาที่กำหนดโดยใช้read
's -t
ตัวเลือก เช่นการหมดเวลา 2.5 วินาที:
if read -t2.5 -n1 -d '' < <(command_here); then
echo "Command outputs something"
else
echo "Command doesn't output anything"
fi
สังเกต. หากคุณคิดว่าคุณต้องพิจารณาว่าคำสั่งแสดงผลสตริงที่ไม่ว่างเปล่าหรือไม่คุณอาจพบปัญหา XY มาก
seq 1 10000000
, seq 1 20
และprintf""
) การจับคู่เดียวที่วิธีอ่านนี้ไม่ชนะคือด้วย-z "$(command)"
วิธีการป้อนข้อมูลที่ว่างเปล่า ถึงอย่างนั้นมันก็สูญเสียประมาณ 10-15% เท่านั้น ดูเหมือนว่าพวกเขาจะทำลายแม้แต่seq 1 10
น้อย ไม่ว่า-z "$(command)"
วิธีการที่จะกลายเป็นดังนั้นช้าเป็นขนาดอินพุตเติบโตที่ฉันต้องการหลีกเลี่ยงการใช้มันสำหรับการป้อนข้อมูลใด ๆ ตัวแปรขนาด
ต่อไปนี้เป็นวิธีทางเลือกที่เขียน std-out และ std-err ของคำสั่งเป็นไฟล์ชั่วคราวจากนั้นตรวจสอบว่าไฟล์นั้นว่างเปล่าหรือไม่ ข้อดีของวิธีการนี้คือมันจับทั้งเอาต์พุตและไม่ใช้ sub-shells หรือไพพ์ ลักษณะหลังเหล่านี้มีความสำคัญเนื่องจากสามารถแทรกแซงการดักจับการออกจากการทุบตี bash (เช่นที่นี่ )
tmpfile=$(mktemp)
some-command &> "$tmpfile"
if [[ $? != 0 ]]; then
echo "Command failed"
elif [[ -s "$tmpfile" ]]; then
echo "Command generated output"
else
echo "Command has no output"
fi
rm -f "$tmpfile"
บางครั้งคุณต้องการบันทึกผลลัพธ์หากไม่ว่างเปล่าให้ส่งผ่านไปยังคำสั่งอื่น ถ้าเป็นเช่นนั้นคุณสามารถใช้สิ่งที่ชอบ
list=`grep -l "MY_DESIRED_STRING" *.log `
if [ $? -eq 0 ]
then
/bin/rm $list
fi
ด้วยวิธีนี้rm
คำสั่งจะไม่หยุดหากรายการว่างเปล่า