it-swarm-tr.com

Bash'deki bir dosyanın içeriğinde dolaşmak

Bash ile bir metin dosyasının her satırında nasıl yinelenirim?

Bu komut dosyasıyla:

echo "Start!"
for p in (peptides.txt)
do
    echo "${p}"
done

Bu çıktıyı ekranda alıyorum:

Start!
./runPep.sh: line 3: syntax error near unexpected token `('
./runPep.sh: line 3: `for p in (peptides.txt)'

(Daha sonra $p ile daha karmaşık bir şey yapmak istiyorum, sadece ekrandan çıkın.)


Shell ortam değişkeni (env'den):

Shell=/bin/bash

/bin/bash --version çıktısı:

GNU bash, version 3.1.17(1)-release (x86_64-suse-linux-gnu)
Copyright (C) 2005 Free Software Foundation, Inc.

cat /proc/version çıktısı:

Linux version 2.6.18.2-34-default ([email protected]) (gcc version 4.1.2 20061115 (prerelease) (SUSE Linux)) #1 SMP Mon Nov 27 11:46:27 UTC 2006

Peptides.txt dosyası şunları içerir:

RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
1126
Peter Mortensen

Bunu yapmanın bir yolu:

while read p; do
  echo "$p"
done <peptides.txt

Yorumlarda da belirtildiği gibi, bu, önde gelen boşlukların kırpılmasının, ters eğik çizgi dizilerinin yorumlanmasının ve sonlandırıcı bir satır beslemesinin eksik olması durumunda izleyen çizgiyi atlamanın yan etkilerine sahiptir. Bunlar endişeliyse, şunları yapabilirsiniz:

while IFS="" read -r p || [ -n "$p" ]
do
  printf '%s\n' "$p"
done < peptides.txt

İstisnai olarak, döngü gövdesi standart girdiden okuyabilir ise, dosyayı farklı bir dosya tanımlayıcı kullanarak açabilirsiniz:

while read -u 10 p; do
  ...
done 10<peptides.txt

Burada, 10 yalnızca rastgele bir sayıdır (0, 1, 2'den farklı).

1786
Bruno De Fraine
cat peptides.txt | while read line
do
   # do something with $line here
done
345
Warren Young

Seçenek 1a: While döngü: Bir seferde tek satır: Giriş yönlendirmesi

#!/bin/bash
filename='peptides.txt'
echo Start
while read p; do 
    echo $p
done < $filename

Seçenek 1b: While döngü: Bir seferde tek satır:
Dosyayı açın, bir dosya tanımlayıcıdan okuyun (bu durumda dosya tanımlayıcı # 4).

#!/bin/bash
filename='peptides.txt'
exec 4<$filename
echo Start
while read -u4 p ; do
    echo $p
done

Seçenek 2: Döngü için: Dosyayı tek değişkende okuyup ayrıştırır.
Bu sözdizimi, belirteçler arasındaki beyaz boşluğa dayalı olarak "çizgileri" ayrıştırır. Bu hala çalışır çünkü verilen girdi dosyası satırları tek kelimeli simgelerdir. Satır başına birden fazla belirteç varsa, bu yöntem işe yaramaz. Ayrıca, tam dosyayı tek bir değişkene okumak, büyük dosyalar için iyi bir strateji değildir.

#!/bin/bash
filename='peptides.txt'
filelines=`cat $filename`
echo Start
for line in $filelines ; do
    echo $line
done
130
Stan Graves

Bu, diğer cevaplardan daha iyi değildir, ancak işi boşluksuz bir dosyada yapmak için bir başka yoldur (yorumlara bakınız). Ayrı komut dosyaları kullanmak için fazladan bir adım olmadan, metin dosyalarında listelerde gezinmek için genellikle tek gömleklere ihtiyacım olduğunu biliyorum.

for Word in $(cat peptides.txt); do echo $Word; done

Bu format hepsini bir komut satırına koymamı sağlıyor. "Echo $ Word" bölümünü istediğiniz şekilde değiştirin; noktalı virgüllerle ayırarak birden çok komut verebilirsiniz. Aşağıdaki örnek, dosyanın içeriğini yazmış olabileceğiniz diğer iki komut dosyasına argüman olarak kullanır.

for Word in $(cat peptides.txt); do cmd_a.sh $Word; cmd_b.py $Word; done

Veya bunu bir akış düzenleyici gibi kullanmayı düşünüyorsanız (sed 'i öğrenin) çıktıyı aşağıdaki gibi başka bir dosyaya aktarabilirsiniz.

for Word in $(cat peptides.txt); do cmd_a.sh $Word; cmd_b.py $Word; done > outfile.txt

Bunları yukarıda yazıldığım gibi kullandım çünkü satırları bir Word ile oluşturduğum metin dosyalarını kullandım. (Yorumlara bakın) Kelimelerinizi/satırlarınızı bölmek istemediğiniz alanlarınız varsa, biraz çirkinleşir, ancak aynı komut hala aşağıdaki şekilde çalışır:

OLDIFS=$IFS; IFS=$'\n'; for line in $(cat peptides.txt); do cmd_a.sh $line; cmd_b.py $line; done > outfile.txt; IFS=$OLDIFS

Bu sadece Shell'e boşluklara değil yeni hatlara bölmesini ve ardından ortamı daha önce olduğu gibi geri döndürmesini söyler. Bu noktada, hepsini tek bir satıra sıkıştırmak yerine bir Shell komut dosyasına koymayı düşünebilirsiniz.

İyi şanslar!

69
mightypile

Diğer cevapların kapsamadığı bir kaç şey daha:

Sınırlandırılmış bir dosyadan okuma

# ':' is the delimiter here, and there are three fields on each line in the file
# IFS set below is restricted to the context of `read`, it doesn't affect any other code
while IFS=: read -r field1 field2 field3; do
  # process the fields
  # if the line has less than three fields, the missing fields will be set to an empty string
  # if the line has more than three fields, `field3` will get all the values, including the third field plus the delimiter(s)
done < input.txt

İşlem ikamesini kullanarak başka bir komutun çıktısından okuma

while read -r line; do
  # process the line
done < <(command ...)

Bu yaklaşım command ... | while read -r line; do ... 'dan daha iyidir, çünkü burada while döngüsü ikinci durumda olduğu gibi alt kabuk yerine geçerli Shell'de çalışır. İlgili yazıya bakınız Bir süre döngüsü içinde değiştirilmiş bir değişken hatırlanmıyor .

Boş bir sınırlı girdiden okuma, örneğin find ... -print0

while read -r -d '' line; do
  # logic
  # use a second 'read ... <<< "$line"' if we need to tokenize the line
done < <(find /path/to/dir -print0)

İlgili okuma: BashFAQ/020 - Yeni satırları, boşlukları veya her ikisini içeren dosya adlarını nasıl bulabilir ve güvenle kullanabilirim?

Bir seferde birden fazla dosyadan okuma

while read -u 3 -r line1 && read -u 4 -r line2; do
  # process the lines
  # note that the loop will end when we reach EOF on either of the files, because of the `&&`
done 3< input1.txt 4< input2.txt

@ chepner'ın esasına göre answer burada :

-u bir bash uzantısıdır. POSIX uyumluluğu için, her çağrı read -r X <&3 gibi bir şeye benzeyecektir.

Tüm dosyayı bir diziye okuma (Bash'in önceki sürümleri 4'ten önceki)

while read -r line; do
    my_array+=("$line")
done < my_file

Dosya tamamlanmamış bir satırla bitiyorsa (sonunda satırsonu eksik), o zaman:

while read -r line || [[ $line ]]; do
    my_array+=("$line")
done < my_file

Tüm dosyayı bir diziye okuma (Bash sürüm 4x ve üstü)

readarray -t my_array < my_file

veya

mapfile -t my_array < my_file

Ve sonra

for line in "${my_array[@]}"; do
  # process the lines
done

İlgili Mesajlar:

56
codeforester

Bir süre döngü kullanın, böyle:

while IFS= read -r line; do
   echo "$line"
done <file

Notlar:

  1. IFS işlevini doğru bir şekilde ayarlamazsanız, girintiyi kaybedersiniz.

  2. Neredeyse her zaman okuma ile -r seçeneğini kullanmalısınız.

  3. for ile satır okuma

42
Jahid

Okumanızın newline karakteriyle kırılmasını istemiyorsanız, - kullanın

#!/bin/bash
while IFS='' read -r line || [[ -n "$line" ]]; do
    echo "$line"
done < "$1"

Ardından komut dosyasını dosya adıyla parametre olarak çalıştırın.

13
Anjul Sharma

Bu dosyanın sizde olduğunu varsayalım:

$ cat /tmp/test.txt
Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR

Birçok Bash çözümü tarafından okunan dosya çıktısının anlamını değiştirecek dört öğe vardır:

  1. Boş çizgi 4;
  2. İki satırda lider veya iz bırakan boşluklar;
  3. Bireysel satırların anlamını korumak (yani, her satır bir kayıttır);
  4. Satır 6 bir CR ile sonlandırılmadı.

Metin dosyası satırında boş satırlar ve CR olmayan sonlandırma satırlarını içeren satırlar istiyorsanız, bir süre döngüsü kullanmalı ve son satır için alternatif bir test yaptırmalısınız.

Dosyayı değiştirebilecek yöntemler şunlardır (cat öğesinin döndürdüğü ile karşılaştırıldığında):

1) Son satırı ve öndeki ve sondaki boşlukları kaybedersiniz:

$ while read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'

(Bunun yerine while IFS= read -r p; do printf "%s\n" "'$p'"; done </tmp/test.txt yaparsanız, baştaki ve sondaki boşlukları korur, ancak CR ile sonlandırılmadıysa son satırı kaybedersiniz)

2) cat ile işlem değişiminin kullanılması, dosyanın tamamını bir yudumda okur ve satırların anlamını kaybeder:

$ for p in "$(cat /tmp/test.txt)"; do printf "%s\n" "'$p'"; done
'Line 1
    Line 2 has leading space
Line 3 followed by blank line

Line 5 (follows a blank line) and has trailing space    
Line 6 has no ending CR'

("$(cat /tmp/test.txt) dosyasından kaldırırsanız, Word dosyasını bir yudum yerine Word ile okursunuz. Ayrıca muhtemelen ne amaçlanmamıştır ...)


Bir dosyayı satır satır okumanın ve tüm boşlukları korumanın en sağlam ve en basit yolu:

$ while IFS= read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'    Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space    '
'Line 6 has no ending CR'

Önde gelen ve işlem alanlarınızı soyunmak istiyorsanız, IFS= bölümünü kaldırın:

$ while read -r line || [[ -n $line ]]; do printf "'%s'\n" "$line"; done </tmp/test.txt
'Line 1'
'Line 2 has leading space'
'Line 3 followed by blank line'
''
'Line 5 (follows a blank line) and has trailing space'
'Line 6 has no ending CR'

(\n sonlandırması olmayan bir metin dosyasının, oldukça yaygın olmasına rağmen, POSIX altında kırılmış olduğu kabul edilir. \n izlemesine güvenebilirseniz, while döngüsünde || [[ -n $line ]] gerekmez.)

Daha fazlası BASH FAQ

12
dawg
#!/bin/bash
#
# Change the file name from "test" to desired input file 
# (The comments in bash are prefixed with #'s)
for x in $(cat test.txt)
do
    echo $x
done
4
Sine

İşte benim gerçek hayatım örneği, başka bir program çıktısının satırlarını nasıl döndüğüme, alt dizileri nasıl kontrol edeceğimize, değişkenden çift tırnak bırakacağım, bu değişkeni döngünün dışında kullanacağım. Sanırım çoğu kişi bu soruları er ya da geç soruyor.

##Parse FPS from first video stream, drop quotes from fps variable
## streams.stream.0.codec_type="video"
## streams.stream.0.r_frame_rate="24000/1001"
## streams.stream.0.avg_frame_rate="24000/1001"
FPS=unknown
while read -r line; do
  if [[ $FPS == "unknown" ]] && [[ $line == *".codec_type=\"video\""* ]]; then
    echo ParseFPS $line
    FPS=parse
  fi
  if [[ $FPS == "parse" ]] && [[ $line == *".r_frame_rate="* ]]; then
    echo ParseFPS $line
    FPS=${line##*=}
    FPS="${FPS%\"}"
    FPS="${FPS#\"}"
  fi
done <<< "$(ffprobe -v quiet -print_format flat -show_format -show_streams -i "$input")"
if [ "$FPS" == "unknown" ] || [ "$FPS" == "parse" ]; then 
  echo ParseFPS Unknown frame rate
fi
echo Found $FPS

Değişkeni döngü dışında bildir, değeri ayarla ve döngü dışında kullan, done <<< "$ (...)" sözdizimini gerektirir. Uygulamanın geçerli bir konsolun bağlamında çalıştırılması gerekir. Komut etrafındaki tırnaklar, çıktı akışının yeni satırlarını tutar.

Alt diziler için döngü eşleşmesi daha sonra name = value pair okur, son = karakterin sağ tarafını böler, ilk teklifi düşürür, son teklifi düşürür, başka yerlerde kullanılacak temiz bir değere sahibiz.

3
Whome

@Peter: Bu sizin için işe yarayabilir-

echo "Start!";for p in $(cat ./pep); do
echo $p
done

Bu çıktıyı döndürürdü.

Start!
RKEKNVQ
IPKKLLQK
QYFHQLEKMNVK
IPKKLLQK
GDLSTALEVAIDCYEK
QYFHQLEKMNVKIPENIYR
RKEKNVQ
VLAKHGKLQDAIN
ILGFMK
LEDVALQILL
1
Alan Jebakumar

Bu oldukça geç geliyor, ancak birisinin yardım edebileceği düşüncesiyle cevabı ekliyorum. Ayrıca bu en iyi yol olmayabilir. head komutu okumak için -n argümanı ile birlikte kullanılabilir n satırları Dosyanın başından itibaren ve aynı şekilde tail komutu alttan okumak için kullanılabilir. Şimdi, dosyadan nth satırını almak için, n satırına yöneliyoruz, Boruyu, verilerden yalnızca 1 satır kuyruğa yönlendirin.

   TOTAL_LINES=`wc -l $USER_FILE | cut -d " " -f1 `
   echo $TOTAL_LINES       # To validate total lines in the file

   for (( i=1 ; i <= $TOTAL_LINES; i++ ))
   do
      LINE=`head -n$i $USER_FILE | tail -n1`
      echo $LINE
   done
0
madD7