Di mana char baris baru tertinggal dari substitusi perintah saya?

15

Kode berikut paling menggambarkan situasi. Mengapa baris terakhir tidak menghasilkan char baris baru yang tertinggal? Output setiap baris ditampilkan dalam komentar. Saya menggunakan bash GNU, versi 4.1.5

     echo -n $'a\nb\n'                  | xxd -p  # 610a620a  
           x=$'a\nb\n'   ; echo -n "$x" | xxd -p  # 610a620a
     echo -ne "a\nb\n"                  | xxd -p  # 610a620a
x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p  # 610a62
Peter.O
sumber
4
Penanganan untuk saat-saat langka ketika dibutuhkan:tmp=$(somecommand; echo a); tmp=${tmp%a}
Gilles 'SO- stop being evil'
Lihat juga Mengapa Substitusi Perintah shell melahap char baris baru yang tertinggal?
Gilles 'SANGAT berhenti menjadi jahat'
1
@Gilles: Berikan kembali contoh Anda, di atas: tmp=$(somecommand; echo a)... Ini sudah pasti mengarahkan poinnya ke rumah ... Sampai saya melihat contohnya, kecenderungan saya masih akan digunakan echo -n a... tapi, tentu saja !, tidak perlu itu -n, karena Substitusi Perintah akan menghapus baris tambahan yang diperkenalkan di awal! ... terima kasih ...
Peter.O
stackoverflow.com/questions/613572/…
sancho.s Reinstate Monica

Jawaban:

18

Fungsi substitusi perintah $()(dan sepupunya pada backtick) secara khusus menghapus baris baru. Ini adalah perilaku yang didokumentasikan , dan Anda harus selalu menyadarinya saat menggunakan konstruk.

Baris baru di dalam badan teks tidak dihapus oleh operator substitusi, tetapi baris tersebut juga dapat dihapus ketika melakukan pemisahan kata pada shell, jadi bagaimana hasilnya tergantung pada apakah Anda menggunakan tanda kutip atau tidak. Perhatikan perbedaan antara kedua penggunaan ini:

$ echo -n "$(echo -n 'a\nb')"
a
b

$ !! | xxd -p
610a62

$ echo -n  $(echo -n 'a\nb')
a b

$ !! | xxd -p   
612062

Dalam contoh kedua, output tidak dikutip dan baris baru ditafsirkan sebagai pemisahan kata, sehingga ditampilkan dalam output sebagai spasi!

Caleb
sumber
1
Terima kasih Caleb .. Saya menyadari kata-membelah mengubah spasi untuk satu ruang ketika kuotasi ... Itulah sebabnya saya paling terkejut melihat baris Trailing saya hilang meskipun saya telah dikutip ... Sekarang aku sadar bahwa itu karena perilaku 'normal' Substitusi Komando menjatuhkan baris baru ... Oh well, c'est la vie .. dan terima kasih untuk tautannya
Peter.O
6

Saat menggunakan substitusi perintah, shell mengeksekusi perintah dalam subshell, mengembalikan stdout mereka. dalam proses ini, karakter IFS kehilangan signifikansinya (jika tidak dikutip), karena commend mengembalikan kata-kata split, jadi yang trailing dihapus. Sebagai contoh:

$ echo "$(echo -e '\n')" | wc
 1       0       1

$ echo -e '\n' | wc
2       0       2

dan lebih praktisnya, pwdakan berfungsi bahkan jika nama direktori Anda memiliki baris baru di antaranya, tetapi $(pwd)tidak akan.

Solusi yang biasa adalah menambahkan sesuatu di akhir perintah Anda dan menghapusnya setelahnya.

Philomath
sumber
1
Terima kasih Philomath ... ngomong-ngomong, contoh pertama Anda secara sintaksis salah ('wc' tidak menerima string sebagai arg). Bagi Anda contoh yang masuk akal, yang pertama bisa jadi echo "$(echo -e '\n')" | wc, yang menghasilkan 1   0   1, dibandingkan dengan2   0   2
Peter.O
@ Fred: Ups, hanya salah ketik.
Philomath
1
"dikonversi menjadi spasi" bukan penjelasan yang tepat, hanya ada beberapa pemisahan yang terlibat. Secara khusus Anda dapat menghapus ruang dari IFS dan itu tidak akan bertindak sebagai pemisah. Terlebih lagi, contoh Anda termasuk dalam kategori kasus khusus, dan ada perbedaan antara $(pwd)dan "$(pwd)", lihat jawaban Caleb.
Stéphane Gimenez
NB .. Saran Anda untuk solusi yang biasa adalah hanya apa yang saya butuhkan untuk membersihkan yang ini ..
Peter.O
Re "dikonversi menjadi spasi", lihat Word Splitting
Peter.O