Neden `IFS = read` `IFS = yerine, bu kadar sık ​​kullanılıyorsa; okurken ...


81

Normal uygulama, IFS ayarını her yinelemede ayarlamayı tekrar etmemek için süre döngüsünün dışına koyar gibi görünüyor ... Bu, bu maymun için olduğu gibi, sadece alışılmış bir "maymun görmek, maymun yapmak" tarzı mı? okuduğum adam okuma , ya da ben burada bazı ince (ya da aşikar) tuzak eksik?

Yanıtlar:


82

Tuzak bu

IFS=; while read..

IFStüm kabuk ortamını döngü dışına ayarlar , oysa

while IFS= read

onu sadece readçağrı için (Bourne kabuğunda hariç) yeniden tanımlıyor. Bunun gibi bir döngü yapıp yapmadığını kontrol edebilirsiniz.

while IFS= read xxx; ... done

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

blabalbla
 ooooooo

halbuki

IFS=; read xxx; ... done

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

blabalbla  ooooooo

Eğer ikinci formu kullanarak, yani sıfırlamak için hatırlamak zorunda: IFS=$' \t\n'.


Bu sorunun ikinci kısmı burada birleştirildi , ilgili cevabı buradan kaldırdım.


Tamam, olası bir 'tuzak' dış IFS'yi sıfırlamayı ihmal ediyor gibi görünüyor ... Ama başka bir şey daha olup olmadığını merak ediyorum ... Burada oldukça ateşli bir şekilde test ediyorum. IFS iken while komut listesinin ayarlanmasının, bunun bir iki nokta üst üste gelip gelmediğine bağlı olarak farklı davranışlar gösterdiğine dikkat edin. Bu davranışı henüz anlamadım, ve şimdi merak ediyorum, bu seviyede bazı özel düşünceler olup olmadığını merak ediyorum ... örn. while IFS=X readEn bölmek değil X, ama while IFS=X; readyapar ...
Peter.O

( Yarı kolonu kastediyorsun , değil mi?) İkincisi whilepek bir anlam ifade etmiyor - o noktalı virgülün while bitmesi için şart , yani gerçek bir döngü yok ... readsadece bir element döngüsünün içindeki ilk komut haline geliyor ... Veya değil ? Peki ya o dozaman?
rozcietrzewiacz 17:11

1
Hayır, bekle - haklısın, whiledurumda (önce do) birkaç komut olabilir .
rozcietrzewiacz 17:11

Oh .. kesinlikle, onlara sahip olabilirsiniz ... fark ettiğiniz gibi ... ama yarı kolon gibi görünmüyorlar ... (ve döngü sonuncu bir emir olmayana dönene kadar ad-sonsuz döngüyü sürdürecek Sıfır çıkış kodu) ... Şimdi tuzağın tamamen farklı bir sektörde olup olmadığını merak ediyorum ; nasıl anlayış bu süre en komut listesi çalışır örn. neden IFS=işe yarıyor, ama IFS=Xyapmıyor ... (ya da belki bir süredir
OD'deyim

1
Ben (önceki açıklamada belirtildiği gibi) .. Bu ilginç görünüyor, ve onun benim güncelleştirme taşındığında $ rozcietrzewiacz .. Hata ... Ben, senin güncelleştirme fark etmemişlerdi başlayan mantıklı ... ama hatta bir gece-için Benim gibi bir kuş, çok geç oldu ... (Sabah kuşlarını yeni duydum:) ... Bu, biraz topladım ve örneklerini okudum ... Sanırım anladım, aslında ben ' anladım eminim, ama uyumalıyım :) ... Bu neredeyse bir Eureka! an ... teşekkürler
Peter.O 17:11

45

Dikkatlice hazırlanmış bir girdi metniyle bir örneğe bakalım:

text=' hello  world\
foo\bar'

Bu iki satır, ilk önce bir boşlukla başlar ve bir ters eğik çizgiyle biter. İlk önce, etrafta herhangi bir önlem alınmadan neler olacağına bakalım read(ancak herhangi bir genişleme riski olmadan printf '%s\n' "$text"dikkatli bir şekilde baskı $textyapmak için). (Aşağıda, $ ‌kabuk istemidir.)

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

readters eğik çizgi kadar yedim: ters eğik çizgi-newline yeni çizginin yok sayılmasına neden olur ve ters eğik çizgi-herhangi bir şey ilk ters eğik çizgiyi yok sayar. Ters eğik çizgilerin özel muamele görmesini önlemek için kullanırız read -r.

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

Bu daha iyi, beklendiği gibi iki çizgimiz var. İki satır neredeyse istenen içeriği içerir: arasındaki hellove arasındaki çift boşluk worldkorunur, çünkü linedeğişken içindedir . Öte yandan, ilk alan yenildi. Bunun nedeni read, değişkenleri geçtiğin kadar çok kelime okuduğu için, son değişken satırın geri kalanını içermesi dışında - ama yine de ilk kelimeyle başlar, yani ilk boşluklar atılır.

Bu nedenle, her satırı tam anlamıyla okumak için, hiçbir kelime ayrılmasının olmadığından emin olmamız gerekir . Bunu, IFSdeğişkeni 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]

Biz set nasıl Not IFS süresince özellikle readyerleşik . IFS= read -r lineSetleri değişkeni IFSspesifik olarak yürütülmesi için (boş değer) read. Bu, genel basit komut sözdiziminin bir örneğidir : bunu bir komut adı ve argümanlarının izlediği (muhtemelen boş) değişken atama dizisi (ayrıca, istediğiniz noktaya yönlendirmelerde de atabilirsiniz). Yana readyerleşik bir değişken gerçekte hiçbir zaman bir harici işlemin ortamda biter olduğu; Bununla birlikte, değeri , yürütüldüğü $IFSsürece orada readatadığımız şeydir¹. Bunun özel bir yerleşikread olmadığını unutmayın , bu nedenle atama yalnızca süresi boyunca devam eder.

Bu nedenle IFS, ona bağlı olabilecek diğer talimatların değerini değiştirmemeye özen gösteriyoruz . Bu kod, çevreleyen kodun IFSbaşlangıçta neye ayarlanmış olursa olsun çalışır ve döngü içindeki kodun kullanılması durumunda herhangi bir soruna neden olmaz IFS.

Dosyaları iki nokta üst üste ayrılmış bir yolda arayan bu kod snippet'iyle kontrast. Dosya adları listesi, bir dosyadan satır başına bir dosya adı 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ü olsaydı while IFS=; read -r name; do …, o for dir in $PATHzaman $PATHkolon ayrılmış bileşenlere ayrılmaz. Kod IFS=; while read …olsaydı , döngü gövdesinde IFSayarlanmamış daha da açık olurdu :.

Tabii ki, IFSçalıştırdıktan sonra değerini geri yüklemek mümkün olacaktır read. Ancak bu, önceki çabanın bilinmesini gerektirir ki bu da ekstra bir çabadır. IFS= readbasit yoldur (ve uygun şekilde, aynı zamanda en kısa yoldur).

¹ Ve eğer readtuzak çalışırken, tuzağa düşürülmüş bir sinyal tarafından kesilirse, bu POSIX tarafından belirtilmez ve pratikte kabuğa bağlıdır.


4
Teşekkürler Gilles .. çok güzel bir rehberli tur .. ('f-set'i mi demek istediniz?) .... Şimdi, okuyucu için, zaten söylenenleri yeniden ifade etmek için, olan konuyu vurgulamak istiyorum yanlış yola bakıyordum. İlk ve en önemlisi yapı gerçektir while IFS= read(daha sonra, bir yarı-kolon olmadan =) olan olmayan özel bir şekli whileya da bir IFSya da readörneğin: yapı geneldir ... anyvar=anyvalue anycommand. Eksikliği ;sonra ayarı anyvarkapsamını yapar anyvar yerel etmek anycommand.. ederken - do / bitmiş döngü% 100'dür ilgisiz yerel kapsamına any_var.
Peter.O

3

Ve deyimler (komut başına vs komut dosyası / kabuk geniş değişken kapsamı) IFSarasındaki (zaten açıklığa kavuşturulmuş) kapsam farklarından ayrı olarak while IFS='' read, eve getirme dersi, eğer IFS değişkeni varsa, bir giriş satırının öndeki ve sondaki boşluklarını kaybedersiniz. (a) boşluğuna ayarlanır.IFS=''; while readwhile IFS=''; readIFS

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

Bu nedenle, IFS değişkenini boş dizgeye ayarlamak, kötü bir fikirden başka bir şey değildir, çünkü bir çizginin önde gelen ve izleyen boşlukların soyulmamasını sağlar.

Ayrıca bakınız: Bash, IFS ile dosyadan satır satır okumak

(
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"
)

+1 mükemmel gösteri, 'rm * dosyasıyla * * boşluklarıyla *' sonra temizleme
Ocak'ta amdn

0

Yuzem'in cevabından ilham alındı

IFSGerç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
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.