Bash'de bir metin dosyasından bir dizi oluşturma


88

Komut dosyası bir URL'yi alır, gerekli alanlar için ayrıştırır ve çıktısını bir dosya.txt dosyasına kaydedilmesi için yeniden yönlendirir . Her alan bulunduğunda çıktı yeni bir satıra kaydedilir.

file.txt

A Cat
A Dog
A Mouse 
etc... 

file.txtYeni bir komut dosyasında ondan bir dizi alıp oluşturmak istiyorum , burada her satır dizideki kendi dizgi değişkeni olur. Şimdiye kadar denedim:

#!/bin/bash

filename=file.txt
declare -a myArray
myArray=(`cat "$filename"`)

for (( i = 0 ; i < 9 ; i++))
do
  echo "Element [$i]: ${myArray[$i]}"
done

Bu komut dosyasını çalıştırdığımda, boşluk kelimelerin bölünmesine neden oluyor ve

Istenilen çıktı

Element [0]: A Cat 
Element [1]: A Dog 
etc... 

Sonunda şunu alıyorum:

Gerçek çıktı

Element [0]: A 
Element [1]: Cat 
Element [2]: A
Element [3]: Dog 
etc... 

Aşağıdaki döngüyü, her satırdaki tüm dizenin dizideki her değişkenle bire bir karşılık geleceği şekilde nasıl ayarlayabilirim?


5
Bu nedir Bash SSS 001 tüm ilgili. Ayrıca Bash SSS 005'teki dizi konusunun bu bölümü .
Etan Reisner

1
Bunu stackoverflow.com/questions/11393817/… 'nin bir kopyası olarak ilişkilendiririm , ancak kabul edilen cevap korkunç.
Charles Duffy

Etan, bu kadar hızlı ve doğru cevap için çok teşekkür ederim! Sorumu forumlarda aramaya çalıştım, ancak stackoverflow ile ilgili SSS'yi aramayı düşünmedim. Mapfile komutu ihtiyaçlarımı tam olarak karşıladı! Tekrar teşekkürler :) Bölüm 2.1'deki cevap .
user2856414

2
(Burada sahip olduğumuzdan daha iyi kabul edilen bir cevaba sahip olduğumuz için bağlantıyı ters yönde kurun).
Charles Duffy

Yanıtlar:


110

Şu mapfilekomutu kullanın :

mapfile -t myArray < file.txt

Hata kullanıyor for- bir dosyanın satırları üzerinden döngü yapmanın deyimsel yolu şudur:

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

Daha fazla ayrıntı için BashFAQ / 005'e bakın .


5
Bu kurallı q & a olarak teşvik ediliyor için bir de bağlantı belirtildiği ne şunları içerebilir: while IFS= read -r; do lines+=("$REPLY"); done <file.
fedorqui

10
mapfile 4.x öncesi bash sürümlerinde mevcut değildir
ericslaw

14
Bash 4 şu anda yaklaşık 5 yaşında. Yükselt.
glenn jackman

5
Bash 4'ün 2009'da piyasaya sürülmesine rağmen, @ ericslaw'ın yorumu geçerliliğini koruyor çünkü birçok makine hala bash 3.x ile geliyor (ve bash GPLv3 altında yayınlandığı sürece yükseltilmeyecek). Taşınabilirlikle ilgileniyorsanız, dikkat etmeniz gereken önemli bir şey
De Novo

12
sorun, geliştiricinin yükseltilmiş bir sürümü yükleyememesi değil, bir geliştiricinin, komut dosyası kullanan bir komut dosyasının mapfileek adımlar olmadan birçok makinede beklendiği gibi çalışmayacağının farkında olması gerektiğidir . @ericslaw macs, yakın gelecekte bash 3.2.57 ile birlikte gönderilmeye devam edecek. Daha yeni sürümler, Apple'ın paylaşmak veya izin vermek istemedikleri şeyleri paylaşmasını veya bunlara izin vermesini gerektiren bir lisans kullanır.
De Novo

24

mapfileve readarray(eşanlamlılar) Bash sürüm 4 ve üzerinde mevcuttur. Bash'in daha eski bir sürümüne sahipseniz, dosyayı bir diziye okumak için bir döngü kullanabilirsiniz:

arr=()
while IFS= read -r line; do
  arr+=("$line")
done < file

Dosyada eksik (yeni satır eksik) bir son satır varsa, şu alternatifi kullanabilirsiniz:

arr=()
while IFS= read -r line || [[ "$line" ]]; do
  arr+=("$line")
done < file

İlişkili:


IFS= read -r line || [[ "$line" ]]Çalışması için parantez koymam gerektiğini anladım . Aksi takdirde harika çalışıyor!
Tatiana Racheva

@TatianaRacheva: Daha önce eksik olan noktalı virgül değil domi?
codeforester

9

Bunu da yapabilirsiniz:

oldIFS="$IFS"
IFS=$'\n' arr=($(<file))
IFS="$oldIFS"
echo "${arr[1]}" # It will print `A Dog`.

Not:

Dosya adı genişletmesi hala devam ediyor. Örneğin, değişmez değeri olan bir satır varsa *, geçerli klasördeki tüm dosyalara genişleyecektir. Bu nedenle, yalnızca dosyanızda bu tür bir senaryo yoksa kullanın.


IFSAtamayı sürdürürken yalnızca geçici olarak (bu komuttan sonra orijinal değerini geri kazanması için) ayarlamanın herhangi bir yolu var mı arr?
Sarılmalar

1
Dosya adı genişletmesinin hala devam ettiğini unutmayın; örn.IFS=$'\n' arr=($(echo 'a 1'; echo '*'; echo 'b 2')); printf "%s\n" "${arr[@]}"
Sarılmalar

@Hugues: yap, dosya adı genişletmesi hala devam ediyor. O bilgiyi biraz ekleyeceğim..thnks ..
Jahid

Üzgünüm katılmıyorum. mevcut kabukta IFS=... commanddeğişmez IFS. Ancak, IFS=... other_variable=...(herhangi bir komuta olmadan) hem değiştirir IFSve other_variablecari kabukta.
Sarılmalar

1
Teşekkürler! Bu çalışıyor; arr=( mapfile/ ile karşılaştırıldığında readarray) notasyonu sevdiğimden daha basit bir yol olmaması talihsiz bir durum .
Sarılmalar

4

Dosyadaki her satırı kolayca okuyabilir ve bir diziye atayabilirsiniz.

#!/bin/bash
i=0
while read line 
do
        arr[$i]="$line"
        i=$((i+1))
done < file.txt

1
Diziye nasıl erişirsiniz?
merhaba

4

Mapfile kullanın veya -a okuyun

Her zaman kodunuzu shellcheck kullanarak kontrol edin . Genellikle size doğru cevabı verecektir. Bu durumda SC2207 , boşlukla ayrılmış veya satırsonu ile ayrılmış değerler içeren bir dosyayı bir diziye okumayı kapsar.

Bunu yapma

array=( $(mycommand) )

Satırsonu ile ayrılmış değerlere sahip dosyalar

mapfile -t array < <(mycommand)

Boşluklarla ayrılmış değerlere sahip dosyalar

IFS=" " read -r -a array <<< "$(mycommand)"

Shellcheck sayfası size bunun neden en iyi uygulama olarak kabul edildiğinin gerekçesini verecektir.


0

Bu cevap kullanmak diyor

mapfile -t myArray < file.txt

Ben yapılan dolgu için mapfilekullanmak istediğiniz takdirde mapfileherhangi bir nedenle bash <4.x üzerinde. mapfileBash> = 4.x üzerindeyseniz mevcut komutu kullanır.

Şu anda, sadece seçenekler -dve -tiş. Ancak yukarıdaki bu komut için bu yeterli olmalıdır. Yalnızca macOS üzerinde test ettim. MacOS Sierra 10.12.6'da sistem bash'ı 3.2.57(1)-release. Böylece altlık işe yarayabilir. Ayrıca bash'inizi homebrew ile güncelleyebilir, bash'ı kendiniz inşa edebilirsiniz, vb.

Bu tekniği değişkenleri bir çağrı yığınına ayarlamak için kullanır .

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.