Boşluk içeren 'for' döngüsünde tam satır nasıl okunur


128

forDosya için bir döngü çalıştırmaya çalışıyorum ve tüm satırı görüntülemek istiyorum. Ancak bunun yerine sadece son kelimeyi gösteriliyor. Bütün çizgiyi istiyorum.

for j in `cat ./file_wget_med`

do
echo $j

done

çalıştırdıktan sonra sonuç:

Found.

İşte verilerim:

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

Yanıtlar:


178

forloop, boşluk, sekme veya yeni satır gibi herhangi bir boşluk gördüğünde ayrılır. Bu nedenle, IFS'yi (Dahili Alan Ayırıcı) kullanmalısınız :

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
Ve IFS = '' nn '' de "nn" dizgilerini de ayıracağınız için IFS'deki tek tırnaklara dikkat edin. IFS setini daha karmaşık sözdizimi veya fonksiyonlardan daha güvenilir buldum.
erm3nda

23
görünüşe göre unset IFSdaha sonra yapılması tavsiye edilir , böylece diğer komutlar bu aynı kabukta etkilenmez.
Boris Däppen

2
Bunun $anlamı ne IFS=$'\n'?
Ren

2
$Satır sonlarını dizenin içinde çalışmasını sağlar. Bu aynı zamanda local IFS=$'\n'bir fonksiyon içinde yapılmalıdır .
Andy Ray,

1
O oluyor @Renn $'...'"bilinen ANSI-C aktaran "
αғsнιη

82

forvarsayılan olarak herhangi bir boşlukta (boşluk, sekme, yeni satır) bölünmüş döngüler; Bir seferde bir satırda çalışmanın en kolay yolu, while readbunun yerine yeni satırlara bölünen bir döngü kullanmaktır:

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

Ben ediyorum bekliyoruz Komut satır başına bir kelime (yani kendime ait bir dosya ile dışarı test edildiğinde böyle oldu) tükürmeliyim. Başka bir şey oluyorsa, neye sebep olacağından emin değilim.


7
Bu aynı zamanda iyi bir alternatif olduğunu for i in `ls`; do echo $1; done, süre komutuna çıkış boru ile: ls|while read i; do echo $i; done. Bu, ortam değişkenlerini değiştirmeye gerek kalmadan boşluk içeren dosya / klasör adlarında çalışır. Çok iyi cevap.
jishi

Süre, ilk ve son satırları çok iyi idare etmedi, IFS ile olan for döngüsünün yanı sıra
15

@jishi Öncelikle ekran için tasarlandığından ve terminal penceresinin boyutuna duyarlı olduğundan ve bu nedenle, (boru operatörü) lsile kullanım için güvenli olarak kabul edilmez |. findbu amaç için daha güvenli olmaya ek olarak daha esnektir.
Nadiren Needy

3
Bu BÜYÜK bir cevap, Mac OSX'te geliştirdiğim bir IOS uygulaması için sahip olduğum bir listeyi PLIST girişlerine dönüştürmek için kullandım. Dosya girişini, pbpaste ->pbpaste | while read t; do echo "<string>$t</string>"; done
Big Rich

3
ls -1ile birlikte kullanılabilir |, tek başına hat biçimlendirilmemiş çıkış sağlamak için
AndreyS Scherbakov

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

Doğrulanmış çalışma, muhtemelen istediğiniz astar değil, ancak zarif bir şekilde yapılamaz.


1
burada bir dize! diğer tüm çözümler arasında en zarif IMO
Eliran Malka

5

Bir değişkeni ayarlamaktan kaçınan küçük yan etkilerin kapsamı nedeniyle sevdiğim, Mitchell Currie'nin cevabının ufak bir uzantısı:

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

1
Kaldırabilirsin -name '*', aynı olurdu, değil mi?
wjandrea,

1

Ben böyle yazardım:

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

orijinal komut dosyasında en az değişiklik gerektirdiğinden (kullanan çözüm dışında IFS, ancak bashyalnızca bu döngü kontrol ifadesi için davranışı değiştirmez ).


1
Boru ve catburada gereksiz. whiledöngü yeniden yönlendirmeyi kabul eder, bu yüzden bu genellikle şöyle yazılırwhile IFS= read -r line; do...done < input.txt
Sergiy Kolodyazhnyy

0

Mapfile , satırları bir dosyadan dizine dizilmiş bir diziye okumak için , okunabileceği kadar taşınabilir ancak biraz daha hızlı okumak için uygun bir yoldur . Döngü için kullanarak alt kabuk oluşturmaktan kaçınırsınız.

#!/bin/bash

mapfile -t < file.txt

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

Boru hatlarını kullanırken aklınızda bulundurun, süre döngüsünü bir alt kabuğa koyacaktır. While döngüsü benzeri değişkenler içindeki değişiklikler komut dosyasının dış kısmına yayılmaz.

Örnek:

#!/bin/bash

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

(Daha iyi çözüm):

#!/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, işlevsel bir çözüme yakın bir noktaya geldi, ancak hiç kimse bilinmeyen miktarda girdi (yani find ~/.gdfuse -name '*') sonucunu değişkenlere atamaya çalışıyor olmalı ! Veya, en azından bir dizi değişkeni ile böyle bir şey yapmaya çalışıyorum; eğer çok tembel olmakta ısrar edersen! İşte Dandalf’ın çözümü, tehlikeli manevralar olmadan yapılıyor; ve hepsi bir arada

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

Hey @odoncaoa, find komutunu çift tırnak işareti olmadan yapmaya çalıştım, ancak tüm sonuçların tek satırda görünmesini sağlıyor. Bu komut ile “bilinmeyen miktarda girdi” hakkında kafam karıştı. Tehlikelere ve teorik olarak nasıl gerçekleşebileceklerine bir örnek verebilir misiniz? Gerçekten tembel olmak istemiyorum, diğer programlama dillerinde daha rahatım.
Dandalf
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.