Bagaimana saya bisa membuat operator backtick bash menjaga baris baru dalam output?

36

Apakah ada cara untuk membuat bash tidak makan baris baru di substitusi backtick?

Sebagai contoh:

var=`echo line one && echo line two`
echo $var

line one line two

Yang saya inginkan adalah

var=`echo line one && echo line two` # plus some magic
echo $var

line one
line two
slowpoison
sumber

Jawaban:

54

Ini bukan masalah dengan substitusi backticks, tetapi dengan echo; Anda harus mengutip variabel untuk membuat karakter kontrol berfungsi:

$ var=`echo line one && echo line two`
$ echo "$var"
line one
line two
cYrus
sumber
Jadi, bash menghapus baris baru sambil memperluas variabel pada command-lien? Dan perilaku itu dapat dinonaktifkan dengan mengutip variabel?
slowpoison
7
bashekspansi Melakukan parameter sebelum mengeksekusi perintah, mengutip diperlukan untuk menginformasikan bashbahwa Anda ingin mencetak string tunggal dan tidak 4 kata-kata ( line, one, linedan two). Coba: echo a <many spaces> bdan echo "a <many spaces> b". Lihatlah jawaban ini juga.
cYrus
5

Meskipun @cyrus benar - sebenarnya tidak menjawab seluruh pertanyaan, dan tidak ada penjelasan tentang apa yang terjadi.

Jadi mari kita berjalan melewatinya.

Baris baru dalam string

Pertama tentukan urutan byte yang diharapkan:

$ { echo a; echo b; } | xxd
0000000: 610a 620a                                a.b.

Sekarang, gunakan Substitusi Perintah (Bagian 3.5.4) untuk mencoba menangkap urutan byte ini:

$ x=$( echo a; echo b; )

Kemudian, lakukan gema sederhana untuk memverifikasi urutan byte:

$ echo $x | xxd
0000000: 6120 620a                                a b.

Jadi sepertinya baris baru pertama diganti dengan spasi, dan baris baru kedua dibiarkan utuh. Tapi kenapa ?

Mari kita lihat apa yang sebenarnya terjadi di sini:

Pertama, bash akan melakukan Shell Parameter Expansion (Bagian 3.5.3)

Karakter '$' memperkenalkan ekspansi parameter, substitusi perintah, atau ekspansi aritmatika. Nama parameter atau simbol yang akan diperluas dapat dilampirkan dalam kurung kurawal, yang merupakan opsional tetapi berfungsi untuk melindungi variabel agar diperluas dari karakter segera setelah itu yang dapat diartikan sebagai bagian dari nama.

Kemudian bash akan melakukan Word Splitting (Bagian 3.5.7)

Shell memindai hasil ekspansi parameter, penggantian perintah, dan ekspansi aritmatika yang tidak terjadi dalam tanda kutip ganda untuk pemisahan kata.

Shell memperlakukan setiap karakter $ IFS sebagai pembatas, dan membagi hasil ekspansi lainnya menjadi kata-kata pada karakter ini. Jika IFS tidak disetel, atau nilainya tepat, ...

Selanjutnya, bash akan memperlakukannya sebagai Perintah Sederhana (Bagian 3.2.1)

Perintah sederhana adalah jenis perintah yang paling sering ditemui. Itu hanya urutan kata-kata yang dipisahkan oleh kosong, diakhiri oleh salah satu operator kontrol shell (lihat Definisi). Kata pertama umumnya menentukan perintah yang akan dieksekusi, dengan sisa kata-kata yang menjadi argumen perintah itu.

Definisi kosong (Bagian 2 - Definisi)

blank Karakter spasi atau tab.

Akhirnya, bash memanggil perintah internal echo (Bagian 4.2 - Bash Builtin)

... Keluarkan argumen, dipisahkan oleh spasi, diakhiri dengan baris baru. ...

Jadi untuk meringkas, baris baru dihapus oleh Word Splitting, dan kemudian gema mendapat 2 argumen, "a", dan "b", dan kemudian output mereka dipisahkan oleh spasi dan diakhiri dengan baris baru.

Melakukan apa yang disarankan @cyrus (dan menekan baris baru dari echo dengan -n) hasilnya lebih baik:

$ echo -n "$x" | xxd
0000000: 610a 62                                  a.b

Baris baru di akhir string

Ini masih belum sempurna, baris baru yang tertinggal hilang. Melihat lebih dekat pada Substitusi Perintah (Bagian 3.5.4) :

Bash melakukan ekspansi dengan mengeksekusi perintah dan mengganti substitusi perintah dengan output standar dari perintah, dengan setiap baris baru yang tertinggal dihapus.

Sekarang sudah jelas mengapa baris baru hilang, bash bisa dibodohi untuk menjaganya. Untuk melakukan ini, tambahkan string tambahan ke bagian akhir, dan hapus ketika menggunakan variabel:

$ x=$( echo a; echo b; echo -n extra )
$ echo -n "${x%extra}" | xxd
0000000: 610a 620a                                a.b.

TL; DR

Tambahkan bagian ekstra ke akhir output, dan kutip variabel:

$ cat /no/file/here 2>&1 | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ cat /no/file/here 2>&1 | cksum
3561909523 46
$ 
$ var=$( cat /no/file/here 2>&1; rc=${?}; echo extra; exit ${rc})
$ echo $?
1
$ 
$ echo -n "${var%extra}" | xxd
0000000: 6361 743a 202f 6e6f 2f66 696c 652f 6865  cat: /no/file/he
0000010: 7265 3a20 4e6f 2073 7563 6820 6669 6c65  re: No such file
0000020: 206f 7220 6469 7265 6374 6f72 790a        or directory.
$ echo -n "${var%extra}" | cksum
3561909523 46
Iwan Aucamp
sumber
0

Saya akan menambahkan tambahan kecil saya di komentar pada jawaban cYrus, tapi saya belum bisa mengomentari jawaban yang saya pikir. Jadi saya akan menduplikasi sebagian cYrus jawabannya untuk kelengkapan.

Apa yang terjadi di baris pertama kode Anda adalah bahwa memang substitusi backticks memakan baris baru yang tertinggal dari output dari perintah yang diganti, seperti yang didokumentasikan . Tapi itu tidak memakan garis baru di antara dua baris Anda.

Apa yang terjadi pada baris kode kedua adalah bahwa echo dieksekusi dengan dua argumen, baris pertama dan kedua. Mengutip variabel yang diperluas akan memperbaikinya: echo "$var"Ini akan mempertahankan baris baru antara baris satu dan baris dua. Dan echoakan menambahkan trailing newline secara default sehingga semuanya baik-baik saja.

Sekarang jika Anda ingin mempertahankan semua baris baru yang tertinggal di output dari beberapa substitusi perintah, solusinya adalah menambahkan baris lain pada output, yang dapat Anda hapus setelah substitusi perintah, lihat di sini .

gaston
sumber