วิธีการวนลูปมากกว่าบรรทัดของไฟล์?


61

บอกว่าฉันมีไฟล์นี้:

hello
world
hello world

โปรแกรมนี้

#!/bin/bash

for i in $(cat $1); do
    echo "tester: $i"
done

เอาท์พุท

tester: hello
tester: world
tester: hello
tester: world

ฉันต้องการให้มีการวนforซ้ำในแต่ละบรรทัดโดยไม่สนใจ whitespaces แม้ว่าเช่นควรจะแทนที่สองบรรทัดสุดท้าย

tester: hello world

การใช้เครื่องหมายคำพูดfor i in "$(cat $1)";ส่งผลiให้มีการกำหนดไฟล์ทั้งหมดในครั้งเดียว ฉันควรเปลี่ยนอะไร

คำตอบ:


69

ด้วยforและIFS :

#!/bin/bash

IFS=$'\n'       # make newlines the only separator
set -f          # disable globbing
for i in $(cat < "$1"); do
  echo "tester: $i"
done

อย่างไรก็ตามโปรดทราบว่ามันจะข้ามบรรทัดว่างเนื่องจากการขึ้นบรรทัดใหม่เป็นอักขระ IFS-white-space ลำดับของมันจะนับเป็น 1 และบรรทัดที่นำหน้าและต่อท้ายจะถูกละเว้น ด้วยzshและksh93(ไม่bash) คุณสามารถเปลี่ยนไปIFS=$'\n\n'สำหรับการขึ้นบรรทัดใหม่ไม่ได้ที่จะได้รับการปฏิบัติเป็นพิเศษ แต่ทราบว่าทั้งหมดต่อท้ายตัวอักษรขึ้นบรรทัดใหม่ (ดังนั้นที่มีต่อท้ายบรรทัดว่าง) จะถูกลบออกโดยการเปลี่ยนตัวคำสั่ง

หรือด้วยread (ไม่เกินcat):

#!/bin/bash

while IFS= read -r line; do
  echo "tester: $line"
done < "$1"

ที่นั่นบรรทัดว่างจะถูกสงวนไว้ แต่โปรดทราบว่ามันจะข้ามบรรทัดสุดท้ายหากไม่คั่นด้วยอักขระขึ้นบรรทัดใหม่อย่างถูกต้อง


5
ขอบคุณฉันไม่รู้ว่าจะมีใคร<เข้ามาวนวนได้เลย แม้ว่าตอนนี้ฉันจะเข้าใจได้อย่างสมบูรณ์แบบ แต่ตอนนี้ฉันเห็นแล้ว
Tobias Kienzler

1
ฉันเห็นIFS \ read -r line' in second example. Is really IFS = `จำเป็นหรือไม่ IMHO ก็พอที่จะพูดว่า:while read -r line; do echo "tester: $line"; done < "$1"
Grzegorz Wierzowiecki

4
@GrzegorzWierzowiecki IFS=ปิดช่องว่างของช่องว่างนำหน้าและต่อท้าย ดูที่ในwhile IFS= read..เหตุใด IFS จึงไม่มีผลกระทบ
Gilles

0

สำหรับสิ่งที่คุ้มค่าฉันต้องทำบ่อยและไม่สามารถจดจำวิธีการใช้ที่แน่นอนได้while IFS= read...ดังนั้นฉันจึงกำหนดฟังก์ชันต่อไปนี้ในโปรไฟล์ทุบตีของฉัน:

# iterate the line of a file and call input function
iterlines() {
    (( $# < 2 )) && { echo "Usage: iterlines <File> <Callback>"; return; }
    local File=$1
    local Func=$2
    n=$(cat "$File" | wc -l)
    for (( i=1; i<=n; i++ )); do
        "$Func" "$(sed "${i}q;d" "$File")"
    done
}

ฟังก์ชันนี้กำหนดจำนวนบรรทัดในไฟล์ก่อนจากนั้นใช้sedเพื่อแยกบรรทัดหลังจากบรรทัดและผ่านแต่ละบรรทัดเป็นอาร์กิวเมนต์สตริงเดียวไปยังฟังก์ชันใด ๆ ฉันคิดว่านี่อาจไม่มีประสิทธิภาพกับไฟล์ขนาดใหญ่ แต่นั่นก็ยังไม่เป็นปัญหาสำหรับฉัน (คำแนะนำเกี่ยวกับวิธีปรับปรุงการต้อนรับนี้แน่นอน)

การใช้งานค่อนข้างน่ารัก IMO:

>> cat example.txt # note the use of spaces, whitespace, etc.
a/path

This is a sentence.
"wi\th quotes"
$End
>> iterlines example.txt echo # preserves quotes, $ and whitespace
a/path

This is a sentence.
"wi\th quotes"
$End
>> x() { echo "$#"; }; iterlines example.txt x # line always passed as single input string
1
1 
1
1
1
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.