อ่านไฟล์ที่มุ่งเน้นบรรทัดซึ่งอาจไม่ลงท้ายด้วยการขึ้นบรรทัดใหม่


11

ฉันมีไฟล์ชื่อ/tmp/urlFileที่แต่ละบรรทัดแสดงถึง url ฉันพยายามอ่านจากไฟล์ดังต่อไปนี้:

cat "/tmp/urlFile" | while read url
do
    echo $url
done

หากบรรทัดสุดท้ายไม่ลงท้ายด้วยอักขระขึ้นบรรทัดใหม่บรรทัดนั้นจะไม่ถูกอ่าน ฉันสงสัยว่าทำไม

เป็นไปได้ไหมที่จะอ่านทุกบรรทัดไม่ว่าจะจบลงด้วยการขึ้นบรรทัดใหม่หรือไม่?



2
Hah @ Stéphaneฉันชอบ TBD ตรงนั้น ;-)
สตีเฟ่น Kitt

2
อีกวิธีในการเพิ่มบรรทัดใหม่ต่อท้ายหากไม่มีอยู่ awk 1 /tmp/urlFile.. ดังนั้นawk 1 /tmp/urlFile | while ...
muru

@muru นั่นเป็นคำตอบที่ดีกว่าที่นี่
ไวด์การ์ด

1
เนื่องจากคุณถามว่าเพราะเหตุใดจึงไม่อ่าน: stackoverflow.com/a/729795/1968
Konrad Rudolph

คำตอบ:


13

คุณต้องการ:

while IFS= read -r url || [ -n "$url" ]; do
  printf '%s\n' "$url"
done < url.list

(อย่างมีประสิทธิภาพวงนั้นจะเพิ่มบรรทัดใหม่ที่หายไปในบรรทัดสุดท้าย (ไม่ใช่ -)

ดูสิ่งนี้ด้วย:


ขอบคุณ ฉันอ่านบทความที่เชื่อมโยงและฉันอาจพลาดบางสิ่งทำไม "ลูปนั้นเพิ่มบรรทัดใหม่ที่หายไปในบรรทัดสุดท้าย (ไม่ใช่ -)"
ทิม

1
@ Tim อะไร Stephane ดูเหมือนว่าจะหมายความว่ามันจะเพิ่มกลับขึ้นบรรทัดใหม่ที่ขาดหายไปในการส่งออกเนื่องจากทุกสายที่นี่มีprintf \n
Sergiy Kolodyazhnyy

6

ดูเหมือนว่าจะแก้ไขได้ด้วยreadarray -t:

readarray -t urls "/tmp/urlFile"
for url in "${urls[@]}"; do
    printf '%s\n' "$url"
done

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


ขอบคุณ ส่วนไหนที่แก้ได้และไม่ได้ส่วนไหน
ทิม

มันแก้ปัญหาโดยไม่ขึ้นบรรทัดใหม่ แต่แนะนำปัญหาใหม่ที่อาจเกิดขึ้นกับไฟล์ที่มีขนาดใหญ่มากเพราะมันจะอ่านไฟล์ในอาเรย์ก่อนซึ่งจะต้องมีการทำซ้ำผ่าน
DopeGhoti

1
@DopeGhoti นั่นเป็นข้อมูลที่ดี - ฉันขอแนะนำให้คุณเพิ่มลงในคำตอบได้โดยตรงหรือไม่
RJHunter

ท่าคำตอบได้รับการแก้ไขแล้ว
DopeGhoti

5

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

readในตัวมีความหมายเพียงการอ่านไฟล์ข้อความ คุณไม่ได้ส่งไฟล์ข้อความดังนั้นคุณจึงไม่สามารถหวังให้ไฟล์ทำงานได้อย่างราบรื่น เชลล์อ่านทุกบรรทัด - สิ่งที่ข้ามไปคืออักขระพิเศษหลังจากบรรทัดสุดท้าย

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

{ cat "/tmp/urlFile"; echo; } | 

ไฟล์ที่ควรเป็นไฟล์ข้อความ แต่หายไปขึ้นบรรทัดใหม่มักจะถูกสร้างขึ้นโดยบรรณาธิการของ Windows สิ่งนี้มักจะรวมกับการสิ้นสุดของบรรทัด Windows ซึ่งก็คือ CR LF ซึ่งตรงข้ามกับ LF ของ Unix อักขระ CR มีประโยชน์น้อยมากในทุกที่และไม่สามารถปรากฏใน URL ได้ในทุกกรณีดังนั้นคุณควรลบออก

{ <"/tmp/urlFile" tr -d '\r'; echo; } | 

ในกรณีที่ไฟล์อินพุตมีรูปแบบที่ถูกต้องและลงท้ายด้วยบรรทัดใหม่การechoเพิ่มบรรทัดว่างพิเศษ เนื่องจาก URL ต้องไม่ว่างเปล่าเพียงแค่ละเว้นบรรทัดว่าง

โปรดทราบว่าreadจะไม่อ่านบรรทัดอย่างตรงไปตรงมา มันละเว้นช่องว่างนำหน้าและต่อท้ายซึ่งสำหรับ URL อาจเป็นที่ต้องการ มันจะใช้เครื่องหมายแบ็กสแลชที่ส่วนท้ายของบรรทัดเป็นอักขระยกเว้นทำให้บรรทัดถัดไปถูกรวมเข้ากับลำดับแรกลบด้วยเครื่องหมายแบ็กสแลช - นิวไลน์ซึ่งไม่ต้องการอย่างแน่นอน ดังนั้นคุณควรผ่านการเลือกที่จะ-r readมันเป็นอย่างมากที่หายากมากสำหรับที่จะเป็นสิ่งที่ถูกต้องมากกว่าreadread -r

{ <"/tmp/urlFile" tr -d '\r'; echo; } | while read -r url
do
  if [ -z "$url" ]; then continue; fi
  
done

3

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

#!/bin/sh
while IFS= read -r line || [ "$line" ]; do 
    echo "line: $line"
done

$ printf 'foo\nbar' | sh ./read.sh 
line: foo
line: bar
$ printf 'foo\nbar\n' | sh ./read.sh 
line: foo
line: bar

1

วิธีอื่นจะเป็นเช่นนี้:

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

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

DONE=false
until $DONE ;do
read || DONE=true
echo $REPLY 
done < /tmp/urlFile

อ้างอิงจากที่นี่


1
cat "/ tmp / urlFile" | ในขณะที่อ่าน URL
ทำ
    echo $ url
เสร็จแล้ว

นี่คือการใช้ประโยชน์ของcat

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

sed -e '$ a \' "/ tmp / urlFile" | ในขณะที่อ่าน -r url
ทำ
    พิมพ์ "% s \ n" "$ {url}"
เสร็จแล้ว

อ่านเพิ่มเติม


1
ลักษณะการทำงานของ sed ไม่ได้ระบุโดย POSIX เมื่ออินพุตไม่สิ้นสุดในอักขระบรรทัดใหม่ นอกจากนี้เมื่อมีบรรทัดที่มีขนาดใหญ่กว่า LINE_MAX ในขณะที่พฤติกรรมของreadถูกระบุในกรณีเหล่านั้น
Stéphane Chazelas
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.