Tidak dapat menggunakan tanda seru (!) Di bash?

87

Saya mencoba menggunakan perintah curl untuk mengakses url http dengan tanda seru ( !) di jalurnya. misalnya:

curl -v "http://example.org/!287s87asdjh2/somepath/someresource"

balasan konsol dengan bash: ... event not found.

Apa yang terjadi disini? dan apa yang akan menjadi sintaksis yang tepat untuk menghindari tanda seru?

netbrain
sumber
Diselesaikan di bash 4.4+
Isaac

Jawaban:

99

Tanda seru adalah bagian dari ekspansi sejarah di bash. Untuk menggunakannya Anda perlu menyertakannya dalam tanda kutip tunggal (misalnya:) 'http://example.org/!132'atau untuk melarikan diri secara langsung dengan tanda garis miring terbalik ( \) sebelum karakter (misalnya:) "http://example.org/\!132".

Perhatikan bahwa dalam tanda kutip ganda, garis miring terbalik sebelum tanda seru mencegah ekspansi riwayat, TETAPI garis miring terbalik tidak dihilangkan dalam kasus seperti itu. Jadi lebih baik menggunakan tanda kutip tunggal, jadi Anda tidak melewatkan backslash literal curlsebagai bagian dari URL.

Daniel Pittman
sumber
8
"http://example.org/\!132"sebenarnya mengembang tanpa menginterpretasikan backslash (alasan kepatuhan POSIX, saya percaya).
Chris Down
@ ChrisDown, saya mencoba mengklarifikasi bahwa itu adalah opsi kedua saya di teks. Terima kasih telah menunjukkan potensi kebingungan.
Daniel Pittman
6
Sebagai catatan: Tidak portabel untuk mencoba melarikan diri "!". Rekomendasi praktik terbaik adalah selalu mengutip (menghanguskan kutipan) "!". Terkait: "^" (caret), adalah non-metacharacter yang perlu mengutip untuk portabilitas. Akhirnya, "!" tidak boleh digunakan dalam pernyataan if; gunakan itu sebagai argumen untuk menguji sebaliknya jika mungkin (lagi karena Solaris / bin / sh).
Nicholas Wilson
5
Hanya satu kutipan yang berhasil untuk saya. zsh masih menafsirkan \!dan mengutip dua kali lipat.
orkoden
1
Pada Solaris (shell lama pre-XPG4), '^' adalah alias untuk |dan digunakan untuk membuat pipa. Jika Anda mengirim skrip ke pelanggan dan tidak yakin di mana mereka akan menjalankannya, Anda harus menguji semuanya!
Nicholas Wilson
61

Selain jawaban yang diberikan oleh Daniel, Anda juga dapat mematikan ekspansi sejarah jika Anda tidak menggunakannya set +H.

Chris Down
sumber
19
Menonaktifkan ekspansi sejarah sama sekali adalah saran terbaik yang saya dengar sepanjang hari! Perluasan riwayat berbahaya dan Bizantium ketika ada alternatif yang jauh lebih baik (pencarian riwayat tambahan Ctrl-R) yang memungkinkan Anda melihat pratinjau & mengedit perintah Anda sehingga Anda tidak membabi buta dengan perintah !-14yang Anda !-12pikir sebelumnya, oops, kebetulan terjadi rm -rf *. Berhati-hatilah. Nonaktifkan ekspansi riwayat! Hindari !!
aculich
6
Jawaban terbesar: ekspansi sejarah adalah risiko keamanan yang sangat besar! Itu dapat digunakan untuk menyerang Unix Anda melalui URL buatan.
dan
@aculich, atau cukup gunakan perintah yang ditentukan POSIX fc -14. Tetapi memang benar bahwa Anda dapat melakukan itu tanpa ekspansi sejarah diaktifkan juga. Secara pribadi, saya menggunakan !$dan !vidan sudo !!dan bahkan git add !vi:$cukup sering untuk menjamin membiarkan ekspansi sejarah diaktifkan.
Wildcard
Saya pikir saya akan menambahkan ini ke file RC shell saya. Saya hanya pernah menggunakan ini sebagai "trik" yang rapi
TonyH
17

Saya pribadi akan melakukan tanda kutip tunggal, tetapi untuk kelengkapan, saya juga akan mencatat karena ini adalah URL, Anda dapat menyandikan !sebagai %21, misalnya curl -v http://example.org/%21132.

Aaron D. Marasco
sumber
13

Ini juga bisa dilakukan

curl -v "http://example.org/"'!'"287s87asdjh2/somepath/someresource"
atau
curl -v "http://example.org/"\!"287s87asdjh2/somepath/someresource"

Yang bekerja karena bash menggabungkan string yang berdekatan. Pendekatan ini sangat berguna ketika Anda memiliki hal-hal lain yang membutuhkan ekspansi shell, jadi Anda tidak dapat menggunakan tanda kutip tunggal untuk seluruh string:

curl -v 'http://example.org/!'"287s87asdjh2/${basepath}/someresource"

!karakter digunakan untuk ekspansi sejarah di command line prompt.
jadi ini bisa menjadi masalah pada prompt tetapi tidak pada file skrip shell.
seperti yang Anda lihat ekspansi sejarah bekerja bahkan dalam tanda kutip ganda.

mug896
sumber
Ada banyak cara untuk membuat perintah Unix dan kalimat bahasa Inggris menggunakan lebih banyak karakter daripada yang diperlukan, dan lebih membingungkan daripada yang seharusnya. Bagaimana ini lebih unggul daripada jawaban pertama / diterima / terpilih, yaitu, menempatkan seluruh URL ke dalam tanda kutip tunggal?
G-Man
2
@ G-Man: Ini memberi tahu cara lain untuk membangun argumen bash. Saya tidak mengetahui metode ini. Tidak ada yang salah dalam mempelajari hal baru.
Sahil Singh
@SahilSingh Bagaimana ini baru? Ini merangkai tiga string, dua tertutup dalam tanda kutip ganda dan satu tertutup dalam tanda kutip tunggal. Tidak ada sarang di sini.
Raphael
@ G-Man Tidak jelas bahwa ketika Anda meletakkan 2 string di samping satu sama lain mereka akan digabungkan. printf ("hello" "world") akan bekerja di c juga, tetapi printf ("hello" 'w') tidak akan berfungsi, jadi Anda tahu bahwa bash mengakomodasi ekspresi seperti itu adalah hal baru bagi saya, tetapi saya setuju dari sudut pandang utilitas, ini tidak unggul. Saya menyukai jawabannya, begitu pula Mark Shust.
Sahil Singh
2
@ G-Man Ini juga berguna ketika ada ekspansi string lain salah satu tidak ingin terjadi dalam string yang sama. Ini adalah cara mudah untuk memisahkan dua jenis perilaku mengutip.
WAF
7

Saya telah menemukan masalah yang sama, dan solusi sederhana saya adalah menggunakan variabel:

E=!  
curl -v "http://example.org/${E}287s87asdjh2/somepath/someresource"

Di sini kesederhanaannya adalah bahwa (1) Ia portabel di seluruh cangkang dan perintah (2) Tidak perlu mengetahui sintaks escape dan kode ASCII.

Prem
sumber
3

Sejak Bash 4.3, sekarang Anda dapat menggunakan tanda kutip ganda untuk mengutip karakter ekspansi sejarah:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!
Flimm
sumber
ini tidak bekerja di luar echo, gema tampaknya menangani ini sendiri
phil294
@ Blauhirn Ini tidak ada hubungannya dengan gema, dan segala sesuatu yang berkaitan dengan mengutip dan versi bash yang Anda jalankan.
Flimm
2
Jawaban ini salah dan harus dihapus. bashVersi Anda tidak ada hubungannya dengan bang yang tidak diperluas, itu disebabkan oleh fakta bahwa dalam contoh Anda !diikuti oleh "end of line" dan yang mencegah shell dari mencoba untuk memperluasnya. Coba echo "!Hello World"dan Anda akan melihat yang bashakan membalas bash: !Hello: event not found. Lihat manual untuk detail lebih lanjut
don_crissti
0

Bagi mereka yang menggunakan git bash di windows, jawaban yang diterima dari @DanielPittman berfungsi. Namun, Anda harus mengganti garis miring terbalik (\) dengan garis miring maju (/).

Misalnya, di unix, akan terlihat seperti ini:

curl https://abc.com/services -H 'Authorization: Bearer 111A80BBZZCnS\!ZR412543s'

Untuk windows, ini akan menjadi seperti ini (fokus pada garis miring di bagian header otorisasi)

curl https://abc.com/services -H 'Authorization: Bearer 111A80BBZZCnS/!ZR412543s'

SamuelDev
sumber
Ini tidak masuk akal. Anda memiliki argumen yang dikutip, jadi terlepas dari garis miring yang terlibat, tanda seru tidak akan menghasilkan perluasan riwayat.
Wildcard
Ohh kamu benar. Saya hanya memposting jawaban ini karena ketika saya menggunakan jawaban Daniel (menggunakan backslash), kesalahan muncul.
SamuelDev