Menangkap beberapa output baris ke dalam variabel Bash

583

Saya punya skrip 'myscript' yang menampilkan sebagai berikut:

abc
def
ghi

di skrip lain, saya sebut:

declare RESULT=$(./myscript)

dan $RESULTmendapat nilai

abc def ghi

Apakah ada cara untuk menyimpan hasilnya baik dengan baris baru, atau dengan karakter '\ n' sehingga saya dapat menampilkannya dengan ' echo -e'?

Parker
sumber
1
itu mengejutkan saya. Anda tidak punya $ (cat ./myscipt)? kalau tidak saya akan mengharapkannya untuk mencoba mengeksekusi perintah abc, def dan ghi
Johannes Schaub - litb
@ litb: ya, saya kira begitu; Anda juga dapat menggunakan $ (<./script) yang menghindari mengeksekusi perintah.
Jonathan Leffler
2
(NB: dua komentar di atas merujuk pada revisi dari pertanyaan yang dimulai saya punya skrip 'myscript' yang berisi yang berikut , yang mengarah ke pertanyaan. Revisi pertanyaan saat ini ( saya punya skrip ' myscript 'yang menampilkan berikut ) membuat komentar berlebihan. Namun, revisi dari 2011-11-11, lama setelah dua komentar dibuat.
Jonathan Leffler

Jawaban:

1083

Sebenarnya, HASIL berisi apa yang Anda inginkan - untuk menunjukkan:

echo "$RESULT"

Apa yang Anda perlihatkan adalah apa yang Anda dapatkan dari:

echo $RESULT

Sebagaimana dicatat dalam komentar, perbedaannya adalah bahwa (1) versi variabel yang dikutip ganda ( echo "$RESULT") menjaga jarak internal dari nilai persis seperti yang diwakili dalam variabel - baris baru, tab, beberapa kosong, dan semua - sedangkan (2 ) versi tanpa tanda kutip ( echo $RESULT) menggantikan setiap urutan satu atau lebih kosong, tab, dan baris baru dengan satu spasi. Jadi (1) mempertahankan bentuk variabel input, sedangkan (2) menciptakan garis output tunggal yang berpotensi sangat panjang dengan 'kata-kata' yang dipisahkan oleh spasi tunggal (di mana 'kata' adalah urutan karakter non-spasi putih; diperlukan dapat berupa alfanumerik apa pun dari kata-kata ini).

Jonathan Leffler
sumber
69
@troelskn: perbedaannya adalah bahwa (1) versi kutip ganda dari variabel menjaga jarak internal dari nilai persis seperti yang diwakili dalam variabel, baris baru, tab, beberapa kosong dan semua, sedangkan (2) versi yang tidak dikutip menggantikan setiap urutan satu atau lebih kosong, tab, dan baris baru dengan satu spasi. Jadi (1) mempertahankan bentuk variabel input, sedangkan (2) menciptakan garis output tunggal yang berpotensi sangat panjang dengan 'kata-kata' yang dipisahkan oleh spasi tunggal (di mana 'kata' adalah urutan karakter non-spasi putih; diperlukan dapat berupa alfanumerik apa pun dari kata-kata ini).
Jonathan Leffler
23
Untuk membuat jawaban lebih mudah dipahami: jawabannya memberitahu bahwa gema "$ HASIL" mempertahankan baris baru, sedangkan gema $ HASIL tidak.
Yu Shen
Ini gagal mempertahankan baris baru dan ruang terdepan dalam beberapa situasi.
CommaToast
3
@ ComaToast: Apakah Anda akan menguraikan itu? Baris baru yang tertinggal hilang; tidak ada cara mudah untuk itu. Memimpin kosong - Saya tidak mengetahui adanya keadaan di mana mereka hilang.
Jonathan Leffler
90

Perangkap lain dengan ini adalah bahwa substitusi perintah - $()- strip mengikuti baris baru. Mungkin tidak selalu penting, tetapi jika Anda benar-benar ingin melestarikan persis apa yang output, Anda harus menggunakan jalur lain dan beberapa mengutip:

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

Ini sangat penting jika Anda ingin menangani semua nama file yang mungkin (untuk menghindari perilaku yang tidak terdefinisi seperti beroperasi pada file yang salah).

l0b0
sumber
4
Saya harus bekerja untuk sementara waktu dengan shell yang rusak yang tidak menghapus baris baru terakhir dari substitusi perintah (itu bukan proses substitusi ), dan itu menghancurkan hampir semuanya. Misalnya, jika Anda melakukannya pwd=`pwd`; ls $pwd/$file, Anda mendapat baris baru sebelum /, dan menyertakan nama dalam tanda kutip ganda tidak membantu. Itu diperbaiki dengan cepat. Ini kembali pada kerangka waktu 1983-5 di ICL Perq PNX; shell tidak memiliki $PWDvariabel bawaan.
Jonathan Leffler
19

Jika Anda tertarik pada baris tertentu, gunakan array hasil:

declare RESULT=($(./myscript))  # (..) = array
echo "First line: ${RESULT[0]}"
echo "Second line: ${RESULT[1]}"
echo "N-th line: ${RESULT[N]}"
pengguna2574210
sumber
3
Jika ada spasi di baris, ini akan menghitung bidang (konten antar spasi) daripada garis.
Liam
2
Anda akan menggunakan readarraydan proses substitusi bukan substitusi perintah: readarray -t RESULT < <(./myscript>.
chepner
15

Selain jawaban yang diberikan oleh @ l0b0 saya hanya punya situasi di mana saya perlu untuk menjaga setiap garis akhir output baris oleh skrip dan memeriksa kode pengembalian skrip. Dan masalah dengan jawaban l0b0 adalah 'echo x' menyetel ulang $? kembali ke nol ... jadi saya berhasil menemukan solusi yang sangat licik ini:

RESULTX="$(./myscript; echo x$?)"
RETURNCODE=${RESULTX##*x}
RESULT="${RESULTX%x*}"
Lurchman
sumber
Ini bagus, saya benar-benar memiliki use case di mana saya ingin memiliki die ()fungsi yang menerima kode keluar yang sewenang-wenang dan opsional pesan, dan saya ingin menggunakan usage ()fungsi lain untuk memasok pesan tetapi baris baru terus terjepit, mudah-mudahan ini memungkinkan saya mengatasi itu.
dragon788
1

Setelah mencoba sebagian besar solusi di sini, hal termudah yang saya temukan adalah yang jelas - menggunakan file temp. Saya tidak yakin apa yang ingin Anda lakukan dengan beberapa output baris Anda, tetapi Anda kemudian dapat mengatasinya baris demi baris menggunakan baca. Satu-satunya hal yang tidak dapat Anda lakukan adalah dengan mudah memasukkan semuanya ke dalam variabel yang sama, tetapi untuk sebagian besar tujuan praktis ini lebih mudah untuk ditangani.

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

Retas cepat untuk membuatnya melakukan tindakan yang diminta:

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

Perhatikan ini menambahkan garis ekstra. Jika Anda mengerjakannya, Anda dapat membuat kode di sekitarnya, saya terlalu malas.


EDIT: Meskipun kasing ini berfungsi dengan baik, orang yang membaca ini harus sadar bahwa Anda dapat dengan mudah menekan stdin di dalam loop while, sehingga memberi Anda skrip yang akan menjalankan satu baris, menghapus stdin, dan keluar. Seperti ssh akan melakukan itu menurut saya? Saya baru saja melihatnya, contoh kode lainnya di sini: /unix/24260/reading-lines-from-a-file-with-bash-for-vs- Sementara

Sekali lagi! Kali ini dengan filehandle yang berbeda (stdin, stdout, stderr adalah 0-2, jadi kita bisa menggunakan & 3 atau lebih tinggi di bash).

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

Anda juga dapat menggunakan mktemp, tetapi ini hanyalah contoh kode cepat. Penggunaan untuk mktemp terlihat seperti:

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

Kemudian gunakan $ filenamevar seperti Anda akan nama file yang sebenarnya. Mungkin tidak perlu dijelaskan di sini tetapi seseorang mengeluh dalam komentar.

pengguna1279741
sumber
Saya mencoba solusi lain juga, dengan saran pertama Anda, akhirnya naskah saya berfungsi
Kar.ma
1
Downvote: Ini terlalu rumit, dan gagal menghindari banyak bashperangkap umum .
tripleee
Ya seseorang mengatakan kepada saya tentang masalah filehandle stdin aneh tempo hari dan saya seperti "wow". Biar saya tambahkan sesuatu dengan sangat cepat.
user1279741
Di Bash, Anda dapat menggunakan readekstensi perintah -u 3untuk membaca dari deskriptor file 3.
Jonathan Leffler
Mengapa tidak sementara ... lakukan ... selesai <<(.
Myscript.sh
0

Bagaimana dengan ini, ia akan membaca setiap baris ke variabel dan yang dapat digunakan selanjutnya! katakanlah output myscript diarahkan ke file bernama myscript_output

awk '{while ( (getline var < "myscript_output") >0){print var;} close ("myscript_output");}'
Rahul Reddy
sumber
4
Yah, itu bukan bash, itu aneh.
vadipp