Mengapa Shell Command Substitusi melahap char baris baru yang tertinggal?

25

Seperti contoh berikut, dan seperti pada pertanyaan saya baru-baru ini Di bash, kemana pergilah baris char baris baru? , Saya ingin tahu "mengapa" itu terjadi

  x="$(echo -ne "a\nb\n")" ; echo -n "$x" | xxd -p 
# Output is: 610a62 
# The trailing newline from the 'echo' command
#   has been "deleted" by Command Substitution

Saya berasumsi pasti ada beberapa alasan yang sangat signifikan untuk tindakan shell, yaitu Substitusi Perintah, untuk benar-benar menghapus beberapa data dari output perintah yang digantikannya ...
tapi saya tidak bisa mendapatkan kepalaku di sekitar yang satu ini, karena tampaknya menjadi kebalikan dari apa yang seharusnya dilakukan .. yaitu. untuk meneruskan output dari sebuah perintah kembali ke proses skrip ... Menahan satu karakter tampaknya aneh bagi saya, tapi saya kira ada alasan yang masuk akal untuk itu ... Saya ingin mengetahui apa alasannya .. .

Peter.O
sumber
Lihat juga Di mana char baris baru tertinggal dari substitusi perintah saya?
Gilles 'SANGAT berhenti menjadi jahat'

Jawaban:

22

Karena shell pada awalnya tidak dimaksudkan sebagai bahasa pemrograman penuh.

Cukup sulit untuk menghapus trailing \ndari beberapa output perintah. Namun, untuk tujuan tampilan, hampir semua perintah mengakhiri outputnya \n, jadi ... harus ada cara sederhana untuk menghapusnya ketika Anda ingin menggunakannya dalam perintah lain. Penghapusan otomatis dengan $()konstruksi adalah solusi yang dipilih.

Jadi, mungkin Anda akan menerima pertanyaan ini sebagai jawaban:

Dapatkah Anda menemukan cara sederhana untuk menghapus trailing \njika ini tidak dilakukan secara otomatis pada perintah berikut?

> echo The current date is "$(date)", have a good day!

Perhatikan bahwa kutipan diperlukan untuk mencegah hancurnya spasi ganda yang mungkin muncul pada tanggal yang diformat.

Stéphane Gimenez
sumber
1
Jika apa yang Anda katakan itu benar, maka saya akan benar-benar terpana. Jika ada shoptuntuk mengubah perilaku default yang dijelaskan, maka saya akan senang lagi ... Saya merasa sulit untuk berpikir bahwa Bash / Linux / Unix akan mencemari fungsi kritis seperti "menangkap stdout" hanya karena datetidak memiliki a dengan / tanpa opsi '\ n' ... Perbaikan yang jelas adalah bahwa bash (atau shell lain) memiliki shoptuntuk ini ... Apakah Anda tahu ada? .. dan apakah Anda memiliki tautan referensi yang berbicara tentang mengapa dan di mana penerbitan ini? ... dan mengapa tidak datememiliki opsi \ n .. Seharusnya!
Peter.O
2
Pergantian komando muncul dalam versi pertama dari cangkang Bourne dalam "edisi ke-7" Unix pada tahun 1979. Itu sudah merupakan revolusi besar dan saya kira (saya tidak dilahirkan) bahwa tidak ada yang peduli untuk tetap mengikuti '\ n' atau tidak. Ya, Anda dapat membaca halaman manual dalam waktu kurang dari 10 menit, dan sepertinya tidak ada yang berubah tentang penggantian perintah sampai saat itu. Kecuali untuk $()sintaks alternatif yang disukai .
Stéphane Gimenez
Terima kasih .. Saya pikir ini mungkin masalah warisan, dan tentu saja membentuk fakta bahwa saya sebaiknya mulai menonton Pergantian Komando saya dengan lebih hati-hati. Sampai hari ini saya pasti telah bertahan hidup di atas sayap dan berdoa .. Beberapa dari "kejadian misterius" yang saya temui mulai masuk akal sekarang :) ... Jadi dalam kasus sebaliknya dari posisi "tanggal" Anda, jika Saya ingin tetap mengikuti saya \ n, saya harus kode sesuatu seperti ini :( { echo -n "The current date is "; var="$(date; echo -e x)"; var="${var%x}"; echo -n "$var"; echo ", have a good day!"; }.. jadi itu :)
Peter.O
PS ulang komentar saya di atas: Tentu saja, hanya dateakan bekerja, tapi saya hanya menggunakan datesebagai contoh dari perintah apa pun ..
Peter.O
'Pertanyaan' Anda tentang kencan adalah janji terkuat menuju pemahaman saya tentang 'mengapa' Pergantian Komando melakukan apa yang dilakukannya, jadi ini telah menjadi jawaban 'terbaik' untuk pertanyaan saya ... dan saya tentu juga membutuhkan jawaban baik lainnya juga ..
Peter.O
19

Itu bagian dari standar :

Shell harus memperluas substitusi perintah dengan mengeksekusi perintah di lingkungan subkulit (lihat Lingkungan Eksekusi Shell ) dan mengganti subtitusi perintah (teks perintah ditambah "$ ()" atau tanda kutip kembali) dengan output standar dari perintah, menghapus urutan satu atau lebih <newline> pada akhir substitusi.

Tentu saja, standarnya mungkin ditulis dengan cara ini karena begitulah caranya kshatau sesuatu, tetapi itu adalah standar, dan itu adalah perilaku yang terdokumentasi. Jika Anda tidak menyukainya, gunakan Perl atau yang lain yang akan membuat Anda tetap mengikuti baris baru.

Steven Pritchard
sumber
Ringkasan yang bagus .. Terima kasih .. dan
tautannya
4
Standarnya seperti itu karena itulah yang dilakukan shell Bourne dan setiap shell lainnya sejak itu.
Gilles 'SANGAT berhenti menjadi jahat'
6

Yah, itu masuk akal bagi saya. Baris baru hanya ada di tempat pertama, dalam output perintah normal, sehingga prompt muncul setelah perintah selesai pada baris baru. Baris baru bukan bagian dari output asli dalam banyak kasus, itu ada di sana untuk merapikan layar. Saat Anda mem-parsing output dari sebuah perintah, baris baru di bagian akhir biasanya merepotkan. Mengapa wcperintah menghasilkan 2 baris teks? Oh, tidak, itu output yang diikuti oleh baris baru. Ketika Anda menguraikan wcAnda tidak ingin khawatir tentang fakta bahwa ada 2 baris output - tidak ada benar-benar, hanya ada satu.

EightBitTony
sumber
@EightBitTony: Pertanyaan saya sama sekali tidak terkait dengan menampilkan sesuatu di terminal .. Itu cukup mudah. Jika ada baris baru untuk ditampilkan, maka itu akan menampilkan baris baru .. Maksudnya di sini, adalah bahwa \ndalam stdoutaliran asli dihapus oleh Substitusi Perintah. yaitu. data asli saya (data yang sangat penting) telah menghapus baris baru, bahkan ketika data tersebut dibungkus dengan "tanda kutip". Saya cukup memahami bagaimana dan mengapa Pemisahan Kata berfungsi, tapi saya sama sekali tidak tahu mengapa Command Substitusi hanya melucuti baris baru dan hanya yang tertinggal .
Peter.O
1
Tidak ada yang Anda katakan mengubah pandangan saya. Lebih sering daripada tidak, baris baru akhir dalam output apa pun ada murni untuk tujuan tampilan, bukan sebagai bagian dari data.
EightBitTony
Saya setuju dengan pandangan Anda, dan tetap bersama ... Ya baris baru di akhir output adalah untuk kenyamanan ... tapi masalah saya tidak ada hubungannya dengan itu ... Saya bertanya: Mengapa Pergantian Perintah secara paksa menghapusnya ? ... Ini adalah dua masalah yang berbeda .. Mungkin pertanyaan saya seharusnya: Apakah ada solusi yang dibangun? . karena harus kode untuk masalah ini dengan menambahkan char boneka tiruan, menyebalkan! ... Aku akan melakukannya jika harus, tapi ini adalah pertama kalinya aku dihalangi oleh bash (dan aku dalam keadaan terguncang :) .. Itu sangat baik ...
Peter.O
Seperti disebutkan dalam jawaban lain, itu adalah bagian dari standar. Bagi saya, saya pikir itu seperti itu karena ketika Anda memposting data Anda hanya ingin melihat data, bukan baris baru yang nyaman. Itu karena shell mengharapkan Anda untuk menangani data dalam pipa, dan baris baru bukan data. Lagi dan kita perlu membahas ini.
EightBitTony
Terima kasih .. Saya sudah cukup mengerti sekarang. Sepertinya ini adalah kasus Substitusi Komando yang condong ke arah jatuhkan trailing newlines, daripada mempertahankannya .. Harus ada beberapa kebijaksanaan dalam gerakan yang saya belum "rasakan" "Belum, tapi saya dulu berpikir bahwa penggunaan populer semua huruf dalam nama file agak aneh / tidak perlu, tapi saya berpikir beberapa hari yang lalu itu sebenarnya sistem yang cukup nyaman .. Saya mungkin akan lihat (lebih dalam) sajak dan alasan perilaku Substitusi Komando, suatu hari ...
Peter.O
1

Saya mengalami masalah yang sama, dan menemukan contoh di bawah ini. Tampaknya mengutip membantu situasi, setidaknya itu berlaku untuk saya:

http://tldp.org/LDP/abs/html/commandsub.html .

dir_listing=`ls -l`
echo $dir_listing     # unquoted

# Expecting a nicely ordered directory listing.

# However, what you get is:
# total 3 -rw-rw-r-- 1 bozo bozo 30 May 13 17:15 1.txt -rw-rw-r-- 1 bozo
# bozo 51 May 15 20:57 t2.sh -rwxr-xr-x 1 bozo bozo 217 Mar 5 21:13 wi.sh

# The newlines disappeared.


echo "$dir_listing"   # quoted
# -rw-rw-r--    1 bozo       30 May 13 17:15 1.txt
# -rw-rw-r--    1 bozo       51 May 15 20:57 t2.sh
# -rwxr-xr-x    1 bozo      217 Mar  5 21:13 wi.sh
christian_hercules
sumber
2
Inilah yang terjadi di sini. Katakanlah Anda memiliki variabel $str,, yang berisi string "a b c". Jika Anda lulus $strke echotanpa tanda kutip ( echo $str), echoakan menerima a, bdan ctiga argumen yang terpisah. Shell Anda tidak peduli dengan spasi putih di antara argumen. echokemudian akan mencetak argumen tersebut dengan spasi yang memisahkan masing-masing, sehingga Anda dapat "a b c". Jika Anda meneruskan variabel ke echodengan tanda kutip di sekitarnya ( echo "$str"), shell melihatnya sebagai satu argumen dan echomenerimanya seperti itu, sehingga spasi putih tidak runtuh. Hal yang sama berlaku untuk baris baru.
1
Masalah yang dimiliki oleh poster asli adalah bahwa baris baru yang tertinggal (hanya yang terakhir) dari perintahnya menghilang. Kecuali melewati -nflag, echotambahkan baris baru pada outputnya, sehingga kedua gema dalam contoh Anda memiliki baris baru. Namun, jika Anda mencoba echo -n $dir_listingatau echo -n "$dir_listing", Anda akan melihat bahwa tidak ada baris baru yang tertinggal dicetak dengan atau tanpa tanda kutip $dir_listing, yang menunjukkan bahwa masalahnya adalah dengan substitusi perintah.
2
tldp adalah buruk tempat usang untuk belajar tentang scripting shell. Coba Wooliki Bash Wiki sebagai gantinya. mywiki.wooledge.org/BashGuide
Wildcard
-1

mengapa echo "hello "(tanpa tanda kutip) melahap ruang? nilai karakter IFS dilihat oleh shell sebagai pembatas, oleh karena itu, karakter trailing (yang tidak membatasi apa pun) sedang dihapus. commend substitution hanya mengeksekusi commend di sub-shell, jadi ia mengikuti logika yang sama.

Philomath
sumber
@ Philomath: Keduanya x="hello  "dan x="hello     "akan kehilangan semua spasi tambahannya jika ditampilkan melalui echo $x... Namun hanya baris baru terakhir dari Substituton Perintah yang hilang.
Peter.O
aneh, saya tidak melihat perbedaan pada bash saya (memverifikasi dengan xxd)
Philomath
Saya baru saja memeriksa ini ... Itu tidak menghapus semua baris baru ... Saya sedang bereksperimen dengan IFS ketika saya mendapat kesan itu, jadi mari kita batalkan yang satu itu :) .. tapi pertanyaannya tetap ... Ini adalah bagian mendasar pemisahan kata untuk menyingkat beberapa ruang menjadi satu ruang, dan untuk menghapus spasi spasi awal dan akhir secara total .. Saya bisa mengerti itu, tapi saya tentu tidak mengerti mengapa, dengan Command Substitusi, hanya strip \ n dan tidak semua spasi putih (seperti dengan kata splitting), dan mengapa hanya akhir trailing? .. dan di atas semua: "Substitusi Perintah" hanya sebagian menggantikan .. Mengapa?
Peter.O
4
IFStidak ada hubungannya dengan menelanjangi baris baru di akhir penggantian perintah.
Gilles 'SO- stop being evil'