Baris baru dalam variabel bash

9

Saya mencoba untuk menyimpan beberapa baris dalam variabel bash, tetapi sepertinya tidak berhasil.

Misalnya, jika saya mencantumkan /binsatu file per baris dan menyimpannya $LS, maka saya meneruskan $LSsebagai stdin wc, selalu mengembalikan 1:

$ ls -1 /bin | wc -l
134
$ LS=$(ls -1 /bin); wc -l <<< $LS
1

Jika saya mencoba menampilkan ke layar, saya mendapatkan berbagai hasil: echomencetak semua baris pada satu baris, sementara printfhanya mencetak baris pertama:

#!/bin/bash
LS=$(ls -1 /bin)
echo $LS
printf $LS

Jadi, variabel bash dapat berisi banyak baris?

Wizard79
sumber

Jawaban:

15

Anda perlu menggandakan penawaran (dan Anda harus menggandakan variabel penawaran dalam banyak kasus):

echo "$LS"

Tapi jangan gunakan gema untuk mencetak konten variabel, gunakan printf sebagai gantinya :

printf '%s\n' "$LS"
cuonglm
sumber
1
Hanya untuk memberikan poin yang lebih baik pada jawaban keseluruhan: printf mengharapkan string format menjadi parameter pertamanya, itulah sebabnya mereka tidak melihat apa yang mereka harapkan. Jika mereka ingin melihat baris baru dalam output dari printf, terpisah secara spasial, printf "%s\n" $LSakan melakukannya.
Jeff Schaller
% LS harusnya $ LS, btw (saya tidak bisa mengedit sesingkat itu)
Jeff Schaller
Terima kasih! Ini tentu saja berfungsi juga dengan wcdan perintah lainnya:wc -l <<< "$LS"
Wizard79
1
@JeffSchaller: Tidak, Anda tidak boleh tanda kutip variabel dalam hal ini, hanya menggunakan printf '%s\n' "$LS"jika Anda ingin melihat baris baru. Itu salah ketik, perbaiki!
cuonglm
Mengapa Anda tidak harus menggunakan echo untuk mencetak variabel?
123
9

Baris baru dalam variabel. LS=$(ls -1)set variabel LSke output dari ls -1(yang menghasilkan output yang sama seperti ls, omong-omong, kecuali ketika output pergi ke terminal), dikurangi trailing newlines.

Masalahnya adalah Anda menghapus baris baru saat Anda mencetak nilainya. Dalam skrip shell, $LStidak berarti "nilai variabel LS", itu berarti "mengambil nilai LS, membaginya menjadi kata sesuai dengan IFSdan menafsirkan setiap kata sebagai pola gumpalan". Untuk mendapatkan nilai LS, Anda perlu menulis "$LS", atau lebih umum untuk menempatkan $LSantara tanda kutip ganda.

echo "$LS"mencetak nilai LS, kecuali dalam beberapa shell yang menginterpretasikan karakter backslash, dan kecuali untuk beberapa nilai yang dimulai dengan -.

printf "$LS"mencetak nilai LSasalkan tidak mengandung persen atau karakter backslash dan (dengan sebagian besar implementasi) tidak dimulai dengan -.

Untuk mencetak nilai LStepatnya, gunakan printf %s "$LS". Jika Anda ingin baris baru di akhir, gunakan printf '%s\n' "$LS".

Perhatikan bahwa $(ls)secara umum tidak ada daftar file dalam direktori saat ini. Ini hanya berfungsi ketika Anda memiliki nama file yang cukup jinak. Untuk mendapatkan daftar nama file (kecuali file dot), Anda perlu menggunakan wildcard: *. Hasilnya adalah daftar string, bukan string, sehingga Anda tidak dapat menetapkannya ke variabel string; Anda bisa menggunakan variabel array files=(*)di shell yang mendukungnya (ksh93, bash, zsh).

Untuk informasi lebih lanjut, lihat Mengapa skrip shell saya tercekik di spasi putih atau karakter khusus lainnya?

Gilles 'SANGAT berhenti menjadi jahat'
sumber
printf "$ LS" juga akan menyerah pada karakter backslash parsing, mirip dengan echo
cnst
0

Yang disebutkan di atas

printf '%s\n' "$LS"

adalah satu-satunya solusi yang benar.

Biarkan saya menunjukkan bahwa solusi yang diusulkan lainnya, baik dengan echodan printf, tidak berfungsi dengan baik:

$ mkdir t
$ cd t
$ touch \\n
$ LS=$(ls -l)
$ echo "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf "$LS\n"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12

$ printf '%s\n' "$LS"
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ ls -l
total 0
-rw-r--r--  1 domain  domain  0 Nov  5 06:12 \n
$ rm \\n
$ cd ..
$ rmdir t
$

(Satu juga dapat menguji dengan V=$(printf 'line0:\\n\\n\nline1.\n') printf '%s\n' "$V"dan variasi.)

Sedangkan \ndalam nama file mungkin tidak umum, simpan git diffke dalam variabel, dan peluang Anda untuk menemukan \npeningkatan literal secara dramatis.

cnst
sumber
Catat itu kshdan zshjuga mendukung print -r -- "$LS"dan zshjuga memiliki echo -E - $LS. cshmemiliki echo $LS:qmeskipun itu tidak menampilkan karakter baris baru jika $ LS kosong, dan mendapatkan nilai multiline menjadi $ LS di tempat pertama cukup rumit di csh.
Stéphane Chazelas