ฉันจะใช้วิธีทุบตีถ้าทดสอบและค้นหาคำสั่งด้วยกันได้อย่างไร


25

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

ไฟล์บันทึกจะถูกจัดเก็บในรูปแบบนี้:

/var/log/crashes/app-2012-08-28.log
/var/log/crashes/otherapp-2012-08-28.log

ฉันต้องการให้คำสั่ง if ส่งคืนจริงถ้ามีบันทึกการทำงานล้มเหลวสำหรับแอปเฉพาะซึ่งได้รับการแก้ไขใน 5 นาทีที่ผ่าน findคำสั่งที่ผมจะใช้เป็น:

find /var/log/crashes -name app-\*\.log -mmin -5

ฉันไม่แน่ใจว่าจะรวมเข้ากับifคำสั่งได้อย่างถูกต้อง ฉันคิดว่านี่อาจใช้งานได้:

if [ test `find /var/log/crashes -name app-\*\.log -mmin -5` ] then
 service myapp restart
fi

มีบางพื้นที่ที่ฉันไม่ชัดเจน:

  • ฉันดูที่ธง ifแต่ฉันไม่แน่ใจว่าควรใช้อันไหนถ้ามี
  • ฉันต้องการtestคำสั่งหรือฉันควรประมวลผลผลลัพธ์ของคำสั่ง find โดยตรงหรืออาจใช้find... | wc -lเพื่อรับจำนวนบรรทัดแทน
  • ไม่จำเป็น 100% ที่จะตอบคำถามนี้ แต่testสำหรับการทดสอบกับโค้ดส่งคืนที่คำสั่งส่งคืน? และพวกมันมองไม่เห็น - ด้านนอกของstdout/ stderr? ฉันอ่านmanหน้านี้แล้ว แต่ฉันก็ยังไม่ค่อยชัดเจนเกี่ยวกับเวลาที่ใช้testและวิธีแก้ไขข้อบกพร่อง

คำตอบที่แท้จริงในการทั่วไปfind ... -execกรณีที่มีการใช้งาน ดูคำสั่งตัวอย่างภายใต้เหตุใดการวนซ้ำของแนวปฏิบัติของผลลัพธ์ของ find
Wildcard

@Wildcard - น่าเสียดายที่ไม่สามารถแก้ปัญหากรณีทั่วไป: มันไม่ทำงานหากมีการแข่งขันมากกว่าหนึ่งรายการและการดำเนินการจำเป็นต้องเรียกใช้เพียงครั้งเดียวเท่านั้นและจะไม่ทำงานหากคุณต้องการให้มีการดำเนินการเมื่อมี ไม่ตรงกัน. อดีตสามารถแก้ไขได้โดยการใช้... -exec command ';' -quitแต่ฉันไม่เชื่อว่าจะมีวิธีการแก้ปัญหาอื่น ๆ นอกเหนือจากการแยกผลลัพธ์ นอกจากนี้ในทั้งสองกรณีปัญหาหลักของการแยกวิเคราะห์ผลลัพธ์ของfind(เช่นไม่สามารถแยกตัวคั่นจากอักขระในชื่อไฟล์) ไม่ได้ใช้เนื่องจากคุณไม่จำเป็นต้องค้นหาตัวคั่นในกรณีเหล่านี้
จูลส์

คำตอบ:


27

[และtestเป็นคำพ้องความหมาย (ยกเว้น[ต้องการ]) ดังนั้นคุณไม่ต้องการใช้[ test:

[ -x /bin/cat ] && echo 'cat is executable'
test -x /bin/cat && echo 'cat is executable'

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

# echoes "command succeeded" because echo rarely fails
if /bin/echo hi; then echo 'command succeeded'; else echo 'command failed'; fi

# echoes "command failed" because rmdir requires an argument
if /bin/rmdir; then echo 'command succeeded'; else echo 'command failed'; fi

อย่างไรก็ตามตัวอย่างข้างต้นทั้งหมดทดสอบเฉพาะกับสถานะออกของโปรแกรมและละเว้นเอาต์พุตของโปรแกรม

สำหรับfindคุณจะต้องทดสอบว่ามีการสร้างผลลัพธ์ใด ๆ หรือไม่ -nทดสอบสตริงที่ไม่ว่างเปล่า:

if [[ -n $(find /var/log/crashes -name "app-*.log" -mmin -5) ]]
then
    service myapp restart
fi

รายการอาร์กิวเมนต์การทดสอบทั้งหมดพร้อมใช้งานโดยเรียกใช้help testที่bashcommandline

หากคุณกำลังใช้bash(และไม่sh) คุณสามารถใช้งาน[[ condition ]]ได้ซึ่งจะทำงานได้ดีกว่าเมื่อมีช่องว่างหรือกรณีพิเศษอื่น ๆ ในสภาพของคุณ [ condition ]มิฉะนั้นมันเป็นเรื่องปกติเหมือนกับการใช้ ฉันใช้[[ condition ]]ในตัวอย่างนี้อย่างที่ฉันทำเมื่อเป็นไปได้

ฉันก็เปลี่ยน`command`เป็น$(command)ซึ่งโดยทั่วไปก็ทำงานในทำนองเดียวกัน แต่ก็ดีกว่าด้วยคำสั่งซ้อน


echoสามารถล้มเหลว: echo 'oops' > /dev/fullลอง
Derobert

คำตอบนี้จะเต้นรอบ ๆ รากของปัญหา แต่หลีกเลี่ยงการกล่าวถึงสิ่งที่เป็น
bahamat

9

findจะออกสำเร็จหากไม่มีข้อผิดพลาดดังนั้นคุณจะไม่สามารถนับสถานะการออกเพื่อทราบว่าพบไฟล์ใด ๆ แต่อย่างที่คุณพูดคุณสามารถนับจำนวนไฟล์ที่พบและทดสอบจำนวนนั้น

มันจะเป็นอะไรเช่นนี้:

if [ $(find /var/log/crashes -name 'app-*.log' -mmin -5 | wc -l) -gt 0 ]; then
    ...
fi

test(aka [) ไม่ได้ตรวจสอบรหัสข้อผิดพลาดของคำสั่งนั้นมีไวยากรณ์พิเศษที่จะทำการทดสอบแล้วออกด้วยรหัสข้อผิดพลาด 0 หากการทดสอบประสบความสำเร็จหรือ 1 อย่างอื่น มันเป็นรหัสifที่ตรวจสอบรหัสข้อผิดพลาดของคำสั่งที่คุณส่งไปให้และประมวลผลเนื้อหาตามคำสั่งนั้น

ดูman test(หรือhelp testถ้าคุณใช้bash) และhelp if(เหมือนกัน)

ในกรณีนี้wc -lจะส่งออกจำนวน เราใช้testตัวเลือก-gtเพื่อทดสอบว่าจำนวนนั้นมากกว่า0หรือไม่ หากเป็นเช่นนั้นtest(หรือ[) 0จะกลับมาพร้อมกับรหัสทางออก ifจะตีความรหัสออกนั้นเป็นความสำเร็จและจะเรียกใช้รหัสภายในเนื้อหา


1

นี่จะเป็น

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)" ]; then

หรือ

if test -n "$(find /var/log/crashes -name app-\*\.log -mmin -5)"; then

คำสั่งtestและ[ … ]มีความหมายเหมือนกันทุกประการ ข้อแตกต่างเพียงอย่างเดียวคือชื่อของพวกเขาและความจริงที่[ต้องปิด]เป็นอาร์กิวเมนต์สุดท้าย เช่นเคยให้ใช้เครื่องหมายคำพูดคู่รอบการทดแทนคำสั่งมิฉะนั้นผลลัพธ์ของfindคำสั่งจะแตกออกเป็นคำและที่นี่คุณจะได้รับข้อผิดพลาดทางไวยากรณ์หากมีไฟล์ที่ตรงกันมากกว่าหนึ่งไฟล์ (และเมื่อไม่มีอาร์กิวเมนต์[ -n ]เป็นจริง ในขณะที่คุณต้องการ[ -n "" ]ซึ่งเป็นเท็จ)

ใน ksh, bash และ zsh แต่ไม่ใช่ใน Ash คุณสามารถใช้[[ … ]]ซึ่งมีกฎการแยกวิเคราะห์ที่แตกต่างกัน: [เป็นคำสั่งทั่วไปในขณะที่[[ … ]]โครงสร้างการแยกวิเคราะห์ที่แตกต่างกัน คุณไม่จำเป็นต้องมีเครื่องหมายคำพูดคู่ภายใน[[ … ]](แม้ว่าพวกเขาจะไม่เจ็บ) คุณยังต้อง;ใช้คำสั่ง after

if [[ -n $(find /var/log/crashes -name app-\*\.log -mmin -5) ]]; then

สิ่งนี้อาจไม่มีประสิทธิภาพ: หากมีไฟล์จำนวนมาก/var/log/crashesค้นหาจะสำรวจไฟล์ทั้งหมด คุณควรหยุดค้นหาทันทีที่พบการแข่งขันหรือหลังจากนั้นไม่นาน ด้วย GNU find (ไม่ฝัง Linux, Cygwin) ให้ใช้-quitprimary

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print -quit)" ]; then

กับระบบอื่น ๆ ท่อfindลงไปheadอย่างน้อยเลิกเร็ว ๆ นี้หลังจากที่นัดแรก (ค้นหาจะตายของท่อหัก)

if [ -n "$(find /var/log/crashes -name app-\*\.log -mmin -5 -print | head -n 1)" ]; then

(คุณสามารถใช้head -c 1หากheadคำสั่งของคุณรองรับ)


หรือใช้ zsh

crash_files=(/var/log/crashes/**/app-*.log(mm-5[1]))
if (($#crash_files)); then


0
find /var/log/crashes -name app-\*\.log -mmin -5 -exec service myapp restart ';' -quit

เป็นทางออกที่เหมาะสมที่นี่

-exec service myapp restart ';'ทำให้เกิดfindการเรียกใช้คำสั่งที่คุณต้องการเรียกใช้โดยตรงแทนที่จะต้องการเปลือกเพื่อตีความอะไร

-quitทำให้เกิดfindการออกหลังจากประมวลผลคำสั่งดังนั้นการป้องกันคำสั่งจะถูกดำเนินการอีกครั้งหากมีหลายไฟล์ที่ตรงกับเกณฑ์

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