วิธีอ่านบรรทัดทั้งหมดในลูป 'สำหรับ' พร้อมช่องว่าง


128

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

for j in `cat ./file_wget_med`

do
echo $j

done

ผลหลังจากรัน:

Found.

นี่คือข้อมูลของฉัน:

$ cat file_wget_med
2013-09-11 14:27:03 ERROR 404: Not Found.

คำตอบ:


178

forวงแยกเมื่อเห็นช่องว่างใด ๆ เช่นพื้นที่แท็บหรือขึ้นบรรทัดใหม่ ดังนั้นคุณควรใช้IFS (Internal Field Separator) :

IFS=$'\n'       # make newlines the only separator
for j in $(cat ./file_wget_med)    
do
    echo "$j"
done
# Note: IFS needs to be reset to default!

5
และใส่ใจกับเครื่องหมายคำพูดเดี่ยว ๆ บน IFS เพราะ IFS = $ "\ n" จะแยกสตริงด้วย "nn" ด้วย ฉันพบว่าชุด IFS น่าเชื่อถือกว่าใช้ไวยากรณ์หรือฟังก์ชันที่ซับซ้อนมากขึ้น
erm3nda

23
เห็นได้ชัดว่ามีการแนะนำในunset IFSภายหลังดังนั้นคำสั่งอื่น ๆ จะไม่ได้รับผลกระทบในเชลล์เดียวกันนี้
Boris Däppen

2
อะไร$หมายถึงในIFS=$'\n'?
Ren

2
$ทำให้ตัวแบ่งบรรทัดทำงานภายในสตริง สิ่งนี้ควรทำด้วยlocal IFS=$'\n'ฟังก์ชั่นภายใน
Andy Ray

1
@ เรนน์ที่$'...'รู้จักกันในชื่อ " ANSI-C Quoting "
αғsнιη

82

forแยกลูปในช่องว่างใด ๆ (พื้นที่แท็บขึ้นบรรทัดใหม่) ตามค่าเริ่มต้น วิธีที่ง่ายที่สุดในการทำงานกับหนึ่งบรรทัดในแต่ละครั้งคือการใช้การwhile readวนซ้ำแทนซึ่งเป็นการแบ่งบรรทัดขึ้นบรรทัดใหม่:

while read i; do echo "$i"; done < ./file_wget_med

ฉันคาดว่าคำสั่งของคุณจะคายหนึ่งคำต่อบรรทัด (นั่นคือสิ่งที่เกิดขึ้นเมื่อฉันทดสอบด้วยไฟล์ของฉันเอง) หากมีสิ่งอื่นเกิดขึ้นฉันไม่แน่ใจว่าจะเกิดอะไรขึ้น


7
และนี่ก็เป็นทางเลือกที่ดีโดยท่อออกไปในขณะที่คำสั่ง:for i in `ls`; do echo $1; done ls|while read i; do echo $i; doneใช้งานได้กับชื่อไฟล์ / โฟลเดอร์ที่มีช่องว่างโดยไม่จำเป็นต้องเปลี่ยนตัวแปรสภาพแวดล้อม คำตอบที่ดีมาก
jishi

ในขณะที่ไม่ได้จัดการบรรทัดแรกและสุดท้ายมากไม่เช่นเดียวกับ for loop กับ IFS
grantbow

@ jishi เนื่องจากมันถูกออกแบบมาเพื่อแสดงผลเป็นหลักและตอบสนองต่อขนาดเทอร์มินัล - หน้าต่างและlsไม่ถือว่าปลอดภัยสำหรับใช้กับ|(ผู้ดำเนินการไพพ์) findมีความยืดหยุ่นมากกว่านอกจากจะปลอดภัยกว่าสำหรับวัตถุประสงค์นี้
SeldomNeedy

3
นี่เป็นคำตอบที่ยอดเยี่ยมฉันใช้เพื่อเปลี่ยนรายการที่ฉันมีให้เป็นรายการ PLIST สำหรับแอพ IOS ที่ฉันพัฒนาบน mac OSX ฉันแทนที่อินพุตไฟล์โดยการวางคลิปบอร์ดโดยใช้pbpaste ->pbpaste | while read t; do echo "<string>$t</string>"; done
Big Rich

3
ls -1อาจใช้กับ|การรับประกันผลลัพธ์ที่ยังไม่ฟอร์แมตหนึ่งบรรทัดต่อบรรทัด
AndreyS Scherbakov

19
#!/bin/bash
files=`find <subdir> -name '*'`
while read -r fname; do
    echo $fname
done <<< "$files"

ตรวจสอบแล้วว่าใช้งานได้ไม่ใช่สายการบินเดียวที่คุณอาจต้องการ แต่เป็นไปไม่ได้ที่จะทำอย่างสวยงาม


1
สตริงที่นี่! งดงามที่สุดของโซลูชั่นอื่น ๆ ทั้งหมด IMO
Eliran Malka

5

นี่คือส่วนขยายเล็กน้อยสำหรับคำตอบของ Mitchell Currie ที่ฉันชอบเพราะขอบเขตของผลข้างเคียงเล็ก ๆ ซึ่งหลีกเลี่ยงการตั้งค่าตัวแปร:

#!/bin/bash
while read -r fname; do
    echo $fname
done <<< "`find <subdir>`"

1
คุณสามารถลบได้-name '*'และมันจะเหมือนกันหรือไม่?
wjandrea

1

ฉันจะเขียนแบบนี้:

cat ./file_wget_med | while read -r j
do
    echo $j
done

เนื่องจากต้องการการเปลี่ยนแปลงน้อยที่สุดในสคริปต์ดั้งเดิม (ยกเว้นการแก้ปัญหาโดยใช้IFSแต่ปรับเปลี่ยนbashพฤติกรรมไม่เพียง แต่สำหรับคำสั่งการควบคุมแบบวนซ้ำนี้)


1
ท่อและcatไม่จำเป็นที่นี่ whileวนรอบยอมรับการเปลี่ยนเส้นทางได้ดีซึ่งเป็นสาเหตุที่มักจะเขียนเป็นwhile IFS= read -r line; do...done < input.txt
Sergiy Kolodyazhnyy

0

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

#!/bin/bash

mapfile -t < file.txt

for line in "${MAPFILE[@]}"; do
    echo $line
done

โปรดทราบว่าเมื่อใช้ท่อจะใส่ห่วง while ลงใน subshell การเปลี่ยนแปลงในขณะที่ลูปเช่นตัวแปรจะไม่แพร่กระจายไปยังส่วนนอกของสคริปต์

ตัวอย่าง:

#!/bin/bash

a=0
printf %s\\n {0..5} | while read; do
  ((a++))
done
echo $a # 'a' will always be 0.

(ทางออกที่ดีกว่า):

#!/bin/bash

b=0
while read; do
  ((b++))
done < <(printf %s\\n {0..5})

echo $b # 'b' equal to 6 (works as expected).

-1

Dandalf เข้าใกล้โซลูชันการใช้งานจริง แต่ไม่ควรพยายามกำหนดผลลัพธ์ของอินพุตที่ไม่รู้จัก (เช่นfind ~/.gdfuse -name '*') ให้กับตัวแปร! หรืออย่างน้อยก็พยายามทำสิ่งนั้นผ่านตัวแปรอาเรย์ ถ้าคุณยืนยันว่าขี้เกียจมาก! ดังนั้นนี่เป็นวิธีแก้ปัญหาของ Dandalf โดยปราศจากการซ้อมรบที่อันตราย และในทุกบรรทัด

while read -r fname; do
  echo $fname;
done <<< `find ~/.gdfuse -name '*'

สวัสดี @odoncaoa ฉันพยายามทำคำสั่ง find โดยไม่ใส่เครื่องหมายอัญประกาศ แต่มันทำให้ผลลัพธ์ทั้งหมดปรากฏเป็นหนึ่งบรรทัด ฉันสับสนเกี่ยวกับ "จำนวนที่ไม่รู้จักการป้อนข้อมูล" ด้วยคำสั่งนี้และอันตรายที่เกี่ยวข้อง คุณสามารถแสดงตัวอย่างของอันตรายและวิธีการที่พวกเขาสามารถเกิดขึ้นในทางทฤษฎี? ฉันไม่ต้องการที่จะขี้เกียจอย่างจริงจังฉันรู้สึกสะดวกสบายในการเขียนโปรแกรมภาษาอื่น ๆ
Dandalf
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.