ฉันจะย้อนกลับสำหรับวงได้อย่างไร


26

ฉันจะวนกลับอย่างถูกต้องได้อย่างไรfor?

for f in /var/logs/foo*.log; do
    bar "$f"
done

ฉันต้องการวิธีแก้ปัญหาที่ไม่ทำลายอักขระขี้ขลาดในชื่อไฟล์


5
ท่อเพียงsort -rก่อนหรือฟอกผ่านfor ls -r
David Schwartz

คำตอบ:


27

ใน 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
user1404316

@ user1404316 นี้จะเป็นวิธีที่ซับซ้อนมากเกินไปของ iterating กว่าองค์ประกอบในการเรียงลำดับ นอกจากนี้ไม่ถูกต้อง: ค่า[ -z $1 ]มิได้[ -z "$1" ](สิ่งที่ถ้าพารามิเตอร์ได้มีการทดสอบที่มีประโยชน์*? หรือสตริงว่างเปล่า) และในกรณีใด ๆ พวกเขาไม่ได้ช่วยที่นี่: คำถามคือวิธีการวนซ้ำในลำดับที่ตรงกันข้าม
Gilles 'หยุดชั่วร้าย'

คำถามนี้บอกว่ามันวนซ้ำชื่อไฟล์ใน / var / log ดังนั้นจึงปลอดภัยที่จะสมมติว่าไม่มีพารามิเตอร์ใดที่จะเป็น "*" หรือว่างเปล่า ฉันยืนแก้ไขในจุดลำดับย้อนกลับ
user1404316

7

ลองใช้วิธีนี้ยกเว้นว่าคุณคิดว่าตัวแบ่งบรรทัดเป็น "ตัวอักษรที่น่ากลัว"

ls /var/logs/foo*.log | tac | while read f; do
    bar "$f"
done

มีวิธีการที่ใช้กับการขึ้นบรรทัดใหม่ได้หรือไม่? (ฉันรู้ว่ามันเป็นของหายาก แต่อย่างน้อยก็เพื่อการเรียนรู้ฉันอยากรู้ว่ามันเป็นไปได้ที่จะเขียนโค้ดที่ถูกต้องหรือไม่)
Mehrdad

ความคิดสร้างสรรค์ที่จะใช้แทคเพื่อย้อนกลับการไหลและหากคุณต้องการกำจัดตัวละครที่ไม่ต้องการเช่นตัวแบ่งบรรทัดคุณสามารถไปที่ tr -d '\ n'
Johan

4
สิ่งนี้จะแตกถ้าชื่อไฟล์มีการขึ้นบรรทัดใหม่แบ็กสแลชหรืออักขระที่ไม่สามารถพิมพ์ได้ ดูmywiki.wooledge.org/ParsingLsและวิธีการวนรอบบรรทัดของไฟล์หรือไม่ (และกระทู้ที่เชื่อมโยง)
Gilles 'หยุดชั่วร้าย'

คำตอบที่โหวตมากที่สุดทำให้fสถานการณ์แตกต่างกันในสถานการณ์ของฉัน คำตอบนี้ทำหน้าที่แทนการดรอปอินสำหรับforบรรทัดธรรมดา
Serge Stroobandt

2

หากใครก็ตามพยายามหาวิธีย้อนกลับซ้ำผ่านรายการสตริงที่คั่นด้วยช่องว่างการทำงาน:

reverse() {
  tac <(echo "$@" | tr ' ' '\n') | tr '\n' ' '
}

list="a bb ccc"

for i in `reverse $list`; do
  echo "$i"
done
> ccc
> bb 
> a

2
ฉันไม่มีแทคในระบบของฉัน; ฉันไม่รู้ว่ามันแข็งแกร่งหรือไม่ แต่ฉันใช้ x เป็น $ {mylist}; ทำ revv = "$ {x} $ {revv}"; เสร็จสิ้น
arp

@arp มันน่าตกใจเหมือนtacเป็นส่วนหนึ่งของ coreutils ของ GNU แต่ทางออกของคุณก็เป็นสิ่งที่ดีเช่นกัน
ACK_stoverflow

@ACK_stoverflow มีหลายระบบที่ไม่มีเครื่องมือผู้ใช้ Linux
Kusalananda

@Kusalananda คุณกำลังบอกว่ามีเพียง Linux เท่านั้นที่ใช้ coreutils ของ GNU? ฮ่า ๆ. tacแม้มี busybox แม้ว่าจากจำนวนการtacตอบกลับที่ไม่มีที่นี่ฉันคิดว่า OSX อาจไม่มีแทค ในกรณีนี้ให้ใช้ตัวแปรของโซลูชันของ @ arp ซึ่งจะเข้าใจได้ง่ายขึ้น
ACK_stoverflow

@ACK_stoverflow นั่นคือสิ่งที่ฉันพูดใช่
Kusalananda

1
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 ที่คุณส่งผ่านจากและตรงกันข้ามกับfindtail

ข้อแม้ของฉัน: ฉันได้อ่านที่tailไม่ได้เล่นได้ดีกับสตริงโมฆะเป็นสิ้นสุด สิ่งนี้ทำงานได้ดีบน Mac OS X แต่ฉันไม่สามารถรับประกันได้ว่าเป็นกรณีสำหรับ * ทุกคน เหยียบอย่างระมัดระวัง

ฉันควรพูดถึงว่าGNU Parallelมักถูกใช้เป็นxargsทางเลือก คุณสามารถตรวจสอบได้เช่นกัน

ฉันอาจจะหายไปบางสิ่งบางอย่างดังนั้นคนอื่นควรพูดสอด


2
+1 คำตอบที่ดี ดูเหมือนว่า Ubuntu จะไม่รองรับtail -rแต่ฉันทำผิดหรือเปล่า?
Mehrdad

1
ไม่ฉันไม่คิดว่าคุณเป็น ฉันไม่มีเครื่อง linux ของฉัน แต่ google สำหรับ 'linux tail man' ไม่แสดงเป็นตัวเลือก sunaku กล่าวถึงtacเป็นทางเลือกดังนั้นฉันจะพยายามที่แทน
tcdyl

ฉันได้แก้ไขคำตอบเพื่อรวมทางเลือกอื่น
tcdyl

ขออภัยฉันลืมที่จะ +1 เมื่อฉันกล่าวว่า 1 :( เสร็จสิ้นขออภัยเกี่ยวกับที่ฮ่าฮ่า! :)
Mehrdad

4
tail -rมีความเฉพาะเจาะจงกับ OSX และย้อนกลับอินพุตที่คั่นด้วยบรรทัดใหม่ไม่ใช่อินพุตที่คั่นด้วย null โซลูชันที่สองของคุณใช้งานไม่ได้เลย (คุณป้อนข้อมูลไปlsที่ซึ่งไม่สนใจ) ไม่มีการแก้ไขที่ง่ายที่จะทำให้มันทำงานได้อย่างน่าเชื่อถือ
Gilles 'หยุดความชั่วร้าย'

1

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

นี่คือวิธีการทำใน 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-เปลือก /


0

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 (แต่ฉันจำไม่ได้เลยว่าหลังจากแก้ไขไปเมื่อมันทำงานบนคอมพิวเตอร์ของฉันแล้ว) หากคุณได้รับข้อความแสดงข้อผิดพลาดและคุณไม่สามารถหาวิธีแก้ปัญหาได้โปรดโพสต์เป็นความคิดเห็นดังนั้นฉันสามารถช่วยคุณแก้ปัญหาได้


0

วิธีที่ง่ายคือการใช้ls -rตามที่ระบุไว้โดย @David Schwartz

for f in $(ls -r /var/logs/foo*.log); do
    bar "$f"
done

-4

ลองสิ่งนี้:

for f in /var/logs/foo*.log; do
bar "$f"
done

ฉันคิดว่ามันเป็นวิธีที่ง่ายที่สุด


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