Bagaimana cara menghasilkan string multiline di Bash?

250

Bagaimana saya bisa menghasilkan string multi baris dalam Bash tanpa menggunakan beberapa panggilan gema seperti:

echo "usage: up [--level <n>| -n <levels>][--help][--version]"
echo 
echo "Report bugs to: "
echo "up home page: "

Saya mencari cara portabel untuk melakukan ini, hanya menggunakan Bash builtins.

metode helpermet
sumber
4
Jika Anda mengeluarkan pesan penggunaan sebagai tanggapan atas permintaan yang salah, Anda biasanya akan mengirim pesan itu ke kesalahan standar alih-alih output standar, denganecho >&2 ...
Mark Reed
2
@ MarkReed Pesan penggunaan adalah output dengan mengetik --help(yang harus pergi ke standar keluar).
helpermethodhod
Untuk orang lain yang datang, info lebih lanjut tentang "dokumen di sini" tersedia: tldp.org/LDP/abs/html/here-docs.html
Jeffrey Martinez
Periksa printfsolusi berbasis dari Gordon Davidson. Meskipun berada dalam bayang-bayang pendekatan berbasis echoatau cat, tampaknya jauh lebih sedikit dari lumpur. Diakui sintaks `printf 'mewakili sedikit kurva pembelajaran, tetapi saya ingin mendengarkan kelemahan lainnya (kompatibilitas?, Kinerja? ...)
mjv
Terkait: stackoverflow.com/questions/23929235/…
Anton Tarasenko

Jawaban:

296

Di sini dokumen sering digunakan untuk tujuan ini.

cat << EOF
usage: up [--level <n>| -n <levels>][--help][--version]

Report bugs to: 
up home page:
EOF

Mereka didukung di semua shell yang diturunkan dari Bourne termasuk semua versi Bash.

Dijeda sampai pemberitahuan lebih lanjut.
sumber
4
Yup - tetapi catbukan built-in.
Mark Reed
8
@ MarkReed: Itu benar, tetapi selalu tersedia (kecuali mungkin dalam keadaan yang tidak biasa).
Dijeda sampai pemberitahuan lebih lanjut.
6
+1 Terima kasih. Saya akhirnya menggunakan read -d '' help <<- EOF ...untuk membaca string multiline menjadi variabel dan kemudian menggemakan hasilnya.
helpermethodhod
3
dapatkah saya menyimpan HEREDOC ke suatu variabel?
chovy
177

atau Anda dapat melakukan ini:

echo "usage: up [--level <n>| -n <levels>][--help][--version]

Report bugs to: 
up home page: "
Chris Mohr
sumber
1
@OliverWeiler: Ia bahkan akan bekerja di shell Bourne seperti Dash dan Heirloom Bourne Shell .
Dijeda sampai pemberitahuan lebih lanjut.
6
Tidak hebat jika Anda membutuhkan ini dalam suatu fungsi karena Anda harus 1) mengalahkan string sampai ke kiri file Anda atau 2) menjaganya agar tetap sejajar dengan sisa kode Anda tetapi kemudian dicetak dengan indentasi juga
sg
43

Terinspirasi oleh jawaban mendalam pada halaman ini, saya menciptakan pendekatan campuran, yang saya anggap paling sederhana dan lebih fleksibel. Bagaimana menurut anda?

Pertama, saya mendefinisikan penggunaan dalam suatu variabel, yang memungkinkan saya untuk menggunakannya kembali dalam konteks yang berbeda. Formatnya sangat sederhana, hampir WYSIWYG, tanpa perlu menambahkan karakter kontrol. Ini tampaknya cukup portabel untuk saya (saya menjalankannya di MacOS dan Ubuntu)

__usage="
Usage: $(basename $0) [OPTIONS]

Options:
  -l, --level <n>              Something something something level
  -n, --nnnnn <levels>         Something something something n
  -h, --help                   Something something something help
  -v, --version                Something something something version
"

Maka saya bisa menggunakannya sebagai

echo "$__usage"

atau bahkan lebih baik, ketika parameter parsing, saya bisa menggema di sana dalam satu-liner:

levelN=${2:?"--level: n is required!""${__usage}"}
Jorge
sumber
4
ini berhasil bagi saya dalam skrip di mana jawaban di atas tidak (tanpa modifikasi).
David Welch
3
Ini jauh lebih bersih daripada melibatkan banyak karakter seperti \ t dan \ n yang sulit ditemukan dalam teks dan berkembang untuk membuat output jauh berbeda dari string dalam skrip
sg
1
Untuk beberapa alasan ia mencetak semuanya pada baris yang sama untuk saya: /
Nicolas de Fontenay
2
@ Nicolas: Menggunakan tanda kutip ganda echo "$__usage"diperlukan untuk saya. echo $__usagetidak bekerja.
Mario
24

Gunakan -eopsi, maka Anda dapat mencetak karakter baris baru dengan \ndi dalam string.

Sampel (tetapi tidak yakin apakah bagus atau tidak)

Yang menyenangkan adalah bahwa -eopsi tidak didokumentasikan di halaman manual MacOS sementara masih dapat digunakan. Itu didokumentasikan di halaman manual Linux .

nhahtdh
sumber
6
Halaman manual tersebut untuk echoperintah yang disediakan sistem /bin/echo, yang pada Mac OS tidak memiliki -eopsi. saat Anda menggunakan bash pada sistem itu, echoperintah bawaannya mengambil alih. Anda dapat melihat ini dengan mengetik /bin/echo whateverdan mengamati perbedaan perilaku secara eksplisit . Untuk melihat dokumentasi untuk built-in, ketik help echo.
Mark Reed
1
/bin/echosering berbeda dari satu OS ke OS lainnya dan berbeda dari Bash's builtin echo.
Dijeda sampai pemberitahuan lebih lanjut.
@ MarkReed: Saya akan mencoba nanti, tapi terima kasih atas informasinya. +1. Saya hanya akan meninggalkan jawaban saya di sini, karena ada cukup banyak diskusi bagus yang terjadi.
nhahtdh
7
echo -etidak portabel - misalnya, beberapa implementasi gema akan mencetak "-e" sebagai bagian dari output. Jika Anda ingin portabilitas, gunakan printf saja. Sebagai contoh, / bin / echo pada OS X 10.7.4 melakukan ini. IIRC gema built-in bash juga aneh di bawah 10.5.0, tapi saya tidak ingat detailnya lagi.
Gordon Davisson
2
echo -etelah menggigit saya sebelumnya ... Pasti menggunakan printfatau catdengan heredoc. The <<-varian dari sini docs yang sangat baik karena Anda dapat menghapus lekukan terkemuka di output, tapi indent untuk dibaca dalam naskah
zbeekman
22

Karena saya merekomendasikan printfdalam komentar, saya mungkin harus memberikan beberapa contoh penggunaannya (walaupun untuk mencetak pesan penggunaan, saya lebih cenderung menggunakan jawaban Dennis atau Chris). printfsedikit lebih kompleks untuk digunakan daripada echo. Argumen pertama adalah string format, di mana lolos (seperti \n) selalu ditafsirkan; itu juga dapat berisi arahan format yang dimulai dengan %, yang mengontrol di mana dan bagaimana argumen tambahan dimasukkan di dalamnya. Berikut adalah dua pendekatan berbeda untuk menggunakannya untuk pesan penggunaan:

Pertama, Anda bisa memasukkan seluruh pesan dalam string format:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: \nup home page: \n"

Perhatikan bahwa tidak seperti itu echo, Anda harus memasukkan baris akhir final secara eksplisit. Juga, jika pesan tersebut mengandung %karakter, mereka harus ditulis sebagai %%. Jika Anda ingin memasukkan laporan bug dan alamat beranda, mereka dapat ditambahkan secara alami:

printf "usage: up [--level <n>| -n <levels>][--help][--version]\n\nReport bugs to: %s\nup home page: %s\n" "$bugreport" "$homepage"

Kedua, Anda bisa menggunakan string format untuk membuatnya mencetak setiap argumen tambahan pada baris terpisah:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: " "up home page: "

Dengan opsi ini, menambahkan laporan bug dan alamat beranda cukup jelas:

printf "%s\n" "usage: up [--level <n>| -n <levels>][--help][--version]" "" "Report bugs to: $bugreport" "up home page: $homepage"
Gordon Davisson
sumber
9

Juga dengan kode sumber indentasi yang dapat Anda gunakan <<-(dengan tanda hubung trailing) untuk mengabaikan tab utama (tetapi tidak memimpin spasi). Sebagai contoh ini:

if [ some test ]; then
    cat <<- xx
        line1
        line2
xx
fi

Keluaran teks menjorok tanpa spasi putih terkemuka:

line1
line2
Pandangan elips
sumber
Itu tidak berhasil untuk saya. Shell apa yang Anda gunakan?
four43
Tidak bekerja di bash 4.4.19 di Ubuntu. Itu tidak menghapus spasi sebelum line1 dan
line2
1
@ four43, Anda benar. Tidak berfungsi untuk menghapus spasi utama. Namun, tidak menghapus tab utama. Jadi saya mengoreksi jawaban saya dari tab dan spasi, ke tab dan bukan spasi. Maaf atas kesalahannya. Saya memeriksa manual dan jelas hanya mengatakan tab dihapus. Terima kasih sudah membawa ini padaku.
Tampilan elips
0

Jika Anda menggunakan solusi dari @jorge dan menemukan bahwa semuanya ada di baris yang sama, pastikan Anda menyertakan variabel dalam tanda kutip:

echo $__usage

akan mencetak semuanya pada satu baris sedangkan

echo "$__usage"

akan mempertahankan baris baru.

pengguna1165471
sumber
Ini sebenarnya satu solusi yang bekerja untuk saya. Printf melakukan banyak hal dan dengan xml multiline saya, mungkin memerlukan banyak pelarian karena sepenuhnya mengacaukan konten. Saya menetapkan foo = cat <<EOF .... EOF&& echo "$ foo"
Jilles van Gurp