Bir Bash değişkenine birden çok hat çıkışı yakalama


583

Aşağıdaki çıktıları bir komut dosyası 'myscript' var:

abc
def
ghi

başka bir senaryoda, ben çağırıyorum:

declare RESULT=$(./myscript)

ve $RESULTdeğeri alır

abc def ghi

Sonucu ya yeni satırlarla ya da '\ n' karakteriyle saklamanın bir yolu var mı echo -e?


1
beni şaşırtıyor. $ (cat ./myscipt) yok mu? Aksi takdirde abc, def ve ghi komutlarını yürütmeyi denemeyi beklerdim
Johannes Schaub - litb 5:09

@litb: evet, sanırım; bir komutu yürütmekten kaçınmak için $ (<./ myscript) kullanabilirsiniz.
Jonathan Leffler

2
(Not: yukarıdaki iki yorum, başlatılan sorunun revizyonuna atıfta bulunur. Aşağıdakileri içeren bir senaryo 'myscript'im var , bu da sorulara yol açtı. Sorunun şu anki revizyonu ( bir senaryom var') myscript 'aşağıdakileri çıkarır ) yorumları gereksiz kılar.Ancak revizyon, iki yorum yapıldıktan çok sonra 2011-11-11 arasındadır.
Jonathan Leffler

Yanıtlar:


1081

Aslında, RESULT istediğini içerir - göstermek için:

echo "$RESULT"

Gösterdiğiniz şey, aldığınız şeydir:

echo $RESULT

Yorumlarda belirtildiği gibi, fark (1) değişkenin çift tırnaklı versiyonunun ( echo "$RESULT") tam olarak değişkente temsil edildiği gibi değerin iç aralığını koruduğu - yeni satırlar, sekmeler, çoklu boşluklar ve hepsi - 2 ) tırnaksız sürüm ( echo $RESULT), bir veya daha fazla boşluk, sekme ve yeni satırın her bir sırasını tek bir boşlukla değiştirir. Böylece (1) giriş değişkeninin şeklini korur, (2) potansiyel olarak çok uzun tek bir çıktı satırı oluşturur ve burada 'kelimeler' tek boşluklarla ayrılır (burada 'kelime' boşluk olmayan karakterlerin bir dizisidir; kelimelerin hiçbirinde alfasayısal olamaz).


69
@troelskn: fark şudur: (1) değişkenin çift tırnaklı versiyonu, değerin tam olarak değişkente, satırlarda, sekmelerde, çoklu boşluklarda ve hepsinde temsil edildiği gibi iç aralığını korur, (2) tırnaksız sürümün yerini alır bir veya daha fazla boşluk, sekme ve yeni satırın her sekansı tek bir boşlukla. Böylece (1) giriş değişkeninin şeklini korur, (2) potansiyel olarak çok uzun tek bir çıktı satırı oluşturur ve burada 'kelimeler' tek boşluklarla ayrılır (burada 'kelime' boşluk olmayan karakterlerin bir dizisidir; kelimelerin hiçbirinde alfasayısal olamaz).
Jonathan Leffler

23
Cevabı daha kolay anlaşılır kılmak için: yankı, "$ RESULT" yankısının satırsonu koruduğunu söylerken yankı $ RESULT değildir.
Yu Shen

Bu, bazı durumlarda yeni satırları ve önde gelen alanları koruyamaz.
CommaToast

3
@CommaToast: Bununla ilgili ayrıntıya girecek misiniz? Sondaki yeni satırlar kaybolur; bunun kolay bir yolu yok. Önde gelen boşluklar - Kaybettikleri koşulların farkında değilim.
Jonathan Leffler


90

Bununla ilgili bir başka tuzak, komut ikamesinin - $()- sondaki satırları kaldırmasıdır. Muhtemelen her zaman önemli değildir, ancak gerçekten çıktıyı tam olarak korumak istiyorsanız , başka bir satır ve bazı alıntılar kullanmanız gerekir:

RESULTX="$(./myscript; echo x)"
RESULT="${RESULTX%x}"

Bu, tüm olası dosya adlarını işlemek istiyorsanız (yanlış dosya üzerinde çalışmak gibi tanımlanmamış davranışlardan kaçınmak için) özellikle önemlidir .


4
Komut ikamesinden son satırsonunu kaldırmayan kırık bir kabukla çalışmak zorunda kaldım ( süreç ikamesi değil ) ve neredeyse her şeyi kırdı. Örneğin , daha önce bir satırsonu buldunuz ve adı çift tırnak işaretleri arasına almak yardımcı olmadı. Hızlı bir şekilde düzeltildi. Bu 1983-5 zaman diliminde ICL Perq PNX'teydi; kabuğun yerleşik bir değişkeni yoktu . pwd=`pwd`; ls $pwd/$file/$PWD
Jonathan Leffler

19

Belirli satırlarla ilgileniyorsanız, bir sonuç dizisi kullanın:

declare RESULT=($(./myscript))  # (..) = array
echo "First line: ${RESULT[0]}"
echo "Second line: ${RESULT[1]}"
echo "N-th line: ${RESULT[N]}"

3
Satırlarda boşluk varsa, bu satırlar yerine alanları (boşluklar arasındaki içerikler) sayar.
Liam

2
Sen kullanmak istiyorsunuz readarrayyerine komut ikamesi ve süreç ikamesini: readarray -t RESULT < <(./myscript>.
Chepner

15

Tarafından verilen cevaba ek olarak @ l0b0 Ben sadece ikisi için gereken durum komut dosyası tarafından herhangi izleyen yeni satırlar çıktı tutmak vardı ve komut dosyasının dönüş kodunu kontrol edin. Ve l0b0'ın cevabı ile ilgili sorun 'echo x' $ 'ı sıfırlıyor mu? sıfıra geri döndüm ... bu yüzden bu kurnaz çözümü bulmayı başardım:

RESULTX="$(./myscript; echo x$?)"
RETURNCODE=${RESULTX##*x}
RESULT="${RESULTX%x*}"

Bu güzel, aslında die ()keyfi bir çıkış kodunu ve isteğe bağlı olarak bir mesajı kabul eden bir işleve sahip olmak istediğim kullanım durumu vardı ve mesajı sağlamak için başka bir usage ()işlev kullanmak istedim ama yeni satırlar ezilmeye devam etti, umarım bu bana izin verir bunun etrafında çalışın.
dragon788

1

Buradaki çözümlerin çoğunu denedikten sonra bulduğum en kolay şey, geçici bir dosya kullanmaktı. Birden çok satır çıkışınızla ne yapmak istediğinizden emin değilim, ancak daha sonra read kullanarak satır satır başa çıkabilirsiniz. Gerçekten yapamayacağınız tek şey, hepsini aynı değişkene kolayca yapıştırmaktır, ancak çoğu pratik amaç için bu başa çıkmak çok daha kolaydır.

./myscript.sh > /tmp/foo
while read line ; do 
    echo 'whatever you want to do with $line'
done < /tmp/foo

İstenen eylemi gerçekleştirmek için hızlı kesmek:

result=""
./myscript.sh > /tmp/foo
while read line ; do
  result="$result$line\n"
done < /tmp/foo
echo -e $result

Bunun fazladan bir satır eklediğini unutmayın. Eğer üzerinde çalışırsanız, kod yazabilirsiniz, ben çok tembelim.


EDIT: Bu durumda mükemmel çalışıyor olsa da, bunu okuyan insanlar while döngüsünde stdin kolayca ezmek, böylece size bir satır çalıştıracak, stdin temizlemek ve çıkış bir komut dosyası verebilir farkında olmalıdır. Sanırım ssh bunu yapacak mı? Kısa süre önce gördüm, diğer kod örnekleri burada: /unix/24260/reading-lines-from-a-file-with-bash-for-vs-while

Bir kere daha! Bu kez farklı bir dosya sapı ile (stdin, stdout, stderr 0-2'dir, bu yüzden bash & 3 veya daha yüksek bir değer kullanabiliriz).

result=""
./test>/tmp/foo
while read line  <&3; do
    result="$result$line\n"
done 3</tmp/foo
echo -e $result

mktemp de kullanabilirsiniz, ancak bu sadece hızlı bir kod örneğidir. Mktemp kullanımı şöyle:

filenamevar=`mktemp /tmp/tempXXXXXX`
./test > $filenamevar

Sonra $ filenamevar'ı bir dosyanın gerçek adı gibi kullanın. Muhtemelen burada açıklanması gerekmez, ancak birisi yorumlarda şikayet etti.


Diğer çözümleri de denedim, ilk önerinizle nihayet senaryomu çalıştırdım
Kar.ma

1
Downvote: Bu aşırı karmaşık ve birden çok ortak önlemek için başarısız bashtuzaklara .
üçlü

Ya birisi bana geçen gün garip stdin dosya tutma probleminden bahsetti ve ben "vay" gibiydim. Lemme gerçekten çok çabuk bir şeyler ekliyor.
user1279741 13:17

Bash'te, dosya tanımlayıcı 3'ten okumak için readkomut uzantısını -u 3kullanabilirsiniz.
Jonathan Leffler

Neden olmasın ... do ... yapılır <<(.
Myscript.sh

0

Buna ne dersin, her satırı bir değişkene okuyacak ve daha sonra kullanılabilecek! myscript çıktısının myscript_output adlı bir dosyaya yönlendirildiğini söyleyin

awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'

4
Eh, bu bash değil, bu garip.
vadipp
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.