Не можете использовать! $ В скрипте?

11

Просто интересно, почему это не работает

#!/bin/bash 

ls /bin
ls !$

Я рассчитываю запустить ls /binдважды, но второй вызывает ошибки, так как !$не был интерпретирован

Я что-то пропустил или !$работал только в командной строке?

Я не мог найти соответствующую часть в man bash(на Mac)

маргаритка
источник
9
Хотя есть решение, действительно ли это лучший способ добиться этого в сценарии? История отключена по умолчанию по причине, когда вы запускаете не в интерактивном режиме - длинный скрипт собирается спамить файл .bash_history. Не сказать, что это не стоило спрашивать об этом, но просто если вы думаете об использовании этого в сценарии, это действительно лучший способ?
flungo

Ответы:

26

Расширение истории и истории отключено по умолчанию, когда оболочка работает не в интерактивном режиме.

Тебе нужно:

#!/bin/bash 

set -o history
set -o histexpand

ls /bin
ls !$

или же:

SHELLOPTS=history:histexpand bash script.sh

это повлияет на все экземпляры bash, которые script.shмогут работать.

cuonglm
источник
9
будьте осторожны с SHELLOPTS, это повлияет на то, bashчто выполняется script.sh, но также и на все другие bashэкземпляры, которые script.shмогут в конечном итоге выполняться (как и другие bashсценарии ...).
Стефан Шазелас
И это не повлияет на любой другой bash экземпляр, который запускает ваш скрипт.
Blacklight Shining
Я думаю, что этот ответ улучшится, если в него будет отредактирован комментарий @ StéphaneChazelas.
Олифаунт - восстановить Монику
7

Нормальной вещью было бы

ls /bin
ls $_

или

set ls /bin
$*
$*

или

c='ls /bin'
$c
$c

Предостережения: стоит помнить, что у каждого из них есть свои подводные камни. Решение $ _ захватывает только последний единственный аргумент: так ls foo barчто оставим $ _, содержащий just bar. Тот, который использует set, переопределит аргументы ( $1, $2и т. Д.). И все это, как написано, будет работать, но при обобщении на более сложные команды (где экранирование и пробелы имеют значение), вы можете столкнуться с некоторыми трудностями. Например: ls 'foo bar'(где один аргумент pathname foo barсодержит два или более пробелов или любые другие пробельные символы) не будет работать правильно ни в одном из этих примеров. Для обхода этих случаев может потребоваться правильное экранирование (возможно, в сочетании с evalкомандой) или использование "$@"вместо $*.

Стивен Пенни
источник
1
+1 за ответ, который является переносимым и не использует башизм, предназначенный для облегчения интерактивного использования. (Кроме того, желание bash интерактивно расширять восклицательные знаки в настройках по умолчанию - это то, что я нахожу противоинтуитивным, как пользователь других оболочек, и контрпродуктивно, потому что оно всегда меня бесит, когда я пытаюсь выполнить какую-то сложную команду оболочки).
mtraceur
Хотя, как написано в этот момент, я не решался дать ему +1 из-за проблем, которые, вероятно, возникнут у сценаристов оболочки новичка при попытке обобщить его для более сложных команд. Я предложил изменить, чтобы хотя бы добавить абзац предостережения, объясняющий возможные подводные камни.
mtraceur
@mtraceur: он не переносимый, работает только в bash, zsh, ksh (если две команды не в одной строке). Работайте в тире, только в интерактивном режиме
cuonglm
@cuongim: Извините, я был слишком небрежным генералом. $_Способ не является портативным, вы правы. setПодход работает в неинтерактивном тире, и с ${1+"$@"}(плюс ЗШ глобального псевдонима) трюк должна быть общим, хотя я смутно помню , что setесть история , не будучи совершенно портативным с некоторыми (старыми?) Оболочками. evalНасколько я знаю, подход define-a-variable-holding-the-command-and-then-eval-it, особенно с использованием правильного экранирования и фактической команды, в целом переносим.
mtraceur