it-swarm-tr.com

`IFS = okunurken 'neden' IFS = yerine 'bu kadar sık ​​kullanılır? okunurken ... `?

Görünüşe göre, normal uygulama IFS'nin ayarını her yineleme için tekrarlamayı tekrarlamamak için while döngüsünün dışına koyacaktır ... Bu sadece bu maymun için olduğu gibi alışılmış bir "maymun görmek, maymun yapmak" tarzı mı Ben okudum adam okudum, ya da burada bazı ince (ya da açıkça bariz) tuzak eksik mi?

85
Peter.O

Tuzak şu ki

IFS=; while read..

döngü dışındaki tüm Shell ortamı için IFS değerini ayarlar.

while IFS= read

yalnızca read çağırma (Bourne Kabuğu hariç) için yeniden tanımlar. Bunun gibi bir döngü yapmayı kontrol edebilirsiniz

while IFS= read xxx; ... done

sonra böyle bir döngüden sonra, echo "blabalbla $IFS ooooooo" baskılar

blabalbla
 ooooooo

oysa sonra

IFS=; read xxx; ... done

IFS yeniden tanımlandı : şimdi echo "blabalbla $IFS ooooooo" baskılar

blabalbla  ooooooo

Dolayısıyla, ikinci formu kullanırsanız, sıfırlamayı unutmayın: IFS=$' \t\n'.


Bu sorunun ikinci kısmı burada birleştirilmiştir , bu yüzden ilgili cevabı buradan kaldırdım.

86
rozcietrzewiacz

Dikkatlice hazırlanmış bazı giriş metinleriyle bir örneğe bakalım:

text=' hello  world\
foo\bar'

Bu iki satır, ilk önce boşlukla başlayıp ters eğik çizgi ile bitiyor. İlk olarak, herhangi bir önlem almadan neler olduğuna bakalım read (ancak printf '%s\n' "$text" dikkatlice yazdırmak için $text genişleme riski olmadan). (Altında, $ ‌ Kabuk İstemidir.)

$ printf '%s\n' "$text" |
  while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]

read ters eğik çizgileri yedi: ters eğik çizgi-newline, yeni satırın yoksayılmasına neden olur ve ters eğik çizgi-her şey ilk ters eğik çizgiyi yok sayar. Ters eğik çizgilerin özel olarak ele alınmasını önlemek için read -r.

$ printf '%s\n' "$text" |
  while read -r line; do printf '%s\n' "[$line]"; done
[hello  world\]
[foo\bar]

Bu daha iyi, beklediğimiz gibi iki çizgimiz var. İki satır neredeyse istenen içeriği içerir: hello ve world arasındaki çift boşluk, çünkü line değişkeni içinde olduğu için korunmuştur. Öte yandan, ilk alan yenildi. Bunun nedeni, read değişkenlerini ilettiğiniz kadar çok kelime okumasıdır, ancak son değişken satırın geri kalanını içerir - ancak yine de ilk Word ile başlar, yani ilk boşluklar atılır.

Bu nedenle, her satırı kelimenin tam anlamıyla okumak için, Word bölme ifadesinin devam ettiğinden emin olmamız gerekir. Bunu IFS değişkeni değerini boş bir değere ayarlayarak yaparız.

$ printf '%s\n' "$text" |
  while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello  world\]
[foo\bar]

IFS özelliğini özellikle read yerleşik süresi boyunca nasıl ayarladığımızı unutmayın. IFS= read -r line özellikle IFS yürütülmesi için read ortam değişkenini (boş bir değere) ayarlar. Bu genel basit komut sözdiziminin bir örneğidir: bir (muhtemelen boş) değişken atama dizisi, ardından bir komut adı ve argümanları (ayrıca, herhangi bir noktada yeniden yönlendirmeler atabilirsiniz). read yerleşik olduğundan, değişken hiçbir zaman harici bir işlem ortamında sonuçlanmaz; yine de $IFS, read yürüttüğü sürece oraya atadığımız şeydir¹. read değerinin bir özel yerleşik olmadığını unutmayın, bu nedenle atama yalnızca süresi boyunca devam eder.

Bu nedenle, ona güvenebilecek diğer talimatlar için IFS değerini değiştirmemeye özen gösteriyoruz. Bu kod, çevreleyen kodun IFS değerini başlangıçta ayarlamasına bakılmaksızın çalışır ve döngünün içindeki kod IFS'ye dayanırsa sorun yaratmaz.

İki nokta üst üste ile ayrılmış bir yolda görünen bu kod snippet'inin aksine. Dosya adları listesi her satırda bir dosya adı olan bir dosyadan okunur.

IFS=":"; set -f
while IFS= read -r name; do
  for dir in $PATH; do
    ## At this point, "$IFS" is still ":"
    if [ -e "$dir/$name" ]; then echo "$dir/$name"; fi
  done
done <filenames.txt

Döngü while IFS=; read -r name; do …, sonra for dir in $PATH bölünmez $PATH kolonla ayrılmış bileşenlere. Kod IFS=; while read …, IFS öğesinin : döngü gövdesinde.

Elbette, IFS yürütüldükten sonra read değerini geri yüklemek mümkün olacaktır. Ancak bu, daha fazla çaba gerektiren önceki değerin bilinmesini gerektirecektir. IFS= read basit yoldur (ve rahatlıkla, en kısa yoldur).

¹ Ve eğer read bir tuzak sinyali tarafından kesilirse, muhtemelen tuzak yürütülürken - bu POSIX tarafından belirtilmez ve pratikte Shell'e bağlıdır.

while IFS='' read, IFS=''; while read Ve while IFS=''; read Deyimleri (komut başına/script/Shell çapında IFS değişken kapsamı) arasındaki (zaten açıklığa kavuşturulmuş) IFS kapsam belirleme farklarının dışında, -ev dersi liderliği kaybetmen ve IFS değişkeni (a içerir) boşluğuna ayarlanmışsa, bir giriş satırının sondaki boşlukları.

Dosya yolları işleniyorsa bunun oldukça ciddi sonuçları olabilir.

Bu nedenle, IFS değişkeninin boş dizeye ayarlanması, kötü bir fikirden başka bir şey değildir çünkü bir satırın önde gelen ve sondaki boşluklarının soyulmamasını sağlar.

Ayrıca bkz .: Bash, dosyadan satır satır oku, IFS ile

(
shopt -s nullglob
touch '  file with spaces   '
IFS=$' \t\n' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
IFS='' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
)
3
jon

Yuzem’in cevabı

IFS değerini gerçek bir karaktere ayarlamak istiyorsanız, bu benim için çalıştı

iconv -f cp1252 zapni.tv.php | while IFS='#' read -d'#' line
do
  echo "$line"
done
1
Steven Penny