Değeri bir değişkene atayarak satır satır bir dosya okuma


752

Aşağıdaki .txt dosyası var:

Marco
Paolo
Antonio

Satır satır okumak istiyorum ve her satır için bir değişkene bir .txt satır değeri atamak istiyorum. Benim değişkenimi varsayalım $name, akış:

  • Dosyadan ilk satırı oku
  • Ata $name= "Marco"
  • İle bazı işler yapın $name
  • Dosyadan ikinci satırı oku
  • Ata $name= "Paolo"


3
Bu sorular bir şekilde birleştirilebilir mi? Her ikisinin de sorunun farklı yönlerini vurgulayan gerçekten iyi cevapları var, kötü cevaplar yorumlarda kendileri için neyin kötü olduğunu derinlemesine açıklıyor ve şu andan itibaren neyin dikkate alınacağına dair genel bir bakış alamıyorsunuz. çiftten tek bir soru. Hepsini 2 sayfaya yerleştirmek yerine tek bir noktaya yerleştirmek faydalı olacaktır.
Egor Hans

Yanıtlar:


1357

Aşağıda satır satır bağımsız değişken olarak iletilen bir dosya okundu:

while IFS= read -r line; do
    echo "Text read from file: $line"
done < my_filename.txt

Bu, döngüdeki bir dosyadan satır okumak için standart formdur . Açıklama:

  • IFS=(veya IFS='') ön / arka boşlukların kırpılmasını önler.
  • -r ters eğik çizgi kaçışlarının yorumlanmasını engeller.

Veya bir bash dosyası yardımcı komut dosyasına, örnek içeriğe koyabilirsiniz:

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

Yukarıdakiler dosya adına sahip bir komut dosyasına kaydedilirse readfile, aşağıdaki gibi çalıştırılabilir:

chmod +x readfile
./readfile filename.txt

Dosya standart bir POSIX metin dosyası değilse (= yeni satır karakteriyle sonlandırılmamışsa), döngü, izleyen kısmi satırları işleyecek şekilde değiştirilebilir:

while IFS= read -r line || [[ -n "$line" ]]; do
    echo "Text read from file: $line"
done < "$1"

Burada, a ile || [[ -n $line ]]bitmezse son satırın yok sayılmasını önler \n(çünkü readEOF ile karşılaştığında sıfırdan farklı bir çıkış kodu döndürür).

Döngü içindeki komutlar standart girdiden de okunuyorsa, tarafından kullanılan dosya tanımlayıcı readbaşka bir şeyle değiştirilebilir ( standart dosya tanımlayıcılardan kaçının ), örn:

while IFS= read -r -u3 line; do
    echo "Text read from file: $line"
done 3< "$1"

(Bash olmayan mermiler bilmiyor olabilir read -u3; read <&3bunun yerine kullanın.)


23
Bu yöntemle bir uyarı var. While döngüsünün içindeki herhangi bir şey etkileşimli ise (örneğin stdin'den okur), girdisini 1 $ 'dan alacaktır. El ile veri girme şansınız olmayacaktır.
carpie

10
Not - bazı komutlar bunu kırıyor (olduğu gibi, döngüyü kırıyorlar). Örneğin ssh, -nbayrak olmadan etkili bir şekilde döngüden kaçmanıza neden olur. Muhtemelen bunun için iyi bir neden var, ama bunu keşfetmeden önce kodumun başarısız olmasına neden olan şeyi çivilemek biraz zaman aldı.
Alex

6
tek astar olarak: IFS = '' -r satırını oku || [[-n "$ line"]]; echo "$ line"; yapılır <dosyaadı
Joseph Johnson

8
@ OndraŽižka, bunun nedeni ffmpegstdin tüketmektir. Ekle </dev/nullsizin için ffmpeghat ve yapabileceksiniz veya döngü için alternatif FD kullanmayacaktır. Bu "alternatif FD" yaklaşımı benziyor while IFS='' read -r line <&3 || [[ -n "$line" ]]; do ...; done 3<"$1".
Charles Duffy

9
grumble re: bir .shuzatma tavsiye . UNIX'teki yürütülebilir dosyalar genellikle hiç uzantıya sahip değildir (çalıştırmazsınız ls.elf) ve bir bash shebang'a (ve sadece bash takımlarına [[ ]]) ve POSIX sh uyumluluğunu ima eden bir uzantıya dahili olarak çelişkilidir.
Charles Duffy

309

-rŞunun için geçerli readolan bayrağı kullanmanızı öneririm:

-r  Do not treat a backslash character in any special way. Consider each
    backslash to be part of the input line.

Alıntı yapıyorum man 1 read.

Başka bir şey, bir dosya adını argüman olarak almaktır.

İşte güncellenmiş kod:

#!/usr/bin/bash
filename="$1"
while read -r line; do
    name="$line"
    echo "Name read from file - $name"
done < "$filename"

4
Hattan

@Tomlar ve ortadaki boşluklara ne olur? İpucu: İstenmeyen komut yürütmeyi denedi.
kmarsh

1
Kabul edilen cevabın aksine bu benim için çalıştı.
Nörotransmitter

3
@TranslucentCloud, bu çalıştı ve kabul cevap, senin kabuktu şüpheli olmasaydı sh, değil bash; || [[ -n "$line" ]]kabul edilen cevapta sözdiziminde kullanılan genişletilmiş test komutu bir bashizmdir. Bununla birlikte, sözdiziminin aslında ilgili bir anlamı vardır: Bir satırsonu olmasa bile, giriş dosyasındaki son satır için döngünün devam etmesine neden olur. Bunu POSIX uyumlu bir şekilde yapmak istiyorsanız || [ -n "$line" ], [yerine kullanmak istersiniz [[.
Charles Duffy

3
Yani bu, söz konusu mu hala kümesine değiştirilmesi gerekir IFS=için readboşluk kırparak önlemek için.
Charles Duffy

132

Aşağıdaki Bash şablonunu kullanmak, bir dosyadan her seferinde bir değeri okumanıza ve işlemenize izin vermelidir.

while read name; do
    # Do what you want to $name
done < filename

14
tek astar olarak: okunurken; echo $ {name} yapın; yapılır <dosyaadı
Joseph Johnson

4
@CalculusKnight, sadece "çalıştı" çünkü test etmek için yeterince ilginç veriler kullanmadınız. Ters eğik çizgi içeren veya yalnızca içeren bir satır içeren içeriği deneyin *.
Charles Duffy

7
@Matthias, nihayetinde yanlış olduğu ortaya çıkan varsayımlar, hem güvenliği etkileyen hem de başka şekilde en büyük hata kaynaklarından biridir. Gördüğüm en büyük veri kaybı olayı, birinin "tam anlamıyla asla gelmeyeceğini" düşündüğü bir senaryodan kaynaklandı - bir arabellek, dosyaları adlandırmak için kullanılan bir arabellek içine rastgele bellek dökerek hangi adların muhtemelen olabileceği hakkında varsayımlar yapan bir komut dosyasına neden oldu çok, çok talihsiz davranışlar sergiliyorlar.
Charles Duffy

5
@Matthias, ... ve burada özellikle doğrudur, çünkü StackOverflow'da gösterilen kod örnekleri, milletlerin kalıpları kendi işlerinde yeniden kullanmaları için öğretim araçları olarak kullanılmak üzere tasarlanmıştır!
Charles Duffy

5
@Matthias, "kodunuzu yalnızca beklediğiniz veriler için tasarlamalısınız" iddiasına tamamen katılmıyorum. Beklenmedik durumlar, hatalarınızın olduğu, güvenlik açıklarınızın bulunduğu yerdir - bunları işlemek, slapdash kodu ile sağlam kod arasındaki farktır. Verilen, bu işlemenin süslü olması gerekmez - sadece "bir hata ile çıkış" olabilir - ancak hiç işleminiz yoksa, beklenmedik durumlarda davranışınız tanımsızdır.
Charles Duffy

76
#! /bin/bash
cat filename | while read LINE; do
    echo $LINE
done

8
Diğer cevaplara karşı hiçbir şey, belki daha sofistike, ama bu cevabı vurguluyorum çünkü basit, okunabilir ve ihtiyacım olan şey için yeterli. Çalışması için okunacak metin dosyasının boş bir satırla bitmesi gerektiğini (yani Enterson satırdan sonra bastırılması gerektiğini ), aksi takdirde son satırın göz ardı edileceğini unutmayın. En azından başıma gelen buydu.
Antonio Vinicius Menezes Medei

12
Kedinin yararsız kullanımı, acımasızca?
Brian Agnew

5
Ve alıntı bozuldu; ve büyük harf değişkeni adları kullanmamalısınız, çünkü bunlar sistem kullanımı için ayrılmıştır.
üçlü

7
@AntonioViniciusMenezesMedei, ... dahası, millet finansal kayıpları sürdürdüğünü gördüm çünkü bu uyarıların onlar için hiçbir zaman önemli olmayacağını varsaydılar; iyi uygulamaları öğrenemedi; ve daha sonra, kritik faturalandırma verilerinin yedeklerini yöneten komut dosyaları yazarken alışkanlıklarını takip ettiler. İşleri doğru yapmayı öğrenmek önemlidir.
Charles Duffy

6
Buradaki başka bir problem, borunun yeni bir alt kabuk açmasıdır, yani döngü içinde ayarlanan tüm değişkenler döngü bittikten sonra okunamaz.
mxmlnkn

20

Birçok kişi aşırı optimize edilmiş bir çözüm yayınladı. Bunun yanlış olduğunu düşünmüyorum, ama herkesin bunun nasıl çalıştığını kolayca anlayabilmesi için daha az optimize edilmiş bir çözümün isteneceğini düşünüyorum. İşte teklifim:

#!/bin/bash
#
# This program reads lines from a file.
#

end_of_file=0
while [[ $end_of_file == 0 ]]; do
  read -r line
  # the last exit status is the 
  # flag of the end of file
  end_of_file=$?
  echo $line
done < "$1"

20

kullanın:

filename=$1
IFS=$'\n'
for next in `cat $filename`; do
    echo "$next read from $filename" 
done
exit 0

IFSFarklı ayarladıysanız garip sonuçlar alırsınız.


34
Bu korkunç bir yöntem . Fark etmeden önce gerçekleşecek globbing ile ilgili sorun yaşamak istemiyorsanız lütfen kullanmayın!
gniourf_gniourf

13
@MUYBelçika *bir satırda bir tane içeren bir dosya ile denediniz mi? Her neyse, bu bir antipattern . İle satırlarını okuma .
gniourf_gniourf

2
@ OndraŽižka, readyaklaşım, toplum konsensüsünün en iyi uygulama yaklaşımıdır . Yorumunuzda bahsettiğiniz uyarı ffmpeg, döngünüz stdin'den okunan komutlar (örneğin ) çalıştırdığında , döngü için stdin olmayan bir FD kullanılarak veya bu tür komutların girişini yeniden yönlendirerek çözüldüğünde uygulanan bir uyarıdır. Buna karşılık, for-loop yaklaşımınızdaki globbing böceğinin etrafında çalışmak, kabuk-global ayar değişiklikleri yapmak (ve sonra tersine çevirmek) anlamına gelir.
Charles Duffy

1
OndraŽižka @ ... dahası, fordöngü yaklaşımı size bile gigabayt veri üzerinde döngü eğer tamamen kullanışsız hale tüm içerik döngü hiç çalıştırmadan başlamadan önce okunması gerektiğini burada araçlarını kullanmak zorunda özürlü globbing; while readdöngüsü, alt işlemi üreten içerik çalışır durumdayken (böylece amacı akışı için kullanılabilmesidir) yürütme başlayabilir, yani bir seferde bir daha fazlası tek bir hattın veri daha depolamak için gereken ve aynı zamanda sınırlı bellek tüketimine sahiptir.
Charles Duffy

1
Aslında, whiletemelli yaklaşımların bile karakter sorunları var gibi görünüyor. Yukarıdaki kabul edilen yanıtın yorumlarına bakın. Yine de, bir anti-patlayıcı olmak için dosyalar üzerinde yinelemeye karşı tartışmamak.
Egor Hans

9

Hem giriş dosyasını hem de kullanıcı girişini (veya stdin'den başka bir şeyi) işlemeniz gerekiyorsa, aşağıdaki çözümü kullanın:

#!/bin/bash
exec 3<"$1"
while IFS='' read -r -u 3 line || [[ -n "$line" ]]; do
    read -p "> $line (Press Enter to continue)"
done

Kabul edilen cevaba ve bash-hackerların yönlendirme eğitimine dayanarak .

Burada, kod argümanı olarak iletilen dosya için dosya tanımlayıcı 3'ü açıyoruz ve readbu tanımlayıcıyı input ( -u 3) olarak kullanmayı söylüyoruz . Böylece, varsayılan giriş tanımlayıcısını (0) bir kullanıcı terminalini okuyabilen bir terminale veya başka bir giriş kaynağına bağlı bırakıyoruz.


7

Düzgün hata yönetimi için:

#!/bin/bash

set -Ee    
trap "echo error" EXIT    
test -e ${FILENAME} || exit
while read -r line
do
    echo ${line}
done < ${FILENAME}

Lütfen biraz açıklama ekler misiniz?
Tyler Christian

Ne yazık ki dosyadaki son satırı özlüyor.
ungalcrys

... ve ayrıca, alıntı yapılmamasından dolayı, BashPitfalls # 14'te açıklandığı gibi, joker karakterler içeren satırları mühimmat eder .
Charles Duffy

0

Aşağıdakiler dosyanın içeriğini yazdırır:

cat $Path/FileName.txt

while read line;
do
echo $line     
done

1
Bu cevap gerçekten mevcut cevapların üzerine hiçbir şey eklemez, yazım hatası / hata nedeniyle işe yaramaz ve birçok yönden kırılır.
Konrad Rudolph

0

IFS (dahili alan ayırıcı) aracını bash'de kullanın , satırları jetonlara ayırmak için kullanılan karakteri tanımlar, varsayılan olarak < tab > / < space > / < newLine >

1. adım : Dosya verilerini yükleyin ve listeye ekleyin:

# declaring array list and index iterator
declare -a array=()
i=0

# reading file in row mode, insert each line into array
while IFS= read -r line; do
    array[i]=$line
    let "i++"
    # reading from file path
done < "<yourFullFilePath>"

Adım 2 : Şimdi çıktıyı tekrarlayın ve yazdırın:

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

dizideki yankı belirli dizin : Dizideki bir değişkene erişim:

echo "${array[0]}"
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.