Nie możesz użyć wykrzyknika (!) W bash?


86

Próbuję użyć polecenia curl, aby uzyskać dostęp do adresu URL z wykrzyknikiem ( !) na ścieżce. na przykład:

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

konsola z odpowiedzi bash: ... event not found.

Co tu się dzieje? i jaka byłaby właściwa składnia, aby uniknąć wykrzyknika?


Rozwiązany w bash 4.4+
Izaak

Odpowiedzi:


98

Wykrzyknik jest częścią ekspansji historii w bash. Aby go użyć, musisz go ująć w pojedyncze cudzysłowy (np .:) 'http://example.org/!132'lub bezpośrednio uciec ukośnikiem odwrotnym ( \) przed znakiem (np "http://example.org/\!132".:).

Zauważ, że w podwójnych cudzysłowach odwrotny ukośnik przed wykrzyknikiem zapobiega rozszerzeniu historii, ALE odwrotny ukośnik nie jest usuwany w takim przypadku. Lepiej więc używać pojedynczych cudzysłowów, aby nie podawać dosłownego odwrotnego ukośnika curljako części adresu URL.


8
"http://example.org/\!132"faktycznie rozwija się bez interpretacji ukośnika odwrotnego (uważam, że jest to zgodne z POSIX).
Chris Down,

@ChrisDown, próbowałem wyjaśnić, że to moja druga opcja w tekście. Dziękujemy za zwrócenie uwagi na potencjalne zamieszanie.
Daniel Pittman,

6
Dla przypomnienia: próba ucieczki przed „!” Nie jest przenośna. Najlepszą praktyką jest, aby zawsze cytować (pojedyncze cytaty) „!”. Powiązane: „^” (karetka), to metaznak, który wymaga cytowania w celu przenoszenia. Wreszcie, "!" nie powinien być stosowany w instrukcji if; użyj go jako argumentu do testowania, jeśli to możliwe (ponownie z powodu Solaris / bin / sh).
Nicholas Wilson

5
Pracowały dla mnie tylko pojedyncze cytaty. zsh wciąż tłumaczył \!i podwójnie cytował.
orkoden

1
W systemie Solaris (stara śmieci przed wersją XPG4) „^” jest aliasem |i służy do tworzenia potoku. Jeśli wysyłasz skrypty do klientów i nie masz pewności, w jakiej powłoce będą one uruchamiane, musisz przetestować je wszystkie!
Nicholas Wilson

61

Oprócz odpowiedzi udzielonej przez Daniela możesz także po prostu całkowicie wyłączyć rozszerzanie historii, jeśli jej nie używasz set +H.


19
Całkowite wyłączenie ekspansji historii to najlepsza rada, jaką słyszałem przez cały dzień! Ekspansja historii jest niebezpieczna i bizantyjska, gdy istnieją o wiele lepsze alternatywy (przyrostowe przeszukiwanie historii za pomocą Ctrl-R), które umożliwiają podgląd i edycję polecenia, abyś nie ślepo strzelał z poleceniem, !-14które ci się !-12wydawało, ups rm -rf *. Bądź bezpieczny. Wyłącz rozszerzanie historii! Unikaj !!
aculich

6
Największa odpowiedź: ekspansja historii stanowi ogromne zagrożenie bezpieczeństwa! Można go użyć do zaatakowania Uniksa poprzez spreparowany adres URL.
dan

@ aculich lub po prostu użyj zamiast tego polecenia podanegofc -14 przez POSIX . Ale to prawda, że ​​możesz to zrobić bez włączonego rozszerzenia historii. Osobiście używam !$i !via sudo !!, a nawet git add !vi:$dość często, aby uzasadnić pozostawiając włączony ekspansja historia.
Wildcard

Myślę, że dodam to do moich plików RC powłoki. Użyłem tego tylko jako zgrabnego „
trika

17

Osobiście robiłbym pojedyncze cytaty, ale dla kompletności, również zauważę, ponieważ jest to adres URL, możesz zakodować !jako %21np curl -v http://example.org/%21132.


13

To też może zrobić

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

Co działa, ponieważ bash łączy sąsiednie łańcuchy. To podejście jest szczególnie przydatne, gdy masz inne rzeczy, które wymagają rozszerzenia powłoki, więc nie możesz używać pojedynczych cudzysłowów dla całego łańcucha:

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

!znak służy do rozszerzania historii w wierszu polecenia.
może to być problemem w pytaniu, ale nie w plikach skryptów powłoki.
jak widać rozszerzenia historii działają nawet w podwójnych cudzysłowach.


Istnieje wiele sposobów na to, aby polecenia uniksowe i angielskie zdania używały więcej znaków, niż powinny, i były bardziej mylące, niż powinny. W jaki sposób przewyższa to pierwszą / zaakceptowaną / najwyżej głosowaną odpowiedź, a mianowicie umieszczenie całego adresu URL w pojedynczym cudzysłowie?
G-Man,

2
@ G-Man: Podaje inny sposób konstruowania argumentów bash. Nie wiedziałem o tej metodzie. Nic złego w uczeniu się nowych rzeczy.
Sahil Singh,

@SahilSingh Jak to jest nowe? Łączy trzy ciągi, dwa ujęte w podwójne cudzysłowy i jedno ujęte w pojedyncze cudzysłowy. Tutaj nie ma gniazdowania.
Raphael,

@ G-Man Nie jest oczywiste, że po umieszczeniu 2 łańcuchów obok siebie łączą się one. printf („cześć” „świat”) również działałby w c, ale printf („cześć” „w”) nie będzie działać, więc widzisz, że bash dostosowuje takie wyrażenia był dla mnie nowy, ale zgadzam się z z punktu widzenia użyteczności, to nie jest lepsze. Podobała mi się odpowiedź, podobnie jak Mark Shust.
Sahil Singh,

2
@ G-Man Przydaje się również, gdy istnieją inne rozwinięcia ciągów , które chcą się odbywać w tym samym ciągu. Jest to łatwy sposób na rozdzielenie dwóch rodzajów zachowań związanych z cytowaniem.
WAF

7

Natknąłem się na ten sam problem, a moim prostym rozwiązaniem było użycie zmiennej:

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

Tutaj prostota polega na tym, że (1) jest przenośny między powłokami i poleceniami (2) Nie wymaga znajomości składni Escape i kodów ASCII.


3

Od czasu wersji Bash 4.3 możesz teraz używać podwójnych cudzysłowów, aby zacytować znak rozszerzenia historii:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!

to nie działa poza echo, echo wydaje się radzić sobie z tym inaczej
phil294

@Blauhirn To nie ma nic wspólnego z echem, a wszystko z cytatem i wersją bash, którą uruchamiasz.
Flimm

2
Ta odpowiedź jest błędna i powinna zostać usunięta. Twoja bashwersja nie ma nic wspólnego z tym, że huk nie jest rozszerzany, wynika to z faktu, że w twoim przykładzie !następuje „koniec linii”, co uniemożliwia powłoce próbę jej rozwinięcia. Spróbuj, echo "!Hello World"a zobaczysz, że bashodpowie bash: !Hello: event not found. Więcej informacji na ten temat można znaleźć w instrukcji obsługi
don_crissti,

0

Dla tych, którzy używają git bash w Windows, działa odpowiedź zaakceptowana przez @DanielPittman. Powinieneś jednak zastąpić ukośnik odwrotny (\) ukośnikiem (/).

Na przykład w Uniksie wyglądałby mniej więcej tak:

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

W przypadku systemu Windows byłoby to mniej więcej tak (skoncentruj się na ukośniku w części nagłówka autoryzacji)

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


To nie ma większego sensu. Podano pojedynczy argument, więc niezależnie od ukośników, wykrzyknik nie spowoduje rozszerzenia historii.
Wildcard

Och, masz rację. Wysłałem tylko tę odpowiedź, ponieważ kiedy korzystałem z odpowiedzi Daniela (używając odwrotnego ukośnika), pojawia się błąd.
SamuelDev,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.