No se puede usar! $ En el script?

11

Solo me pregunto por qué esto no funciona

#!/bin/bash 

ls /bin
ls !$

Espero correr ls /bindos veces, pero la segunda genera errores porque !$no fue interpretada

¿Me perdí algo o !$solo trabajé en la línea de comando?

No pude encontrar parte relevante en man bash(en mac)

margarita
fuente
99
Aunque hay una solución, ¿es esta realmente la mejor manera de lograr esto en un script? el historial está deshabilitado de forma predeterminada por un motivo cuando se ejecuta de forma no interactiva: un script largo enviará spam al archivo .bash_history. No digo que no valga la pena preguntar esto, pero solo si estás pensando en usar esto en un script, ¿es esta realmente la mejor manera?
flungo

Respuestas:

26

El historial y la expansión del historial están deshabilitados de forma predeterminada cuando el shell se ejecuta de forma no interactiva.

Necesitas:

#!/bin/bash 

set -o history
set -o histexpand

ls /bin
ls !$

o:

SHELLOPTS=history:histexpand bash script.sh

afectará a todas las instancias de bash que script.shpuedan ejecutarse.

Cuonglm
fuente
99
cuidado con SHELLOPTS, afectará a lo que se bashejecuta script.sh, pero también a todas las otras bashinstancias que script.sheventualmente pueden ejecutarse (como otros bashscripts ...).
Stéphane Chazelas
Y no afectará a ninguna otra bash instancia que ejecute su script.
Blacklight Shining
Creo que esta respuesta mejoraría si el comentario de @ StéphaneChazelas se edita en él.
Oliphaunt - reinstalar a Monica
7

Lo más sensato sería

ls /bin
ls $_

o

set ls /bin
$*
$*

o

c='ls /bin'
$c
$c

Advertencias: vale la pena recordar que cada una de estas viene con algunas trampas. La solución $ _ solo captura el último argumento simple: por ls foo barlo tanto , dejará $ _ conteniendo solo bar. El uno usando setanulará los argumentos ( $1, $2, etc). Y todo esto tal como está escrito funcionará, pero cuando se generaliza a comandos más complejos (donde el escape y el espacio en blanco son importantes), puede encontrar algunas dificultades. Por ejemplo: ls 'foo bar'(donde el argumento de nombre de ruta único foo barcontiene dos o más espacios, o cualquier otro espacio en blanco) no se comportará correctamente en ninguno de estos ejemplos. Es posible que se necesite escapar adecuadamente (posiblemente combinado con un evalcomando), o usar en "$@"lugar de $*, para evitar esos casos.

Steven Penny
fuente
1
+1 para una respuesta que sea portátil y que no abuse de un bashism destinado a facilitar el uso interactivo. (Por otro lado, el deseo de bash de expandir interactivamente los signos de exclamación en la configuración predeterminada es algo que me parece contradictorio como usuario de otros shells, y contraproducente porque siempre me fastidia cuando intento hacer un comando de shell complicado).
mtraceur
Aunque como está escrito en este momento, dudaba en darle el +1 debido a los problemas que probablemente tendrán los scripters de shell para principiantes cuando intente generalizarlo a comandos más complejos. Sugerí una edición para al menos agregar un párrafo de advertencia que explicara las posibles trampas del mismo.
mtraceur
@mtraceur: no es portátil, solo funciona en bash, zsh, ksh (si dos comandos no están en la misma línea). Trabaje en el tablero, mksh solo cuando sea interactivo
cuonglm
@cuongim: Lo siento, quizás fui demasiado descuidado en general. El $_camino no es portátil, tienes razón. El setenfoque funciona en un tablero no interactivo, y con el ${1+"$@"}truco (más el alias global de zsh) debería ser general, aunque recuerdo vagamente que settiene un historial de no ser perfectamente portátil con algunos shells (¿viejos?). El enfoque de definir-una-variable-mantener-el-comando-y-luego-evaluarlo, especialmente usando un escape apropiado y un evalcomando real , es completamente portátil por lo que yo sé.
mtraceur