Garis kelanjutan bash

158

Bagaimana Anda menggunakan garis kelanjutan bash?

Saya menyadari bahwa Anda dapat melakukan ini:

echo "continuation \
lines"
>continuation lines

Namun, jika Anda memiliki kode indentasi, itu tidak berfungsi dengan baik:

    echo "continuation \
    lines"
>continuation     lines
PyRulez
sumber
5
Panduan Gaya Bash Shell Google merekomendasikan "di sini" dokumen untuk "Jika Anda harus menulis string yang lebih panjang dari 80 karakter". Lihat jawaban @ tripleee .
Trevor Boyd Smith
google.github.io/styleguide/… - ini adalah tautan langsung dalam dokumen baru
jcollum

Jawaban:

161

Inilah yang mungkin Anda inginkan

$       echo "continuation"\
>       "lines"
continuation lines

Jika ini membuat dua argumen untuk digemakan dan Anda hanya ingin satu, maka mari kita lihat penggabungan string. Dalam bash, menempatkan dua string di samping satu sama lain bersambung:

$ echo "continuation""lines"
continuationlines

Jadi garis kelanjutan tanpa indentasi adalah salah satu cara untuk memecah string:

$ echo "continuation"\
> "lines"
continuationlines

Tetapi ketika sebuah indent digunakan:

$       echo "continuation"\
>       "lines"
continuation lines

Anda mendapatkan dua argumen karena ini bukan lagi gabungan.

Jika Anda ingin string tunggal yang melintasi garis, sementara membuat indentasi tetapi tidak mendapatkan semua ruang itu, satu pendekatan yang dapat Anda coba adalah dengan membuang baris lanjutan dan menggunakan variabel:

$ a="continuation"
$ b="lines"
$ echo $a$b
continuationlines

Ini akan memungkinkan Anda untuk memiliki kode indentasi bersih dengan mengorbankan variabel tambahan. Jika Anda membuat variabel lokal, itu tidak boleh terlalu buruk.

Ray Toal
sumber
1
Terima kasih atas bantuan Anda, tetapi meskipun ini menghilangkan spasi, mereka sekarang menjadi parameter yang terpisah (Bash mengartikan spasi pada baris kedua sebagai pemisah parameter) dan sekarang hanya dicetak dengan benar karena perintah gema.
1
Oh, Anda ingin satu string (bash) untuk menjangkau garis! Saya mengerti sekarang.
Ray Toal
4
Solusi dengan satu variabel:s="string no. 1" s+="string no. 2" s+=" string no. 3" echo "$s"
Johnny Thunderman
30

Di sini, dokumen dengan <<-HEREterminator berfungsi baik untuk string teks multi-baris yang indentasi. Ini akan menghapus semua tab utama dari dokumen di sini. (Terminator saluran masih akan tetap.)

cat <<-____HERE
    continuation
    lines
____HERE

Lihat juga http://ss64.com/bash/syntax-here.html

Jika Anda perlu melestarikan beberapa, tetapi tidak semua, memimpin spasi putih, Anda dapat menggunakan sesuatu seperti

sed 's/^  //' <<____HERE
    This has four leading spaces.
    Two of them will be removed by sed.
____HERE

atau mungkin gunakan truntuk menghilangkan baris baru:

tr -d '\012' <<-____
    continuation
     lines
____

(Baris kedua memiliki tab dan spasi di depan; tab akan dihapus oleh operator dasbor sebelum terminator heredoc, sedangkan ruang akan dipertahankan.)

Untuk membungkus string kompleks panjang melalui banyak garis, saya suka printf:

printf '%s' \
    "This will all be printed on a " \
    "single line (because the format string " \
    "doesn't specify any newline)"

Ini juga berfungsi dengan baik dalam konteks di mana Anda ingin menanamkan potongan skrip shell nontrivial dalam bahasa lain di mana sintaks bahasa host tidak akan membiarkan Anda menggunakan dokumen di sini, seperti dalam a Makefileatau Dockerfile.

printf '%s\n' >./myscript \
    '#!/bin/sh` \
    "echo \"G'day, World\"" \
    'date +%F\ %T' && \
chmod a+x ./myscript && \
./myscript
tripleee
sumber
Tidak bekerja untuk saya. Ubuntu 16.04. Saya mendapatkan dua baris bukannya diharapkan satu baris.
Penghe Geng
@PengheGeng Memang, ini memecahkan masalah menghilangkan lekukan, bukan yang menyatukan baris. Anda masih dapat melakukan backslash baris baru di akhir baris untuk menggabungkan dua baris bersama.
tripleee
(Tapi lihat printfcontoh pertama juga sekarang.)
tripleee
12

Anda dapat menggunakan array bash

$ str_array=("continuation"
             "lines")

kemudian

$ echo "${str_array[*]}"
continuation lines

ada ruang ekstra, karena (setelah manual bash):

Jika kata tersebut dikutip ganda, ${name[*]}perluas satu kata dengan nilai setiap anggota array dipisahkan oleh karakter pertama dari variabel IFS

Jadi siapkan IFS=''untuk menyingkirkan ruang ekstra

$ IFS=''
$ echo "${str_array[*]}"
continuationlines
tworec
sumber
4

Dalam skenario tertentu memanfaatkan kemampuan gabungan Bash mungkin tepat.

Contoh:

temp='this string is very long '
temp+='so I will separate it onto multiple lines'
echo $temp
this string is very long so I will separate it onto multiple lines

Dari bagian PARAMETER halaman Bash Man:

name = [value] ...

... Dalam konteks di mana pernyataan penugasan menetapkan nilai ke variabel shell atau indeks array, operator + = dapat digunakan untuk menambahkan atau menambah nilai variabel sebelumnya. Ketika + = diterapkan pada variabel yang atribut integernya telah ditetapkan, nilai dievaluasi sebagai ekspresi aritmatika dan ditambahkan ke nilai saat variabel, yang juga dievaluasi. Ketika + = diterapkan pada variabel array menggunakan penugasan majemuk (lihat Array di bawah), nilai variabel tidak disetel (seperti ketika menggunakan =), dan nilai-nilai baru ditambahkan ke array yang dimulai pada satu lebih besar dari indeks maksimum array. (untuk array yang diindeks) atau ditambahkan sebagai pasangan nilai kunci tambahan dalam array asosiatif. Ketika diterapkan pada variabel bernilai string, nilai diperluas dan ditambahkan ke nilai variabel.

Cybernaut
sumber
Mungkin saran terbaik di halaman ini. Menggunakan HEREDOC dan memipipkan ke dalam terjemahan untuk <CR> s adalah super tidak intuitif dan gagal ketika string benar-benar perlu memiliki pemisah baris yang ditempatkan secara tidak bijaksana.
ingyhere
2

Saya menemukan situasi di mana saya harus mengirim pesan panjang sebagai bagian dari argumen perintah dan harus mematuhi batasan panjang garis. Perintahnya terlihat seperti ini:

somecommand --message="I am a long message" args

Cara saya menyelesaikan ini adalah dengan memindahkan pesan sebagai dokumen di sini (seperti yang disarankan @tripleee). Tapi dokumen di sini menjadi stdin, jadi perlu dibaca kembali, saya pergi dengan pendekatan di bawah ini:

message=$(
    tr "\n" " " <<- END
        This is a
        long message
END
)
somecommand --message="$message" args

Ini memiliki keunggulan yang $messagedapat digunakan tepat sebagai string konstan tanpa spasi tambahan atau jeda baris.

Perhatikan bahwa baris pesan aktual di atas diawali dengan tabkarakter masing-masing, yang dilucuti oleh dokumen di sini sendiri (karena penggunaan <<-). Masih ada jeda baris di akhir, yang kemudian diganti dddengan spasi.

Perhatikan juga bahwa jika Anda tidak menghapus baris baru, mereka akan muncul seperti saat "$message"diperluas. Dalam beberapa kasus, Anda mungkin dapat mengatasinya dengan menghapus tanda kutip ganda di sekitar $message, tetapi pesan tidak akan lagi menjadi argumen tunggal.

haridsv
sumber
2

Kelanjutan garis juga dapat dicapai melalui penggunaan sintaks yang pintar.

Dalam hal echo:

# echo '-n' flag prevents trailing <CR> 
echo -n "This is my one-line statement" ;
echo -n " that I would like to make."
This is my one-line statement that I would like to make.

Dalam hal vars:

outp="This is my one-line statement" ; 
outp+=" that I would like to make." ; 
echo -n "${outp}"
This is my one-line statement that I would like to make.

Pendekatan lain dalam kasus vars:

outp="This is my one-line statement" ; 
outp="${outp} that I would like to make." ; 
echo -n "${outp}"
This is my one-line statement that I would like to make.

Voila!

di sana
sumber
1

Anda bisa memisahkannya dengan baris baru (tanpa menggunakan garis miring terbalik) seperti yang diperlukan dalam lekukan sebagai berikut dan hanya strip baris baru.

Contoh:

echo "continuation
of 
lines" | tr '\n' ' '

Atau jika itu adalah definisi variabel, baris baru akan secara otomatis dikonversi ke spasi. Jadi, lepaskan ruang ekstra hanya jika berlaku.

x="continuation
of multiple
lines"
y="red|blue|
green|yellow"

echo $x # This will do as the converted space actually is meaningful
echo $y | tr -d ' ' # Stripping of space may be preferable in this case
rjni
sumber
1

Ini tidak persis apa yang diminta pengguna, tetapi cara lain untuk membuat string panjang yang membentang beberapa baris adalah dengan menambahkannya secara bertahap, seperti:

$ greeting="Hello"
$ greeting="$greeting, World"
$ echo $greeting
Hello, World

Jelas dalam hal ini akan lebih mudah untuk membangunnya sekali jalan, tetapi gaya ini bisa sangat ringan dan dapat dimengerti ketika berhadapan dengan string yang lebih panjang.

Jon McClung
sumber
0

Namun, jika Anda memiliki kode indentasi, itu tidak berfungsi dengan baik:

    echo "continuation \
    lines"
>continuation     lines

Coba dengan kutipan tunggal dan merangkai string:

    echo 'continuation' \
    'lines'
>continuation lines

Catatan: rangkaian termasuk spasi putih.

mMontu
sumber
2
Ini berfungsi dengan argumen echo dan string, tetapi tidak bekerja dengan hal lain, seperti penugasan variabel. Meskipun pertanyaannya bukan tentang variabel, menggunakan gema hanyalah sebuah contoh. Alih-alih echo jika Anda memiliki x=, Anda akan mendapatkan error: lines: command not found.
LS
0

Bergantung pada jenis risiko apa yang akan Anda terima dan seberapa baik Anda tahu dan mempercayai data, Anda dapat menggunakan interpolasi variabel sederhana.

$: x="
    this
    is
       variably indented
    stuff
   "
$: echo "$x" # preserves the newlines and spacing

    this
    is
       variably indented
    stuff

$: echo $x # no quotes, stacks it "neatly" with minimal spacing
this is variably indented stuff
Paul Hodges
sumber
-2

Ini mungkin tidak benar-benar menjawab pertanyaan Anda, tetapi Anda mungkin tetap merasa berguna.

Perintah pertama membuat skrip yang ditampilkan oleh perintah kedua.

Perintah ketiga membuat skrip itu dapat dieksekusi.

Perintah keempat memberikan contoh penggunaan.

john@malkovich:~/tmp/so$ echo $'#!/usr/bin/env python\nimport textwrap, sys\n\ndef bash_dedent(text):\n    """Dedent all but the first line in the passed `text`."""\n    try:\n        first, rest = text.split("\\n", 1)\n        return "\\n".join([first, textwrap.dedent(rest)])\n    except ValueError:\n        return text  # single-line string\n\nprint bash_dedent(sys.argv[1])'  > bash_dedent
john@malkovich:~/tmp/so$ cat bash_dedent 
#!/usr/bin/env python
import textwrap, sys

def bash_dedent(text):
    """Dedent all but the first line in the passed `text`."""
    try:
        first, rest = text.split("\n", 1)
        return "\n".join([first, textwrap.dedent(rest)])
    except ValueError:
        return text  # single-line string

print bash_dedent(sys.argv[1])
john@malkovich:~/tmp/so$ chmod a+x bash_dedent
john@malkovich:~/tmp/so$ echo "$(./bash_dedent "first line
>     second line
>     third line")"
first line
second line
third line

Perhatikan bahwa jika Anda benar-benar ingin menggunakan skrip ini, lebih masuk akal untuk memindahkan skrip yang dapat dieksekusi ke dalam ~/binsehingga skrip tersebut akan berada di jalur Anda.

Periksa referensi python untuk perincian tentang cara textwrap.dedentkerjanya.

Jika penggunaan $'...'atau "$(...)"membingungkan Anda, ajukan pertanyaan lain (satu per konstruksi) jika belum ada satu. Mungkin bagus untuk memberikan tautan ke pertanyaan yang Anda temukan / tanyakan sehingga orang lain akan memiliki referensi yang ditautkan.

intuisi
sumber
6
Niat baik - dan mungkin bahkan berguna - meskipun ini mungkin, OP meminta saran tentang sintaks bash dasar, dan Anda memberinya definisi fungsi python yang menggunakan paradigma OO, kontrol aliran yang luar biasa, dan impor. Lebih lanjut, Anda memanggil sebuah executable sebagai bagian dari interpolasi string - sesuatu yang orang yang menanyakan pertanyaan semacam ini pasti belum melihat di bash.
Parthian Shot