Kannst du $ nicht im Skript verwenden?

11

Ich frage mich nur, warum das nicht funktioniert

#!/bin/bash 

ls /bin
ls !$

Ich erwarte, ls /binzweimal zu laufen , aber der zweite wirft Fehler auf, wie !$nicht interpretiert wurde

Habe ich etwas verpasst oder !$nur in der Kommandozeile gearbeitet?

Ich konnte kein relevantes Teil in man bash(auf dem Mac) finden.

Gänseblümchen
quelle
9
Obwohl es eine Lösung gibt, ist dies wirklich der beste Weg, dies in einem Skript zu erreichen? Der Verlauf ist standardmäßig aus einem Grund deaktiviert, wenn Sie nicht interaktiv ausgeführt werden. Ein langes Skript wird die Datei .bash_history als Spam versenden. Nicht zu sagen, dass es sich nicht gelohnt hat, dies zu fragen, aber nur, wenn Sie daran denken, dies in einem Skript zu verwenden, ist dies wirklich der beste Weg?
Flungo

Antworten:

26

Verlauf und Verlaufserweiterung sind standardmäßig deaktiviert, wenn die Shell nicht interaktiv ausgeführt wird.

Du brauchst:

#!/bin/bash 

set -o history
set -o histexpand

ls /bin
ls !$

oder:

SHELLOPTS=history:histexpand bash script.sh

Dies wirkt sich auf alle script.shmöglicherweise ausgeführten Bash-Instanzen aus.

cuonglm
quelle
9
Sie vorsichtig mit SHELLOPTS, wirkt es , dass bashdas läuft script.sh, sondern auch alle anderen bashInstanzen , die script.shschließlich führen kann (wie auch andere bashSkripte ...).
Stéphane Chazelas
Und es wirkt sich nicht auf eine andere bash Instanz aus, die Ihr Skript ausführt.
Blacklight Shining
Ich denke, diese Antwort würde sich verbessern, wenn der Kommentar von @ StéphaneChazelas darin bearbeitet wird.
Oliphaunt - wieder Monica
7

Das Vernünftige wäre

ls /bin
ls $_

oder

set ls /bin
$*
$*

oder

c='ls /bin'
$c
$c

Vorsichtsmaßnahmen: Es ist erwähnenswert, dass jede dieser Situationen mit einigen Fallstricken verbunden ist. Die $ _ -Lösung erfasst nur das letzte einzelne Argument: So ls foo barbleibt $ _ mit just übrig bar. Die eine mit setwerden die Argumente außer Kraft setzen ( $1, $2usw.). Und all dies wird wie geschrieben funktionieren, aber wenn es auf komplexere Befehle verallgemeinert wird (wo Flucht und Leerzeichen wichtig sind), könnten Sie auf einige Schwierigkeiten stoßen. Beispiel: ls 'foo bar'(wenn das Argument für foo bareinen einzelnen Pfadnamen zwei oder mehr Leerzeichen oder andere Leerzeichen enthält) wird sich in keinem dieser Beispiele korrekt verhalten. Um diese Fälle zu umgehen, ist möglicherweise eine ordnungsgemäße Flucht (möglicherweise in Kombination mit einem evalBefehl) oder eine Verwendung "$@"anstelle von $*erforderlich.

Steven Penny
quelle
1
+1 für eine Antwort, die portabel ist und keinen Bashismus missbraucht, der die interaktive Verwendung erleichtern soll. (Abgesehen davon ist der Wunsch von bash, Ausrufezeichen im Standard-Setup interaktiv zu erweitern, für mich als Benutzer anderer Shells kontraintuitiv und kontraproduktiv, da es mich immer nervt, wenn ich versuche, einen komplizierten Shell-Befehl auszuführen.)
mtraceur
Obwohl ich, wie in diesem Moment geschrieben, zögerte, ihm die +1 zu geben, aufgrund der Probleme, die Anfänger-Shell-Scripter wahrscheinlich haben werden, wenn sie versuchen, sie auf komplexere Befehle zu verallgemeinern. Ich schlug eine Änderung vor, um zumindest einen Vorbehaltsabsatz hinzuzufügen, in dem die möglichen Fallstricke erläutert werden.
mtraceur
@mtraceur: Es ist nicht portabel, funktioniert nur in bash, zsh, ksh (wenn sich zwei Befehle nicht in derselben Zeile befinden). Arbeiten Sie in Strich, mksh nur wenn interaktiv
cuonglm
@ Cuongim: Entschuldigung, ich war vielleicht zu nachlässig allgemein. Der $_Weg ist nicht tragbar, Sie haben Recht. Der setAnsatz funktioniert in nicht-interaktivem Dash und sollte mit dem ${1+"$@"}Trick (plus zsh global alias) allgemein sein, obwohl ich mich vage daran erinnere, dass seter in der Vergangenheit mit einigen (alten?) Shells nicht perfekt portierbar war. Der Ansatz, eine Variable zu definieren, die den Befehl und dann die Auswertung enthält, insbesondere evalsoweit ein ordnungsgemäßes Escapezeichen und ein tatsächlicher Befehl verwendet werden, ist meines Wissens im Allgemeinen vollständig portierbar.
mtraceur