Não é possível usar o ponto de exclamação (!) No bash?

87

Estou tentando usar o comando curl para acessar um URL http com um ponto de exclamação ( !) no caminho. por exemplo:

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

o console responde com bash: ... event not found.

O que está acontecendo aqui? e qual seria a sintaxe apropriada para escapar do ponto de exclamação?

netbrain
fonte
Resolvido no bash 4.4+
Isaac

Respostas:

99

O ponto de exclamação faz parte da expansão do histórico no bash. Para usá-lo, você precisa dele entre aspas simples (por exemplo 'http://example.org/!132':) ou para escapar diretamente com uma barra invertida ( \) antes do caractere (por exemplo "http://example.org/\!132":).

Observe que, entre aspas duplas, uma barra invertida antes do exclam impede a expansão do histórico, MAS a barra invertida não é removida nesse caso. Portanto, é melhor usar aspas simples, para que você não passe uma barra invertida literal curlcomo parte do URL.

Daniel Pittman
fonte
8
"http://example.org/\!132"na verdade se expande sem interpretar a barra invertida (razões de conformidade com POSIX, acredito).
Chris Baixo
@ ChrisDown, tentei esclarecer que era minha segunda opção no texto. Obrigado por apontar o potencial de confusão.
Daniel Pittman
6
Para constar: Não é portátil tentar escapar de "!". A recomendação de melhores práticas é sempre citar (aspas simples) "!". Relacionado: "^" (circunflexo), é um não-metacaractere que precisa ser citado para portabilidade. Finalmente, "!" não deve ser usado em uma instrução if; use-o como argumento para testar, se possível (novamente por causa do Solaris / bin / sh).
22412 Nicholas
5
Apenas aspas simples funcionaram para mim. O zsh ainda estava interpretando \!e com aspas duplas.
Orkoden
11
No Solaris (shell antigo anterior ao XPG4), '^' é um alias para |e é usado para criar um canal. Se você estiver enviando scripts para os clientes e não tiver certeza de qual shell eles serão executados, será necessário testar com todos eles!
Nicholas Wilson
61

Assim como a resposta dada por Daniel, você também pode simplesmente desativar completamente a expansão do histórico se não a usar set +H.

Chris Down
fonte
19
Desativar completamente a expansão do histórico é o melhor conselho que ouvi o dia todo! A expansão do histórico é perigosa e bizantina quando existem alternativas muito melhores (pesquisa incremental do histórico com Ctrl-R) que permitem visualizar e editar seu comando para que você não atire cegamente com o comando !-14que você achava que era !-12, opa rm -rf *. Ser seguro. Desative a expansão do histórico! Evite o !!
Aculich
6
Maior resposta: a expansão do histórico é um enorme risco de segurança! Ele pode ser usado para atacar seu Unix por meio de um URL criado.
dan
@aculich, ou apenas use o comando especificadofc -14 no POSIX . Mas é verdade que você pode fazer isso sem a expansão do histórico estar ativada também. Pessoalmente, eu uso !$e !vie sudo !!e até mesmo git add !vi:$muitas vezes o suficiente para justificar deixando expansão história habilitado.
Curinga
Acho que vou adicionar isso aos meus arquivos RC do shell. Eu só usei isso como um "truque" puro
TonyH
17

Eu pessoalmente faria aspas simples, mas, para completar, também observarei, como é uma URL, você pode codificar !como %21, por exemplo curl -v http://example.org/%21132.

Aaron D. Marasco
fonte
13

Isso também pode fazer

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

O que funciona porque o bash concatena as seqüências adjacentes. Essa abordagem é particularmente útil quando você tem outras coisas que precisam de expansão do shell; portanto, não é possível usar aspas simples para toda a cadeia:

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

!O caractere é usado para expansões do histórico no prompt da linha de comando.
portanto, isso pode ser um problema no prompt, mas não nos arquivos de script do shell.
como você pode ver as expansões do histórico funcionam mesmo entre aspas duplas.

mug896
fonte
Existem várias maneiras de fazer com que os comandos do Unix e as sentenças em inglês usem mais caracteres do que precisam e sejam mais confusos do que precisam. Como isso é superior à primeira resposta aceita / mais votada, ou seja, colocar todo o URL entre aspas simples?
G-Man
2
@ G-Man: Diz outra maneira de construir argumentos bash. Eu não estava ciente desse método. Nada de errado em aprender coisas novas.
Sahil Singh
@SahilSingh Como isso é novo? Concatena três cadeias de caracteres, duas entre aspas duplas e uma entre aspas simples. Não há aninhamento aqui.
Raphael
@ G-Man Não é óbvio que quando você coloca duas cordas próximas uma da outra, elas são concatenadas. printf ("hello" "world") também funcionaria em c, mas printf ("hello" 'w') não funcionará, portanto, você sabe que o bash acomoda essas expressões era novo para mim, mas eu concordo que ponto de vista da utilidade, isso não é superior. Gostei da resposta, assim como Mark Shust.
Sahil Singh
2
@ G-Man Também é útil quando há outras expansões de string que se deseja que aconteçam na mesma string. Essa é uma maneira fácil de separar dois tipos de comportamento de cotação.
WAF
7

Eu me deparei com o mesmo problema e minha solução simples era usar uma variável:

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

Aqui a simplicidade é que (1) é portátil através de shells e comandos (2) Não requer conhecimento de sintaxe de escape e códigos ASCII.

Prem
fonte
3

Desde o Bash 4.3, agora você pode usar aspas duplas para citar o caractere de expansão do histórico:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!
Flimm
fonte
isto não trabalho fora echo, eco parece lidar com isso de forma diferente por conta própria
phil294
@ Blauhirn Isso não tem nada a ver com eco, e tudo a ver com citações e a versão do bash que você está executando.
Flimm
2
Esta resposta está errada e deve ser excluída. Sua bashversão não tem nada a ver com o estrondo não ser expandido, é devido ao fato de que, no seu exemplo, !é seguido por "fim de linha" e isso impede que o shell tente expandi-lo. Tente echo "!Hello World"e você verá que bashresponderá com bash: !Hello: event not found. Veja o manual para mais detalhes
don_crissti
0

Para quem está usando o git bash no Windows, a resposta aceita de @DanielPittman funciona. No entanto, você deve substituir a barra invertida (\) por uma barra (/).

Por exemplo, no unix, seria algo parecido com isto:

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

Para o Windows, seria algo assim (concentre-se na barra na parte do cabeçalho da autorização)

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

SamuelDev
fonte
Isso não faz muito sentido. Você tem o argumento citado individualmente, portanto, independentemente das barras envolvidas, o exclame não resultará na expansão do histórico.
Curinga
Ohh, você está certo. Eu postei apenas esta resposta porque, quando eu estava usando a resposta de Daniel (usando barra invertida), um erro aparece.
SamuelDev 16/01