Jak smyčka přes řádky souboru?

Řekněme, že mám tento soubor:

hello world hello world 

Tento program

#!/bin/bash for i in $(cat $1); do echo "tester: $i" done 

výstupy

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

Chtěl bych mít for iteruje na každém řádku samostatně, ignoruje však mezery, tj. poslední dva řádky by měly být nahrazeny

tester: hello world 

Používáním uvozovek for i in "$(cat $1)"; má za následek i přiřazení celého souboru najednou. Co bych měl změnit?

Odpovědět

(o 9 let později 🙂
Obě poskytnuté odpovědi by selhaly na souborech bez nového řádku na konci, tím se efektivně přeskočí poslední řádek, neprodukují se žádné chyby, povede to ke katastrofě (naučil způsobem :).

Nejlepší výstižné řešení, které jsem zatím našel, že „Just Works“ (v bash i sh):

while IFS="" read -r LINE || [ -n "${LINE}" ]; do echo "processing line: ${LINE}" done < /path/to/input/file.txt 

Podrobnější diskusi najdete v této diskusi StackOverflow: Jak používat „while read „(Bash) pro čtení posledního řádku v souboru, pokud na konci souboru není nový řádek?

Pozor: tento přístup přidá do posledního řádku další nový řádek, pokud existuje zatím žádné.

Komentáře

  • Pěkný úlovek, děkuji! Všimněte si " Nezapomeňte, že na EOF přidáte nový nový řádek, pokud již žádný neexistuje. " komentář ačkoli
  • Tobiasi, ' přidám to jako poznámku, děkuji.

Odpověď

S for a IFS :

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

Upozorňujeme však, že přeskočí prázdné řádky, protože nový řádek je znak IFS-white-space, sekvence z toho se počítá jako 1 a přední a koncové jsou ignorovány. Pomocí zsh a ksh93 (ne bash) jej můžete změnit na IFS=$"\n\n" aby nový řádek nebyl zpracován speciálně, mějte však na paměti, že všechny koncové znaky nového řádku (takže zahrnují koncové prázdné řádky) budou vždy odstraněny nahrazením příkazu.

Nebo s read (již cat ):

#!/bin/bash while IFS= read -r line; do echo "tester: $line" done < "$1" 

Tam zůstanou zachovány prázdné řádky, ale mějte na paměti, že přeskočí poslední řádek, pokud nebyl řádně ohraničen znakem nového řádku.

Komentáře

  • díky, nevěděl jsem ', že by člověk nemohl < do celé smyčky. I když to teď dává naprosto smysl, viděl jsem to
  • vidím IFS \ read -r line' in second example. Is really IFS = `potřebný? IMHO stačí říct: while read -r line; do echo "tester: $line"; done < "$1"
  • @GrzegorzWierzowiecki IFS= vypíná odstraňování úvodních a koncových mezer. Viz Proč nemá systém IFS v while IFS= read.. žádný účinek?
  • @BenMares Chcete-li zabránit globování výrazy, které se případně objevují v textu, který čteme, se rozšíří na shodné názvy souborů. Zkuste například printf '%s\n' '*o' 'bar' >afile; touch foo; IFS=$'\n'; for i in $(cat afile); do echo "$i"; done.
  • A while IFS= read -r line || [ "$line" ]; do zpracuje koncový řádek, který není správně ohraničen znakem nového řádku (ale bude přidán zpět).

Odpověď

Aby to stálo za to, musím udělat to docela často a nikdy si nepamatuji přesný způsob použití while IFS= read..., proto jsem ve svém bash profilu definoval následující funkci:

# 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 } 

Tato funkce nejprve určí počet řádků v souboru, poté pomocí sed extrahuje řádek po řádku a předá každý řádek jako jeden řetězcový argument libovolnému danému funkce. Předpokládám, že by to u velkých souborů mohlo být opravdu neúčinné, ale to pro mě dosud nebyl problém (samozřejmě návrhy, jak toto uvítání vylepšit).

Využití je docela sladké 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 

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *