Bagaimana saya bisa memiliki baris baru dalam string di sh?


339

Ini

STR="Hello\nWorld"
echo $STR

menghasilkan sebagai output

Hello\nWorld

dari pada

Hello
World

Apa yang harus saya lakukan untuk memiliki baris baru dalam sebuah string?

Catatan: Pertanyaan ini bukan tentang gema . Saya sadar echo -e, tapi saya sedang mencari solusi yang memungkinkan melewati string (yang termasuk baris baru) sebagai argumen untuk perintah lain yang tidak memiliki opsi serupa untuk menafsirkan \nsebagai baris baru.

Jawaban:


353

Solusinya adalah menggunakan $'string', misalnya:

$ STR=$'Hello\nWorld'
$ echo "$STR" # quotes are required here!
Hello
World

Berikut ini kutipan dari halaman manual Bash:

   Words of the form $'string' are treated specially.  The word expands to
   string, with backslash-escaped characters replaced as specified by  the
   ANSI  C  standard.  Backslash escape sequences, if present, are decoded
   as follows:
          \a     alert (bell)
          \b     backspace
          \e
          \E     an escape character
          \f     form feed
          \n     new line
          \r     carriage return
          \t     horizontal tab
          \v     vertical tab
          \\     backslash
          \'     single quote
          \"     double quote
          \nnn   the eight-bit character whose value is  the  octal  value
                 nnn (one to three digits)
          \xHH   the  eight-bit  character  whose value is the hexadecimal
                 value HH (one or two hex digits)
          \cx    a control-x character

   The expanded result is single-quoted, as if the  dollar  sign  had  not
   been present.

   A double-quoted string preceded by a dollar sign ($"string") will cause
   the string to be translated according to the current  locale.   If  the
   current  locale  is  C  or  POSIX,  the dollar sign is ignored.  If the
   string is translated and replaced, the replacement is double-quoted.

74
Itu adalah bash yang valid, tetapi bukan POSIX sh.
jmanning2k

7
+1 hanya catatan: ekspor varDynamic = "$ varAnother1 $ varAnother2" $ '\ n'
Yordan Georgiev

3
Ini memang bekerja dengan string literal seperti yang diminta OP, tetapi gagal segera setelah variabel diganti: STR=$"Hello\nWorld"cukup cetak Hello\nWorld.
ssc

3
@ssc tanda kutip tunggal, bukan ganda
miken32

7
@ ssc tidak ada variabel dalam contoh Anda. Juga, metode ini membutuhkan tanda kutip tunggal. Akhirnya, untuk memasukkan variabel interpolasi, Anda harus menggabungkan string dengan tanda kutip ganda dan kutipan khusus tunggal secara bersamaan. misalnya H="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}"akan menggemakan "Halo" dan "Dunia" pada baris yang terpisah
JDS

166

Echo begitu berusia sembilan puluhan dan penuh dengan bahaya sehingga penggunaannya akan menghasilkan dump inti tidak kurang dari 4GB. Serius, masalah gema adalah alasan mengapa proses Standarisasi Unix akhirnya menemukan printfutilitas, menghilangkan semua masalah.

Jadi untuk mendapatkan baris baru dalam sebuah string:

FOO="hello
world"
BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
printf '<%s>\n' "$FOO"
printf '<%s>\n' "$BAR"

Sana! Tidak ada SYSV vs BSD, kegilaan gema, semuanya dicetak dengan rapi dan dukungan sepenuhnya portabel untuk urutan pelarian C. Semua orang silakan gunakan printfsekarang dan jangan pernah melihat ke belakang.


3
Terima kasih atas jawaban ini, saya pikir ini memberikan saran yang sangat bagus. Perhatikan, bagaimanapun, bahwa pertanyaan awal saya sebenarnya bukan tentang bagaimana mendapatkan gaung untuk mencetak baris baru, tetapi bagaimana mendapatkan baris baru ke dalam variabel shell. Anda menunjukkan bagaimana melakukan itu menggunakan printf juga, tapi saya pikir solusi dari amfetamachine menggunakan $'string'lebih bersih.
Juan A. Navarro

11
Hanya untuk defisiensi definisi 'bersih'. The $'foo'sintaks tidak sintaks POSIX berlaku sejak 2013. Banyak kerang akan mengeluh.
Jens

5
BAR=$(printf "hello\nworld\n")tidak mencetak trailing \ n untuk saya
Jonny

3
@ Jonny Tidak seharusnya; spesifikasi shell untuk substitusi perintah mengatakan bahwa satu atau lebih baris baru di akhir output dihapus. Saya menyadari ini tidak terduga untuk contoh saya dan telah mengeditnya untuk memasukkan baris baru di printf.
Jens

5
@ismael No, $()ditentukan oleh POSIX sebagai menghapus urutan satu atau lebih karakter <newline> di akhir substitusi .
Jens

61

Apa yang saya lakukan berdasarkan jawaban yang lain adalah

NEWLINE=$'\n'
my_var="__between eggs and bacon__"
echo "spam${NEWLINE}eggs${my_var}bacon${NEWLINE}knight"

# which outputs:
spam
eggs__between eggs and bacon__bacon
knight

30

Masalahnya bukan pada shell. Masalahnya sebenarnya dengan echoperintah itu sendiri, dan kurangnya tanda kutip ganda di sekitar interpolasi variabel. Anda dapat mencoba menggunakan echo -etetapi itu tidak didukung pada semua platform, dan salah satu alasan printfsekarang direkomendasikan untuk portabilitas.

Anda juga dapat mencoba dan menyisipkan baris baru langsung ke skrip shell Anda (jika skrip adalah apa yang Anda tulis) sehingga terlihat seperti ...

#!/bin/sh
echo "Hello
World"
#EOF

atau setara

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!

24

Saya menemukan -ebendera yang elegan dan lurus ke depan

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

Jika string adalah output dari perintah lain, saya hanya menggunakan tanda kutip

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"

2
Ini sudah disebutkan dalam jawaban lain. Juga, meskipun sudah ada di sana, saya baru saja mengedit pertanyaan untuk menekankan fakta bahwa pertanyaan ini bukan tentang echo.
Juan A. Navarro

Ini bukan jawaban untuk pertanyaan
Rohit Gupta

Ini benar-benar menjawab pertanyaan yang saya cari! Terima kasih!
Kieveli

1
Bekerja dengan baik untuk saya di sini. Saya sudah terbiasa membuat file berdasarkan gema ini dan menghasilkan baris baru pada output dengan benar.
Mateus Leon

echo -eterkenal karena masalah portabilitasnya. Ini kurang dari situasi Wild West sekarang daripada pra-POSIX, tetapi masih belum ada alasan untuk memilih echo -e. Lihat juga stackoverflow.com/questions/11530203/…
tripleee

21
  1. Satu-satunya alternatif sederhana adalah mengetikkan baris baru di variabel:

    $ STR='new
    line'
    $ printf '%s' "$STR"
    new
    line

    Ya, itu artinya menulis Enter mana diperlukan dalam kode.

  2. Ada beberapa padanan dengan new linekarakter.

    \n           ### A common way to represent a new line character.
    \012         ### Octal value of a new line character.
    \x0A         ### Hexadecimal value of a new line character.

    Tetapi semua itu membutuhkan "interpretasi" oleh beberapa alat ( POSIX printf ):

    echo -e "new\nline"           ### on POSIX echo, `-e` is not required.
    printf 'new\nline'            ### Understood by POSIX printf.
    printf 'new\012line'          ### Valid in POSIX printf.
    printf 'new\x0Aline'       
    printf '%b' 'new\0012line'    ### Valid in POSIX printf.

    Dan karena itu, alat ini diperlukan untuk membangun string dengan baris baru:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
  3. Dalam beberapa shell, urutannya $'adalah ekspansi shell khusus. Dikenal bekerja di ksh93, bash, dan zsh:

    $ STR=$'new\nline'
  4. Tentu saja, solusi yang lebih kompleks juga dimungkinkan:

    $ echo '6e65770a6c696e650a' | xxd -p -r
    new
    line

    Atau

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line

6

A $ tepat sebelum tanda kutip tunggal '... \ n ...' sebagai berikut, namun tanda kutip ganda tidak berfungsi.

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld

4

Saya bukan pakar bash, tetapi yang ini bekerja untuk saya:

STR1="Hello"
STR2="World"
NEWSTR=$(cat << EOF
$STR1

$STR2
EOF
)
echo "$NEWSTR"

Saya menemukan ini lebih mudah untuk memformat teks.


2
Heredoc tua yang bagus . Dan itu mungkin juga sesuai dengan POSIX!
pyb

Kutipan di $NEWSTRdalam echo "$NEWSTR"sangat penting di sini
shadi

0

Pada sistem saya (Ubuntu 17.10) contoh Anda hanya berfungsi seperti yang diinginkan, baik ketika diketik dari baris perintah (ke sh) dan ketika dieksekusi sebagai shskrip:

[bash sh
$ STR="Hello\nWorld"
$ echo $STR
Hello
World
$ exit
[bash echo "STR=\"Hello\nWorld\"
> echo \$STR" > test-str.sh
[bash cat test-str.sh 
STR="Hello\nWorld"
echo $STR
[bash sh test-str.sh 
Hello
World

Saya kira ini menjawab pertanyaan Anda: itu hanya berfungsi. (Saya belum mencoba mencari tahu detail seperti kapan tepatnya penggantian karakter baris baru \nterjadi dish ).

Namun, saya perhatikan bahwa skrip yang sama ini akan berperilaku berbeda ketika dieksekusi denganbash dan akan dicetak Hello\nWorldsebagai gantinya:

[bash bash test-str.sh
Hello\nWorld

Saya sudah berhasil mendapatkan hasil yang diinginkan bash sebagai berikut:

[bash STR="Hello
> World"
[bash echo "$STR"

Perhatikan tanda kutip ganda di sekitar $STR. Ini berperilaku identik jika disimpan dan dijalankan sebagai bashskrip.

Berikut ini juga memberikan output yang diinginkan:

[bash echo "Hello
> World"

0

Mereka pemilih yang hanya membutuhkan baris baru dan membenci kode multiline yang merusak lekukan, bisa melakukan:

IFS="$(printf '\nx')"
IFS="${IFS%x}"

Bash (dan kemungkinan cangkang lain) melahap semua baris baru yang tertinggal setelah penggantian perintah, jadi Anda harus mengakhiri printfstring dengan karakter yang bukan baris baru dan menghapusnya setelah itu. Ini juga bisa dengan mudah menjadi oneliner.

IFS="$(printf '\nx')" IFS="${IFS%x}"

Saya tahu ini adalah dua tindakan alih-alih satu, tetapi lekukan dan portabilitas saya OCD adalah damai sekarang :) Saya awalnya mengembangkan ini untuk dapat membagi output yang dipisahkan hanya baris baru dan saya akhirnya menggunakan modifikasi yang menggunakan \rsebagai karakter terminating. Itu membuat pemisahan baris baru bekerja bahkan untuk keluaran dos diakhiri dengan \r\n.

IFS="$(printf '\n\r')"

0

Saya tidak terlalu senang dengan salah satu opsi di sini. Inilah yang bekerja untuk saya.

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")

Itu adalah cara yang sangat membosankan untuk menulis str=$(printf '%s\n' "first line" "another line" "and another line")(shell akan dengan mudah (atau tidak) memotong baris baru akhir dari substitusi perintah). Beberapa jawaban di sini sudah dipromosikan printfdalam berbagai bentuk jadi saya tidak yakin ini menambah nilai sebagai jawaban terpisah.
tripleee

Ini untuk string pendek, tetapi jika Anda ingin menjaga hal-hal tetap rapi dan setiap baris sebenarnya panjangnya 60 karakter, maka itu adalah cara yang rapi untuk melakukannya. Mengingat bahwa itu adalah solusi yang akhirnya saya gunakan, itu menambahkan sesuatu, jika bukan untuk Anda, maka untuk diri saya di masa depan ketika saya pasti kembali.
Adam K Dean

1
Bahkan untuk string panjang saya lebih suka printf '%s\n' "first line" \ (baris baru, indentasi) 'second line'dll. Lihat juga printf -v struntuk menetapkan ke variabel tanpa memunculkan subkulit.
tripleee
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.