ฉันจะวนกลับอย่างถูกต้องได้อย่างไรfor
?
for f in /var/logs/foo*.log; do
bar "$f"
done
ฉันต้องการวิธีแก้ปัญหาที่ไม่ทำลายอักขระขี้ขลาดในชื่อไฟล์
ฉันจะวนกลับอย่างถูกต้องได้อย่างไรfor
?
for f in /var/logs/foo*.log; do
bar "$f"
done
ฉันต้องการวิธีแก้ปัญหาที่ไม่ทำลายอักขระขี้ขลาดในชื่อไฟล์
คำตอบ:
ใน bash หรือ ksh ให้ใส่ชื่อไฟล์ในอาร์เรย์และวนซ้ำแถวนั้นในลำดับที่กลับกัน
files=(/var/logs/foo*.log)
for ((i=${#files[@]}-1; i>=0; i--)); do
bar "${files[$i]}"
done
รหัสด้านบนยังใช้งานได้ใน zsh หากksh_arrays
ตั้งค่าตัวเลือก (อยู่ในโหมดจำลอง ksh) มีวิธีที่ง่ายกว่าใน zsh ซึ่งเป็นการกลับลำดับของการแข่งขันผ่านโปรแกรมคัดเลือกรอบตัว:
for f in /var/logs/foo*.log(On); do bar $f; done
POSIX ไม่รวมอาร์เรย์ดังนั้นหากคุณต้องการพกพาตัวเลือกเดียวของคุณในการจัดเก็บอาร์เรย์ของสตริงโดยตรงคือพารามิเตอร์ตำแหน่ง
set -- /var/logs/foo*.log
i=$#
while [ $i -gt 0 ]; do
eval "f=\${$i}"
bar "$f"
i=$((i-1))
done
i
และf
; เพียงแค่ดำเนินการshift
ใช้$1
สำหรับการเรียกร้องให้bar
และการทดสอบในของคุณ[ -z $1 ]
while
[ -z $1 ]
มิได้[ -z "$1" ]
(สิ่งที่ถ้าพารามิเตอร์ได้มีการทดสอบที่มีประโยชน์*
? หรือสตริงว่างเปล่า) และในกรณีใด ๆ พวกเขาไม่ได้ช่วยที่นี่: คำถามคือวิธีการวนซ้ำในลำดับที่ตรงกันข้าม
ลองใช้วิธีนี้ยกเว้นว่าคุณคิดว่าตัวแบ่งบรรทัดเป็น "ตัวอักษรที่น่ากลัว"
ls /var/logs/foo*.log | tac | while read f; do
bar "$f"
done
f
สถานการณ์แตกต่างกันในสถานการณ์ของฉัน คำตอบนี้ทำหน้าที่แทนการดรอปอินสำหรับfor
บรรทัดธรรมดา
หากใครก็ตามพยายามหาวิธีย้อนกลับซ้ำผ่านรายการสตริงที่คั่นด้วยช่องว่างการทำงาน:
reverse() {
tac <(echo "$@" | tr ' ' '\n') | tr '\n' ' '
}
list="a bb ccc"
for i in `reverse $list`; do
echo "$i"
done
> ccc
> bb
> a
tac
เป็นส่วนหนึ่งของ coreutils ของ GNU แต่ทางออกของคุณก็เป็นสิ่งที่ดีเช่นกัน
tac
แม้มี busybox แม้ว่าจากจำนวนการtac
ตอบกลับที่ไม่มีที่นี่ฉันคิดว่า OSX อาจไม่มีแทค ในกรณีนี้ให้ใช้ตัวแปรของโซลูชันของ @ arp ซึ่งจะเข้าใจได้ง่ายขึ้น
find /var/logs/ -name 'foo*.log' -print0 | tail -r | xargs -0 bar
ควรดำเนินการตามที่คุณต้องการ (นี่คือการทดสอบใน Mac OS X และฉันมีข้อแม้ด้านล่าง ... )
จาก man page เพื่อค้นหา:
-print0
This primary always evaluates to true. It prints the pathname of the current file to standard output, followed by an ASCII NUL character (charac-
ter code 0).
โดยพื้นฐานแล้วคุณกำลังค้นหาไฟล์ที่ตรงกับสตริง + glob ของคุณและยกเลิกแต่ละไฟล์ด้วยอักขระ NUL หากชื่อไฟล์ของคุณมีการขึ้นบรรทัดใหม่หรือตัวละครแปลก ๆ อื่น ๆ การค้นหาควรจัดการเรื่องนี้ให้ดี
tail -r
ใช้เวลาเข้ามาตรฐานผ่านท่อและฝืนมัน (หมายเหตุที่tail -r
พิมพ์ทั้งหมดของท่านที่ stdout และไม่เพียง แต่ที่ผ่านมา 10 เส้นซึ่งเป็นค่าเริ่มต้นมาตรฐาน. man tail
สำหรับข้อมูลเพิ่มเติม)
จากนั้นเราจะนำไปที่xargs -0
:
-0 Change xargs to expect NUL (``\0'') characters as separators, instead of spaces and newlines. This is expected to be used in concert with the
-print0 function in find(1).
นี่ xargs คาดว่าจะเห็นการขัดแย้งแยกจากกันโดยตัวละคร NUL ที่คุณส่งผ่านจากและตรงกันข้ามกับfind
tail
ข้อแม้ของฉัน: ฉันได้อ่านที่tail
ไม่ได้เล่นได้ดีกับสตริงโมฆะเป็นสิ้นสุด สิ่งนี้ทำงานได้ดีบน Mac OS X แต่ฉันไม่สามารถรับประกันได้ว่าเป็นกรณีสำหรับ * ทุกคน เหยียบอย่างระมัดระวัง
ฉันควรพูดถึงว่าGNU Parallelมักถูกใช้เป็นxargs
ทางเลือก คุณสามารถตรวจสอบได้เช่นกัน
ฉันอาจจะหายไปบางสิ่งบางอย่างดังนั้นคนอื่นควรพูดสอด
tail -r
แต่ฉันทำผิดหรือเปล่า?
tac
เป็นทางเลือกดังนั้นฉันจะพยายามที่แทน
tail -r
มีความเฉพาะเจาะจงกับ OSX และย้อนกลับอินพุตที่คั่นด้วยบรรทัดใหม่ไม่ใช่อินพุตที่คั่นด้วย null โซลูชันที่สองของคุณใช้งานไม่ได้เลย (คุณป้อนข้อมูลไปls
ที่ซึ่งไม่สนใจ) ไม่มีการแก้ไขที่ง่ายที่จะทำให้มันทำงานได้อย่างน่าเชื่อถือ
ในตัวอย่างของคุณคุณวนลูปมากกว่าหลายไฟล์ แต่ฉันพบคำถามนี้เพราะมีชื่อทั่วไปมากกว่าซึ่งอาจครอบคลุมการวนลูปมากกว่าอาร์เรย์หรือการย้อนกลับตามคำสั่งซื้อจำนวนเท่าใดก็ได้
นี่คือวิธีการทำใน Zsh:
หากคุณวนลูปมากกว่าองค์ประกอบในอาร์เรย์ใช้ไวยากรณ์นี้ (ที่มา )
for f in ${(Oa)your_array}; do
...
done
O
กลับคำสั่งที่ระบุในการตั้งค่าสถานะถัดไป; a
เป็นคำสั่งอาร์เรย์ปกติ
ในฐานะที่เป็น @Gilles กล่าวว่าOn
จะกลับคำสั่งไฟล์ globbed my/file/glob/*(On)
ของคุณเช่นกับ นั่นเป็นเพราะOn
"ย้อนกลับลำดับชื่อ "
ธงเรียง Zsh:
a
ลำดับของอาร์เรย์L
ความยาวไฟล์l
จำนวนลิงก์m
วันที่แก้ไขn
ชื่อ^o
ลำดับย้อนกลับ ( o
เป็นคำสั่งปกติ)O
กลับคำสั่งสำหรับตัวอย่างดูhttps://github.com/grml/zsh-lovers/blob/master/zsh-lovers.1.txtและhttp://reasoniamhere.com/2014/01/11/outrageously-useful-tips- การต้นแบบของคุณ-z-เปลือก /
Mac OSX ไม่รองรับtac
คำสั่ง โซลูชันโดย @tcdyl ทำงานเมื่อคุณเรียกคำสั่งเดียวในการfor
วนซ้ำ สำหรับกรณีอื่น ๆ ทั้งหมดต่อไปนี้เป็นวิธีที่ง่ายที่สุดในการหลีกเลี่ยง
วิธีนี้ไม่สนับสนุนการมีบรรทัดใหม่ในชื่อไฟล์ของคุณ เหตุผลพื้นฐานคือการtail -r
เรียงลำดับการป้อนข้อมูลของมันเป็นตัวคั่นด้วยการขึ้นบรรทัดใหม่
for i in `ls -1 [filename pattern] | tail -r`; do [commands here]; done
อย่างไรก็ตามมีวิธีหลีกเลี่ยงข้อ จำกัด ของการขึ้นบรรทัดใหม่ หากคุณรู้ว่าชื่อไฟล์ของคุณไม่มีอักขระบางตัว (พูด, '=') คุณสามารถใช้tr
เพื่อแทนที่บรรทัดใหม่ทั้งหมดเพื่อให้กลายเป็นตัวละครนี้แล้วทำการเรียงลำดับ ผลลัพธ์จะมีลักษณะดังนี้:
for i in `find [directory] -name '[filename]' -print0 | tr '\n' '=' | tr '\0' '\n'
| tail -r | tr '\n' '\0' | tr '=' '\n' | xargs -0`; do [commands]; done
หมายเหตุ: ขึ้นอยู่กับเวอร์ชันของคุณtr
ซึ่งอาจไม่รองรับ'\0'
เป็นอักขระ โดยปกติจะสามารถแก้ไขได้โดยเปลี่ยนโลแคลเป็น C (แต่ฉันจำไม่ได้เลยว่าหลังจากแก้ไขไปเมื่อมันทำงานบนคอมพิวเตอร์ของฉันแล้ว) หากคุณได้รับข้อความแสดงข้อผิดพลาดและคุณไม่สามารถหาวิธีแก้ปัญหาได้โปรดโพสต์เป็นความคิดเห็นดังนั้นฉันสามารถช่วยคุณแก้ปัญหาได้
วิธีที่ง่ายคือการใช้ls -r
ตามที่ระบุไว้โดย @David Schwartz
for f in $(ls -r /var/logs/foo*.log); do
bar "$f"
done
ลองสิ่งนี้:
for f in /var/logs/foo*.log; do
bar "$f"
done
ฉันคิดว่ามันเป็นวิธีที่ง่ายที่สุด
for
วนในลำดับย้อนกลับ"
sort -r
ก่อนหรือฟอกผ่านfor
ls -r