Ich habe ein Szenario, in dem eine Befehlsersetzung ohne Verwendung einer Subshell erforderlich ist. Ich habe ein Konstrukt wie dieses:
pushd $(mktemp -d)
Jetzt möchte ich das temporäre Verzeichnis auf einmal beenden und entfernen:
rmdir $(popd)
Dies funktioniert jedoch nicht, weil popd
das gepoppte Verzeichnis nicht zurückgegeben wird (es gibt das neue, jetzt aktuelle Verzeichnis zurück) und weil es in einer Subshell ausgeführt wird.
Etwas wie
dirs -l -1 ; popd &> /dev/null
gibt das gepoppte Verzeichnis zurück, kann aber nicht wie folgt verwendet werden:
rmdir $(dirs -l -1 ; popd &> /dev/null)
weil das popd
nur die Subshell betrifft. Was erforderlich ist, ist die Fähigkeit, dies zu tun:
rmdir { dirs -l -1 ; popd &> /dev/null; }
aber das ist ungültige Syntax. Ist es möglich, diesen Effekt zu erzielen?
(Hinweis: Ich weiß, dass ich das temporäre Verzeichnis in einer Variablen speichern kann. Ich habe versucht, dies zu vermeiden und dabei etwas Neues zu lernen!)
trap
Handler das Verzeichnis bereinigen, falls der Prozess durch ein Signal ausgelöst wird.fish
, das Äquivalent zur Befehlssubstitution,()
den Ordner der äußeren Shell ändern. Es ist normalerweise nervig, aber in solchen Fällen ist es sicher nützlich, da bin ich mir sicher.Antworten:
Die Wahl des Titels Ihrer Frage ist etwas verwirrend.
pushd
/popd
, einecsh
vonbash
und kopierte Funktionzsh
, ist eine Möglichkeit, einen Stapel gespeicherter Verzeichnisse zu verwalten.Verschiebt das aktuelle Arbeitsverzeichnis auf einen Stapel und ändert dann das aktuelle Arbeitsverzeichnis (und druckt dann,
/some/dir
gefolgt vom Inhalt dieses Stapels (durch Leerzeichen getrennt).druckt den Inhalt des Stapels (wieder durch Leerzeichen getrennt) und wechselt dann zum obersten Element des Stapels und entfernt es vom Stapel.
(Beachten Sie auch, dass einige Verzeichnisse dort mit ihrer
~/x
oder ihrer~user/x
Notation dargestellt werden).Wenn der Stack derzeit
/a
und hat/b
, ist das aktuelle Verzeichnis/here
und Sie führen:pushd
wird gedruckt/tmp/whatever /here /a /b
undpopd
ausgegeben/here /a /b
, nicht/tmp/whatever
. Das ist unabhängig von der Verwendung der Befehlssubstitution oder nicht.popd
kann nicht verwendet werden, um den Pfad des vorherigen Verzeichnisses abzurufen, und im Allgemeinen kann seine Ausgabe nicht nachbearbeitet werden (siehe das$dirstack
oder$DIRSTACK
Array einiger Shells für den Zugriff auf die Elemente dieses Verzeichnisstapels).Vielleicht möchten Sie:
Oder
Allerdings würde ich verwenden:
Auf jeden Fall
pushd "$(mktemp -d)"
läuft nichtpushd
in einer Subshell. In diesem Fall konnte das Arbeitsverzeichnis nicht geändert werden. Dasmktemp
läuft in einer Unterschale. Da es sich um einen separaten Befehl handelt, muss er in einem separaten Prozess ausgeführt werden. Es schreibt seine Ausgabe in eine Pipe und der Shell-Prozess liest sie am anderen Ende der Pipe.ksh93 kann den separaten Prozess vermeiden, wenn der Befehl eingebaut ist, aber selbst dort handelt es sich immer noch um eine Subshell (eine andere Arbeitsumgebung), die dieses Mal emuliert wird, anstatt sich auf die separate Umgebung zu verlassen, die normalerweise durch Forking bereitgestellt wird. Zum Beispiel ist in
ksh93
,a=0; echo "$(a=1; echo test)"; echo "$a"
keine Gabel beteiligt, sondernecho "$a"
gibt immer noch aus0
.Wenn Sie die Ausgabe von
mktemp
in einer Variablen speichern möchten, während Sie sie gleichzeitigpushd
mit übergebenzsh
, können Sie Folgendes tun:Mit anderen Bourne-ähnlichen Muscheln:
Oder um die Ausgabe
$(mktemp -d)
mehrmals zu verwenden, ohne sie explizit in einer Variablen zu speichern, können Siezsh
anonyme Funktionen verwenden:quelle
pushd
undpopd
arbeite so, wie Sie es beschreiben und dass sie unabhängig von der Befehlsersetzung sind - keine Verwirrung! Sie antworteten jedoch durch eigenes Problem, indem Sie enthüllten$OLDPWD
. Ich kann tunpopd; rmdir $OLDPWD
. Das ist die Antwort auf mein Problem - alles andere bestätigt nur, was ich dachte. Die Befehlssubstitution scheint ein Weg zu sein, dies zu lösen, aber es liegt nicht an der Subshell, und Sie können die Befehlssubstitution nicht ohne Subshell durchführen. Vielen Dank, dass Sie OLDPWD enthüllt haben - genau das brauche ich!rmdir $(popd)
bewirkt , dasspopd
es in einer Unter-Shell ausgeführt wird, was bedeutet, dass das aktuelle Verzeichnis nicht geändert wird. Selbst wenn es nicht in einer Unter-Shell ausgeführt wurde, ist die Ausgabe vonpopd
nicht das temporäre Verzeichnis, sondern eine durch Leerzeichen getrennte Liste von Verzeichnissen ohne dieses temporäre Verzeichnis. Wo ich sage, dass Sie verwirrt sind.OLDPWD
. Ich muss daran denken, eines Tagesman bash
von Ende zu Ende zu lesen !Sie können die Verknüpfung des Verzeichnisses zuerst aufheben, bevor Sie es verlassen:
oder
aber beachten Sie, dass
pushd
undpopd
sind wirklich Werkzeuge für die interaktive Shell, nicht für Skripte (deshalb sind sie so gesprächig, echte Skriptbefehle schweigen , wenn sie Erfolg haben ).quelle
dirs
Geben Sie in bash eine Liste der gespeicherten Verzeichnisse mit der pushd / popd-Methode an.Außerdem
dirs -1
druckt das letzte Verzeichnis in der Liste enthalten.pushd $(mktmp -d)
Verwenden Sie zum Entfernen des zuvor durch Ausführen erstellten Verzeichnisses Folgendes :Und dann
popd
das bereits entfernte Verzeichnis aus der Liste:Alles in einer Zeile:
Und fügen Sie die Option (-l) hinzu, um die Notation
~/x
oder zu vermeiden~user/x
:Das ist bemerkenswert ähnlich zu der Zeile, nach der Sie gefragt haben.
Außer dass ich nicht verwenden würde,
&>
da es jede Fehlermeldung von verbergen würdepopd
.Hinweis: Das Verzeichnis bleibt
rmdir
wiepwd
an diesem Punkt erhalten. Und wird nach dempopd
Befehl tatsächlich getrennt (keine verbleibenden Links verwendet).Es gibt eine Option für Shells, die die Variable "OLDPWD" unterstützen (die meisten bourneähnlichen Shells: ksh, bash, zsh have
$OLDPWD
). Es ist interessant festzustellen, dass ksh standardmäßig keine dirs, pod, pushd implementiert (lksh, dash und andere haben auch kein popd verfügbar, können also hier nicht verwendet werden):Oder idiomatischer (gleiche Liste von Muscheln wie oben):
quelle
rmdir
das aktuelle Verzeichnis verwenden können, in dem Sie sich dabei befinden würden. Sie müssen das ausführen,popd
bevor Sie das tun,rmdir
aber Sie müssen wissen, was zu entfernen ist, undpopd
das wird Ihnen nicht gesagt. Ich dachte, wie Sie,dirs -l -1
war die Antwort, habe aber inzwischen herausgefunden, dass die Antwort tatsächlich zu verwenden ist$OLDPWD
.popd && rmdir ~-
.rmdir "$PWD"
. Außerdem sollte das aktuelle Verzeichnis dasjenige sein, das von erstellt wurdemktmp -d
(fallspushd $(mktemp -d)
es zuvor ausgeführt wurde) und in daspushd
das pwd verschoben wird. Also, ja, nachpushd
und vor popd wird das erstellte Verzeichnis gespeichert$(dirs -1)
, ich sehe kein Problem.