Não é possível usar! $ No script?

11

Apenas querendo saber por que isso não está funcionando

#!/bin/bash 

ls /bin
ls !$

Espero executar ls /binduas vezes, mas o segundo gera erros, pois !$não foi interpretado

Perdi alguma coisa, ou !$apenas trabalhei na linha de comando?

Não encontrei parte relevante em man bash(no mac)

margarida
fonte
9
Embora exista uma solução, essa é realmente a melhor maneira de conseguir isso em um script? o histórico é desativado por padrão por um motivo quando você executa de maneira não interativa - um script longo envia um spam para o arquivo .bash_history. Não estou dizendo que não valia a pena perguntar isso, mas apenas se você estiver pensando em usá-lo em um script, essa é realmente a melhor maneira?
Flungo

Respostas:

26

O histórico e a expansão do histórico são desativados por padrão quando o shell é executado de maneira não interativa.

Você precisa:

#!/bin/bash 

set -o history
set -o histexpand

ls /bin
ls !$

ou:

SHELLOPTS=history:histexpand bash script.sh

isso afetará todas as instâncias do bash que script.shpodem ser executadas.

cuonglm
fonte
9
cuidado com o SHELLOPTS, isso afetará o bashque é executado script.sh, mas também todas as outras bashinstâncias que script.shpodem ser executadas (como outros bashscripts ...).
Stéphane Chazelas
E isso não afetará nenhuma outra bash instância que execute seu script.
Blacklight Shining
Acho que essa resposta melhoraria se o comentário de @ StéphaneChazelas fosse editado nele.
Oliphaunt - restabelece Monica
7

A melhor coisa a fazer seria

ls /bin
ls $_

ou

set ls /bin
$*
$*

ou

c='ls /bin'
$c
$c

Advertências: vale lembrar que cada uma delas traz algumas armadilhas. A solução $ _ captura apenas o último argumento único: assim ls foo bar, $ _ contém apenas bar. A uma usando setvai substituir os argumentos ( $1, $2, etc.). E tudo isso, conforme escrito, funcionará, mas quando generalizado para comandos mais complexos (onde escape e espaço em branco são importantes), você pode encontrar algumas dificuldades. Por exemplo: ls 'foo bar'(onde o argumento do nome do caminho único foo barcontém dois ou mais espaços ou qualquer outro caractere de espaço em branco) não se comportará corretamente em nenhum desses exemplos. Uma fuga adequada (possivelmente combinada com um evalcomando), ou o uso em "$@"vez de $*, pode ser necessária para contornar esses casos.

Steven Penny
fonte
1
+1 para uma resposta que seja portátil e não abuse de um bashismo destinado a facilitar o uso interativo. (De um lado, o desejo do bash de expandir interativamente os pontos de exclamação na configuração padrão é algo que acho contra-intuitivo como usuário de outros shells e contraproducente, porque sempre me ferra quando estou tentando executar algum comando complicado do shell).
Mtraceur
Embora, conforme escrito neste momento, eu hesitei em dar o +1 devido aos problemas que os scripts de shell iniciantes provavelmente terão ao tentar generalizá-lo para comandos mais complexos. Sugeri uma edição para, pelo menos, adicionar um parágrafo de advertência explicando as possíveis armadilhas.
Mtraceur
@ mtraceur: não é portátil, apenas funciona no bash, zsh, ksh (se dois comandos não estiverem na mesma linha). Trabalho em traço, mksh somente quando interativo
cuonglm
@cuongim: Desculpe, eu fui talvez descuidadamente geral. O $_caminho não é portátil, você está certo. A setabordagem funciona em um traço não interativo, e com o ${1+"$@"}truque (mais zsh global alias) deve ser geral, embora eu me lembre vagamente que settem um histórico de não ser perfeitamente portátil com alguns shells (antigos?). A abordagem definir-uma-variável-segurando-o-comando-e-depois-avaliar-ele, especialmente usando escape apropriado e um evalcomando real , é completamente portátil, até onde eu sei.
Mtraceur