Pengobatan backslash lintas kulit

9

Bagaimana echodan printfmemperlakukan backslashes di zsh, bashdan kerang lainnya?

Di bawah zsh saya mendapatkan perilaku berikut:

$ echo "foo\bar\baz"
foaaz
$ echo "foo\\bar\\baz"
foaaz
$ echo 'foo\bar\baz'
foaaz
$ echo 'foo\\bar\\baz'
foo\bar\baz

Di bawah bash , segalanya tampak sedikit lebih konsisten:

bash$ echo "foo\bar\baz"
foo\bar\baz
bash$ echo 'foo\bar\baz'
foo\bar\baz
bash$

Tetapi yang lebih konkret: Bagaimana saya bisa meneruskan string yang mengandung garis miring terbalik seperti\\foo\bar\something ke:

  • echo
  • printf
  • print

dan mendapatkan string yang sama persis? (dalam zshdan bash)?

Berikut ini eksperimen lain dengan fungsi di zsh:

function foo
{
    echo -E '$1'
}

$ foo \\here\is\some\path
$1

Bagaimana saya bisa mencetaknya \\here\is\some\path?

Pembaruan (Catatan: ini sekarang telah dijawab dalam komentar Stephane)

Saya sudah mencoba yang berikut ini di zsh 5.0.2:

function foo
{
    printf '$s\n' $1 
}

foo '\\some\path'

Tapi ini dicetak $s?

Amelio Vazquez-Reina
sumber
3
Gunakan printf, echotidak portabel dalam hal ini.
jordanm
Anda "foo" berfungsi: ganti 'dengan ", dan aktifkan dengan: foo '\\ here \ is \ some \ path' (jika tidak, shell pemanggil mendapat kesempatan untuk menafsirkan '\' sebelum mereka sampai ke fungsinya)
Olivier Dulac
2
Fungsi Anda digunakan '$s\n'saat seharusnya digunakan '%s\n'.
cjm

Jawaban:

12

zsh echoberperilaku dengan cara standar, seperti bashdalam mode UNIX. Itu memperluas \bke karakter ASCII BS seperti spesifikasi UNIX membutuhkan.

Jangan gunakan echountuk menampilkan string acak, gunakanprintf :

printf '%s\n' "$1"

print -r -- "$1"juga bekerja tetapi ksh/ zshspesifik.

echo -E - "$1"bekerja dengan zshdan saya percaya beberapa BSD.

cat << EOF
$1
EOF

bekerja di shell seperti Bourne bahkan dari beberapa dekade ketika tidak ada printfperintah tapi itu menelurkan proses baru, dan benar-benar tidak diperlukan saat ini seperti yang kita miliki di printfmana - mana.

Dan omong-omong, Anda perlu melarikan diri garis miring terbalik pada baris perintah shell karena khusus untuk shell (setiap shell tetapi rc), jadi:

$ foo '\\foo\bar'

foo \\foo\barakan meneruskan "\foo\bar"string fooyang tidak dapat menemukan kembali backslash yang hilang kembali.

Stéphane Chazelas
sumber
Terima kasih Stephane. Anehnya, konstruksi printf '%s\n' "$var"bekerja untuk saya di baris perintah ( zsh 5.0.2, yaitu yang terbaru), tetapi tidak di dalam suatu fungsi (lihat pembaruan saya di OP). Mengapa?
Amelio Vazquez-Reina
1
Itu printf '%s\n', bukan yang printf '$s\n'kamu inginkan. Perhatikan bahwa Anda perlu mengutip variabel dalam shell lain selain zsh( "$1", bukan $1) dan sintaks definisi fungsi standar adalah foo() { ...; }, meskipun shell apa pun kecuali bashdiizinkan foo() any-command, dan function foo { .... }adalah kshsintaksnya.
Stéphane Chazelas
@ Marscus, itu jawaban untuk pertanyaan yang berbeda.
Stéphane Chazelas
1

jawaban baru: baca -r var

-r  raw input - disables interpretion of backslash escapes and line-continuation in the read data

dan untuk ditampilkan:

printf "%s" "$var"
echo "$var"

harus bekerja.

Jadi untuk fungsi foo Anda:

function foo
{
    read -r var
    echo -E "var is : ${var}"
}

$ foo 
\\here\is\some\path
var is : \\here\is\some\path

jawaban lama di bawah (tidak menjawab, tapi mungkin berguna ^^)

cukup ganti masing-masing \dengan \\memberi tahu shell "bahwa itu benar-benar \saya inginkan". jika tidak (misalnya dalam zsh, dalam contoh Anda) bisa jadi itu \bberarti "1 backspace", atau apa pun.

Anda bisa misalnya menggunakan sed:

sed -e 's,\\,\\\\,g' < the_original > the_escaped_backslaches_version

(lihat bagaimana Anda juga perlu melarikan diri "\" di sana, untuk memberi tahu sed hal yang sama: "itu adalah literal" \ "Saya ingin) (dan perhatikan saya mengelilinginya dengan '' dan bukan" "untuk menghindari shell untuk menafsirkan sebagian besar juga)

Olivier Dulac
sumber
Terima kasih tetapi seperti yang saya sebutkan dalam judul: Saya ingin menghindari melarikan diri backslash.
Amelio Vazquez-Reina
oops, saya akan mengedit ^^
Olivier Dulac
Terima kasih! Tapi read -r varsepertinya menunggu masukan dari STDIN. Bagaimana jika saya menyampaikannya sebagai argumen? (mis. foo \\here\is\some\path)
Amelio Vazquez-Reina
shell IS menerjemahkan backslash. Itu sebabnya Anda mungkin harus memindahkan argumen yang lewat ke baca, lihat contoh "foo" saya.
Olivier Dulac
1
iow: Anda ingin penanganan "non-standar" string oleh shell: Anda bisa lebih mudah (dan lebih luas) membuat program Anda sesuai dengan apa yang Anda inginkan, daripada mencoba membuat shell-invoking-that-program untuk berperilaku seperti yang Anda inginkan. Jadi: lewati argumen (yang berisi "\" yang tidak ingin Anda hindari) setelah program dimulai, melalui "read -r", dan bukan pada shell command-line pemanggilannya.
Olivier Dulac
1

Secara umum Anda tidak bisa, karena backslash adalah karakter pelarian (dan karenanya harus melarikan diri ketika nilai literalnya diperlukan). BASH memberikan penawaran tunggal untuk tujuan ini, namun Anda tidak dapat menggunakan escape quote tunggal dalam string yang dikutip tunggal.

Adapun echoperbedaan, itu adalah built-in yang mungkin berperilaku berbeda di shell (sampai batas tertentu). Misalnya BASH built-in memiliki -eopsi yang memerintahkannya untuk menginterpretasikan urutan backslash - dengan itu Anda akan mendapatkan perilaku yang Anda lihat di shell Z.

peterph
sumber