Wie führe ich Befehle in einem anderen Ordner aus, ohne den Ordnerpfad zu wiederholen?

73

Gibt es eine clevere Möglichkeit, Kopier- und Verschiebevorgänge oder einen Befehl zum Duplizieren einer Datei auszuführen, ohne cddanach einen Befehl mvim selben Ordner ausführen zu müssen ?

Zum Beispiel muss ich Folgendes ausführen:

mv /folder1/folder2/folder3/file.txt /folder1/folder2/folder3/file-2013.txt

Beachten Sie, dass das Verzeichnis, in das ich die Datei verschiebe, dasselbe ist, aber ich muss den gesamten Pfad erneut eingeben, und manchmal wird es ärgerlich. Ich bin gespannt, ob es eine andere Möglichkeit gibt, dies zu tun, ohne den gesamten Pfad erneut eingeben zu müssen , da die Operation auf demselben Pfad ausgeführt werden würde.

Valter Silva
quelle
9
Ich kann nicht glauben, dass dies so viele positive Stimmen hat. Es ist ein Duplikat von unix.stackexchange.com/questions/35782/… und unix.stackexchange.com/questions/66889/…
user13107
13
@ user13107 Es gibt viele Möglichkeiten, eine Frage zu stellen, einschließlich unterschiedlicher Formulierungen. Und wenn Sie nicht wissen, dass die Antwort "Klammererweiterung" heißt, können Sie sie möglicherweise nicht sofort finden.
Slhck
2
@ user13107 sie sind auf einer anderen Seite so nicht Duplikate
Mark
1
Mark, danke, ich kannte diese Regel über Duplikate nicht. @slhck Ja. Ich verstehe. Ich war nur frustriert, weil meine Frage zu Unix.SE als Duplikat geschlossen wurde und diese so beliebt wurde.
user13107
3
@ user13107, das bekommen Sie, wenn Sie auf der richtigen Seite
Samuel Edwin Ward

Antworten:

125

Verwenden Sie einfach die Klammererweiterung :

mv /folder1/folder2/folder3/{file.txt,file-2013.txt}

Dies entspricht dem Schreiben:

mv /folder1/folder2/folder3/file.txt /folder1/folder2/folder3/file-2013.txt

Mit der Klammererweiterung können Sie natürlich mehr Argumente angeben. Sie können sogar Bereiche , um es zB passieren ein paar Test Ordner zu erstellen, können Sie laufen mkdir test_{a..z}, und beginnend mit Bash 4 können Sie mit Nullen aufgefüllt Sequenzen als auch erzeugen, wie in touch foo{0001..3}, das schafft foo0001, foo0002und foo0003. Das Bash Hackers Wiki hat einen Artikel mit ein paar Beispielen für Sie.

Wenn Sie zwei verschiedene Befehle verwenden müssen, verwenden Sie zuerst eine Subshell und cddort, wie in der Antwort von @ Ignacio .

slhck
quelle
5
Ich wusste nichts über die braceErweiterung, danke!
Valter Silva
Ich habe es versucht und es scheint nicht zu funktionieren:meniac ~: mv /tmp/f1/f2/f3/f4/f5/f6/{file.txt, file2.txt} mv: cannot stat ``/tmp/f1/f2/f3/f4/f5/f6/{file.txt,': No such file or directory
Valter Silva
5
Sind Sie sicher, dass Sie Bash wie in verwenden /bin/bashund sich nicht in einem Skript befinden, das sich /bin/shim shebang befindet, oder in einer anderen Shell, die keine Klammererweiterung unterstützt? Wenn du rennst set, ist dein SHELLOPTSInhalt braceexpand?
Slhck
22
Beachten Sie, dass zwischen file.txt,und kein Leerzeichen stehen darf file2.txt.
Slhck
9
Sie können es noch kürzer machen, um Tippfehler in dem Teil zu vermeiden, der sich nicht ändert:mv /folder1/folder2/folder3/file{,-2013}.txt
Jan Fabry
74

Führen Sie die Operation in einer Subshell aus.

( cd /folder1/folder2/folder3 && mv file.txt file-2013.txt )

Die Änderung des Arbeitsverzeichnisses wird nicht an die übergeordnete Shell weitergegeben.

Ignacio Vazquez-Abrams
quelle
11
+1: Ich mag das eine, tragbarer über Muscheln als die Klammer-Erweiterung Trick (die ordentlich, aber weniger portabel ist)
Olivier Dulac
@Olivier, was lässt dich denken, dass die Klammererweiterung nicht tragbar ist? Welche Shell haben Sie im Sinn, die es nicht unterstützt?
Alexis
5
Die @alexis Brace-Erweiterung wird von POSIX nicht angegeben und ist daher nicht "von Entwurf" portierbar. ash, dash, ksh88Nicht die alte Bourne - Shell Beispiel Schalen nicht unterstützt zu erwähnen.
Juli
@jlliagre Welche der von Ihnen erwähnten Shells sind POSIX-konform? Das heißt, sie hätten keine Klammererweiterung, selbst wenn es POSIX wäre. ksh88 war vor der Ratifizierung von POSIX; Sie sollten mindestens auf ksh93 upgraden. Die einzigen zwei, die Linux interessieren, sind ash and dash, da sie in einigen kleinen eingebetteten Distributionen (busybox, iirc?) Und Rettungsdisketten verwendet werden.
Kaz
2
@Kaz Mir geht es um die Portabilität von Shell-Befehlen und die Tatsache, dass sie interaktiv sind oder nicht, spielt keine Rolle. Natürlich können Sie sich nicht darum kümmern, aber bitte akzeptieren Sie, dass die Leute anders denken. Die Tatsache, dass Sie immer bash oder eine Shell verwenden, die die Barce-Erweiterung unterstützt, bedeutet nicht, dass dies jedermanns Sache ist.
Juli
21

Wenn Sie klug sein möchten, finden Sie hier eine Erweiterung des Bash- Verlaufs

mv /folder1/folder2/folder3/file.txt !#:1:h/file-2013.txt

Ich würde das nicht selbst benutzen, da ich es unmöglich finde, es mir zu merken. Ich verwende gelegentlich das vim-Äquivalent , muss es aber fast jedes Mal nachschlagen.

Glenn Jackman
quelle
11

Sie können eine Variable festlegen. Dies hat natürlich den Nebeneffekt, dass die Variablen nicht verändert werden.

D=/folder1/folder2/folder3; mv $D/file.txt $D/file-2013.txt
sjbotha
quelle
Und natürlich können Sie den Nebeneffekt vermeiden, die Variable (n) zu belassen (D="/folder1/folder2/folder3"; mv "$D"/file.txt "$D"/file-2013.txt), indem Sie die gesamte Befehlszeile in eine Unterschale setzen: oder indem Sie einfach einen unsetBefehl am Ende hinzufügen . (Ich habe Zitate als „Best Practice“ hinzugefügt. Wenn Sie es sich zur Gewohnheit machen, immer Zitate zu verwenden, müssen Sie nicht aufhören und sich den Kopf kratzen, wenn ein Pfadname mit Sonderzeichen auftaucht.)
Scott,
@Scott Wenn Sie eine Subshell verwenden, um Nebenwirkungen zu vermeiden, ist es einfacher, cdeine Variable festzulegen, als sie zu verwenden. Nicht viel einfacher, gebe ich zu.
Isaac Rabinovitch
2

Ich mag die anderen Lösungen, aber hier ist eine andere, implementiert als Skript mit Bash-Arrays, pushd, popd:

#!/bin/bash
set -e
# from http://stackoverflow.com/a/246128/178651
script_path="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# paths relative to the script
relative_paths=( \
path1 \
path2 \
path3 \
path4
)

for relative_path in "${relative_paths[@]}"
do
  pushd "$script_path/$relative_path" > /dev/null 2>&1
  pwd
  mv filename1 filename2
  # could do other stuff in this directory...
  popd > /dev/null 2>&1
done

pushd "$script_path" > /dev/null 2>&1
# could do other stuff in same directory as script...
popd > /dev/null 2>&1
Gary S. Weaver
quelle
1

Slhck beantwortet die Frage direkt auf die einfachste Art und Weise, aber Valter mag auch die Autopop-Antwort.

pushd /folder1/folder2/folder3/; mv file.txt file-2013.txt; popd
Isaac Rabinovitch
quelle