Bir komutun çok satırlı çıktısını içeren bir değişken var. Çıkış satırını değişkenden satır satır okumanın en etkili yolu nedir?
Örneğin:
jobs="$(jobs)"
if [ "$jobs" ]; then
# read lines from $jobs
fi
Bir komutun çok satırlı çıktısını içeren bir değişken var. Çıkış satırını değişkenden satır satır okumanın en etkili yolu nedir?
Örneğin:
jobs="$(jobs)"
if [ "$jobs" ]; then
# read lines from $jobs
fi
Yanıtlar:
Bir süre döngü işlem değiştirme ile kullanabilirsiniz:
while read -r line
do
echo "$line"
done < <(jobs)
Çok satırlı bir değişkeni okumanın en iyi yolu, boş bir IFS
değişkeni ve printf
değişkeni izleyen bir satırsonu ayarlamaktır:
# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
echo "$line"
done < <(printf '%s\n' "$var")
Not: kabuk kontrolü sc2031'e göre , bir alt kabuk oluşturmaktan [ince bir şekilde] kaçınmak için işlem alt bölümünün kullanılması bir boruya tercih edilir.
Ayrıca, değişkeni isimlendirmenin jobs
karışıklığa yol açabileceğini lütfen unutmayın, çünkü bu aynı zamanda ortak bir kabuk komutunun adıdır.
echo
için printf %s
betiğinize olmayan uysal girişli bile çalışacak böylece.
/tmp
, geçici bir çalışma dosyası oluşturabilmeye dayandığından, her zaman dizinin yazılabilir olmasını gerektirdiği belirtilmelidir . Kısıtlı bir sistemde /tmp
salt okunur olma (ve sizin tarafınızdan değiştirilemeyen) durumunuzu bulursanız, alternatif bir çözüm, örneğin printf
boru ile kullanma olasılığından memnun olacaksınız .
printf "%s\n" "$var" | while IFS= read -r line
Bir komut satırının çıktısını satır satır işlemek için ( açıklama ):
jobs |
while IFS= read -r line; do
process "$line"
done
Verileri zaten bir değişkende varsa:
printf %s "$foo" | …
printf %s "$foo"
hemen hemen aynıdır echo "$foo"
, fakat baskılar $foo
ise tam anlamıyla echo "$foo"
yorumlayabilir $foo
bir başlıyorsa yankı komutuna bir seçenek olarak -
ve de ters eğik çizgi dizileri genişleyebilir $foo
bazı kabuklarda.
Bazı kabuklarda (kül, bash, pdksh, ancak ksh veya zsh değil), bir boru hattının sağ tarafının ayrı bir işlemde çalıştığını ve döngüde ayarladığınız herhangi bir değişkenin kaybolduğunu unutmayın. Örneğin, aşağıdaki satır sayma komut dosyası bu kabuklarda 0 basar:
n=0
printf %s "$foo" |
while IFS= read -r line; do
n=$(($n + 1))
done
echo $n
Çözüm, komut dosyasının geri kalanını (veya en azından $n
döngünün değerini gerektiren kısmı ) komut listesine koymaktır :
n=0
printf %s "$foo" | {
while IFS= read -r line; do
n=$(($n + 1))
done
echo $n
}
Boş olmayan satırlara etki etmek yeterince iyi ise ve girdi çok büyük değilse, bölme kelimesini kullanabilirsiniz:
IFS='
'
set -f
for line in $(jobs); do
# process line
done
set +f
unset IFS
Açıklama: IFS
tek bir yeni satıra ayarlamak , kelime bölmenin yalnızca yeni satırlarda yapılmasını sağlar (varsayılan ayardaki herhangi bir boşluk karakterinin aksine). set -f
Komut ikame edilmesi $(jobs)
veya değişken ikame edici sonucu ortaya çıkacak olan globbing'i (yani joker karakter genişlemesi) kapatır $foo
. for
Döngüsü, tüm ilgili parçalarını hareket $(jobs)
komutu çıkışı boş olmayan tüm satırları,. Son olarak, genelleme ve IFS
ayarları geri yükleyin .
local IFS=something
. Global kapsam değerini etkilemez. IIRC, unset IFS
sizi varsayılana döndürmez (önceden varsayılan değilse kesinlikle çalışmaz).
Sorun: Eğer döngü kullanırken kullanırsanız alt kabukta çalışacak ve tüm değişkenler kaybolacak. Çözüm: döngü için kullanın
# change delimiter (IFS) to new line.
IFS_BAK=$IFS
IFS=$'\n'
for line in $variableWithSeveralLines; do
echo "$line"
# return IFS back if you need to split new line by spaces:
IFS=$IFS_BAK
IFS_BAK=
lineConvertedToArraySplittedBySpaces=( $line )
echo "{lineConvertedToArraySplittedBySpaces[0]}"
# return IFS back to newline for "for" loop
IFS_BAK=$IFS
IFS=$'\n'
done
# return delimiter to previous value
IFS=$IFS_BAK
IFS_BAK=
while read
bash içindeki bir döngüye borulamak , while döngüsünün bir alt kabukta olduğu anlamına gelir, bu nedenle değişkenler global değildir. while read;do ;done <<< "$var"
döngü gövdesini alt kabuk değil yapar. (Son bash, cmd | while
ksh'nin her zaman olduğu gibi, bir alt gövdesinin alt gövdesinde olmayan bir döngünün gövdesini koyma seçeneğine sahiptir.)
Son bash sürümlerinde, komut çıktısını dizilere verimli bir şekilde okumak için kullanın mapfile
veya readarray
kullanın
$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305
Feragatname: korkunç bir örnek, ancak kendinizden daha iyi bir komutla gelebilirsiniz.
readarray
bir fonksiyon koyun ve fonksiyonu birkaç kez çağırın.
jobs="$(jobs)"
while IFS= read
do
echo $REPLY
done <<< "$jobs"
Referanslar:
-r
bu da iyi bir fikirdir; \` interpretation... (it is in your links, but its probably worth mentioning, just to round out your
IFS = `yi engeller (boşluk kaybını önlemek için esastır)
Yeni satırla ayrılmış verileri içeren değişkenden okumak için <<< öğesini kullanabilirsiniz:
while read -r line
do
echo "A line of input: $line"
done <<<"$lines"
while IFS= read
... \ yorumlamayı önlemek istiyorsanız, o zaman kullanınread -r