¿No puedes usar el signo de exclamación (!) En bash?

87

Estoy tratando de usar el comando curl para acceder a una url http con un signo de exclamación ( !) en su ruta. p.ej:

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

la consola responde con bash: ... event not found.

¿Que esta pasando aqui? ¿Y cuál sería la sintaxis adecuada para escapar del signo de exclamación?

netbrain
fuente
Resuelto en bash 4.4+
Isaac

Respuestas:

99

El signo de exclamación es parte de la expansión de la historia en bash. Para usarlo, debe estar entre comillas simples (por ejemplo:) 'http://example.org/!132'o para escapar directamente con una barra diagonal inversa ( \) antes del carácter (por ejemplo:) "http://example.org/\!132".

Tenga en cuenta que entre comillas dobles, una barra invertida antes de la exclamación impide la expansión del historial, PERO la barra invertida no se elimina en tal caso. Por lo tanto, es mejor usar comillas simples, por lo que no está pasando una barra diagonal inversa curlcomo parte de la URL.

Daniel Pittman
fuente
8
"http://example.org/\!132"en realidad se expande sin interpretar la barra diagonal inversa (razones de cumplimiento POSIX, creo).
Chris Down
@ChrisDown, traté de aclarar que esa era mi segunda opción en el texto. Gracias por señalar el potencial de confusión.
Daniel Pittman
66
Para el registro: no es portátil intentar escapar "!". La recomendación de mejores prácticas es siempre citar (comillas simples) "!". Relacionado: "^" (caret), es un no-metacarácter que necesita ser citado para portabilidad. Finalmente, "!" no debe usarse en una declaración if; úselo como argumento para probar si es posible (nuevamente debido a Solaris / bin / sh).
Nicholas Wilson el
55
Solo las comillas simples funcionaron para mí. zsh todavía estaba interpretando \!y comillas dobles.
Orkoden
1
En Solaris (viejo shell anterior a XPG4), '^' es un alias para |y se utiliza para crear una tubería. Si envía scripts a los clientes y no puede estar seguro de en qué shell lo ejecutarán, ¡debe probar con todos ellos!
Nicholas Wilson el
61

Además de la respuesta dada por Daniel, también puede desactivar la expansión del historial por completo si no la usa set +H.

Chris Down
fuente
19
¡Desactivar por completo la expansión de la historia es el mejor consejo que he escuchado todo el día! La expansión del historial es peligrosa y bizantina cuando hay alternativas mucho mejores (búsqueda de historial incremental con Ctrl-R) que te permiten previsualizar y editar tu comando para que no dispares a ciegas con el comando !-14que creías que estaba en !-12ese momento rm -rf *. Cuidate. ¡Deshabilita la expansión del historial! ¡Evita el !!
aculich
66
Mayor respuesta: ¡la expansión del historial es un gran riesgo de seguridad! Se puede usar para atacar su Unix a través de una URL diseñada.
dan
@aculich, o simplemente use el comando especificado por POSIX en su fc -14lugar. Pero es cierto que puede hacerlo sin que también se habilite la expansión del historial. Personalmente, utilizo !$y !vie sudo !!incluso con la git add !vi:$frecuencia suficiente para garantizar que se habilite la expansión del historial.
Comodín el
Creo que agregaré esto a mis archivos RC de shell. Solo he usado esto como un "truco"
ordenado
17

Yo personalmente haría comillas simples, pero para completar, también notaré que, como es una URL, puede codificar !como %21, por ejemplo curl -v http://example.org/%21132.

Aaron D. Marasco
fuente
13

Esto también puede hacer

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

Lo que funciona porque bash concatena cadenas adyacentes. Este enfoque es particularmente útil cuando tiene otras cosas que necesitan expansión de shell, por lo que no puede usar comillas simples para toda la cadena:

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

!El carácter se utiliza para expansiones del historial en la línea de comandos.
así que esto puede ser un problema en la solicitud pero no en los archivos de script de shell.
como puede ver, las expansiones históricas funcionan incluso entre comillas dobles.

mug896
fuente
Hay muchas maneras de hacer que los comandos de Unix y las oraciones en inglés usen más caracteres de los que necesitan y sean más confusos de lo que deben ser. ¿Cómo es esto superior a la primera / aceptada / respuesta más votada, es decir, poner la URL completa entre comillas simples?
G-Man
2
@ G-Man: dice otra forma de construir argumentos bash. No estaba al tanto de este método. No hay nada malo en aprender cosas nuevas.
Sahil Singh
@SahilSingh ¿Cómo es esto nuevo? Concatena tres cadenas, dos entre comillas dobles y una entre comillas simples. No hay anidamiento aquí.
Raphael
@ G-Man No es obvio que cuando pones 2 cuerdas una al lado de la otra se concatenan. printf ("hola" "mundo") también funcionaría en c, pero printf ("hola" 'w') no funcionará, así que ya sabes que bash acomoda tales expresiones era nuevo para mí, pero estoy de acuerdo punto de vista de utilidad, esto no es superior. Me gustó la respuesta, al igual que Mark Shust.
Sahil Singh
2
@ G-Man Es también útil cuando hay otras expansiones de la secuencia una qué desea que ocurra en la misma cadena. Esta es una manera fácil de separar dos tipos de comportamiento de comillas.
WAF
7

Me he encontrado con el mismo problema, y ​​mi solución simple fue usar una variable:

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

Aquí la simplicidad es que (1) es portátil a través de shells y comandos (2) No requiere conocer la sintaxis de escape y los códigos ASCII.

Prem
fuente
3

Desde Bash 4.3, ahora puede usar comillas dobles para citar el carácter de expansión del historial:

$ bash --version
GNU bash, version 4.3...
[...]
$ echo "Hello World!"
Hello World!
Flimm
fuente
esto no funciona fuera de echo, echo parece manejar esto de manera diferente por sí mismo
phil294
@Blauhirn Esto no tiene nada que ver con echo, y todo que ver con citas y la versión de bash que estás ejecutando.
Flimm
2
Esta respuesta es incorrecta y debe eliminarse. Su bashversión no tiene nada que ver con que la explosión no se expanda, se debe al hecho de que, en su ejemplo, !le sigue "final de línea" y eso evita que el shell intente expandirlo. Intente echo "!Hello World"y verá que bashresponderá con bash: !Hello: event not found. Consulte el manual para obtener más detalles
don_crissti
0

Para aquellos que usan git bash en windows, la respuesta aceptada de @DanielPittman funciona. Sin embargo, debe reemplazar la barra diagonal inversa (\) con una barra diagonal (/).

Por ejemplo, en Unix, se vería así:

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

Para Windows, sería algo como esto (concéntrese en la barra diagonal en la parte del encabezado de autorización)

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

SamuelDev
fuente
Esto no tiene mucho sentido. Tiene el argumento solo citado, así que, independientemente de las barras inclinadas involucradas, la exclamación no dará como resultado la expansión del historial.
Comodín el
Ohh tienes razon Solo publiqué esta respuesta porque cuando estaba usando la respuesta de Daniel (usando la barra invertida), aparece un error.
SamuelDev