Comment exécuter des commandes dans un autre dossier sans répéter le chemin du dossier?

73

Existe-t-il un moyen astucieux de copier et de déplacer des opérations ou une commande permettant de dupliquer un fichier, sans avoir à faire un cd, puis mvaprès, dans le même dossier?

Par exemple, je dois exécuter ce qui suit:

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

Notez que le répertoire dans lequel je déplace le fichier est le même, mais je dois mettre le chemin complet à nouveau et parfois cela devient agaçant. Je suis curieux de savoir s'il existe un autre moyen de le faire sans avoir à refaire tout le chemin , car l'opération se ferait dans le même chemin.

Valter Silva
la source
9
Je ne peux pas croire que cela a eu autant de votes positifs. C'est un doublon unix.stackexchange.com/questions/35782/… et unix.stackexchange.com/questions/66889/…
user13107
13
@ user13107 Il y a plusieurs façons de poser une question, y compris un libellé différent. Et si vous ne savez pas que la réponse s'appelle "expansion du corset", vous ne pourrez peut-être pas la trouver immédiatement.
Slhck
2
@ user13107 ils sont sur un site différent, donc pas de doublons
user151019
1
Mark, merci, je ne connaissais pas cette règle sur les doublons. @slhck Oui. Je comprends. J'étais juste frustré parce que ma question sur Unix.SE a été fermée en double et que celle-ci est devenue si populaire.
user13107
3
@ user13107, c'est ce que vous obtenez pour poster sur le bon site
Samuel Edwin Ward

Réponses:

125

Il suffit d'utiliser l' expansion de corset :

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

Cela équivaut à écrire:

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

L'expansion de l'accolade vous permet naturellement de fournir plus d'arguments Vous pouvez même lui transmettre des plages, par exemple, pour créer quelques dossiers de test, vous pouvez l'exécuter mkdir test_{a..z}, et à partir de Bash 4, vous pouvez créer des séquences sans remplissage, ainsi que dans touch foo{0001..3}, ce qui crée foo0001, foo0002et foo0003. Le wiki de Bash Hackers contient un article avec quelques exemples pour vous.

Si vous devez utiliser deux commandes différentes, utilisez un sous-shell et cdlà - bas, comme dans la réponse de @ Ignacio .

slhck
la source
5
Je ne connaissais pas l' braceextension, merci!
Valter Silva
J'ai essayé et cela ne semble pas fonctionner: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
Êtes-vous sûr que vous utilisez Bash, comme dans /bin/bash, et que vous n'êtes pas dans un script /bin/shinclus dans shebang ou dans un autre shell qui ne prend pas en charge le développement d'accolades? Si tu cours set, est-ce que tu SHELLOPTScontiens braceexpand?
Slhck
22
Notez qu'il ne doit y avoir aucun espace entre file.txt,et file2.txt.
Slhck
9
Vous pouvez le rendre encore plus court, pour éviter les fautes de frappe dans la partie qui ne change pas:mv /folder1/folder2/folder3/file{,-2013}.txt
Jan Fabry
74

Exécutez l'opération dans un sous-shell.

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

Le changement de répertoire de travail ne sera pas propagé au shell parent.

Ignacio Vazquez-Abrams
la source
11
+1: Je l' aime bien, coquilles dans plus portables que l'astuce croisillon expansion ( ce qui est propre, mais moins portable)
Olivier Dulac
@Olivier, qu'est-ce qui vous fait penser que l'expansion du corset n'est pas portable? Quel shell avez-vous en tête qui ne le supporte pas?
Alexis
5
L'extension @alexis Brace n'étant pas spécifiée par POSIX, elle n'est donc pas portable "de par sa conception". ash, dash, ksh88Sans parler de l'ancienne bourne shell sont des coquilles de ne le soutiennent.
Juillet
@jlliagre Parmi les coquilles que vous avez mentionnées, lesquelles sont entièrement conformes à POSIX? Cela signifie qu'ils n'auraient pas d'expansion d'accolade même s'il s'agissait de POSIX. ksh88 était avant la ratification de POSIX; vous devriez passer à au moins ksh93. Les deux seuls utilisateurs de Linux qui s’intéressent à nous sont les cendres et les tableaux de bord car ils sont utilisés dans certaines petites distributions intégrées (busybox, iirc?) Et disques de secours.
Kaz
2
@Kaz Je me soucie de la portabilité des commandes shell et le fait qu'elles soient interactives ou non importe peu. Bien sûr, vous êtes certainement libre de ne pas vous soucier de cela, mais acceptez que les gens pensent le contraire. Le fait que vous utilisiez toujours bash ou un shell prenant en charge l'extension à la barre ne signifie pas que c'est le cas de tout le monde.
Juillet
21

Si vous voulez intelligent, voici l' expansion de l'histoire bash

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

Je ne l'utiliserais pas moi-même car je trouve impossible de le mémoriser. J'utilise occasionnellement l' équivalent vim , mais je dois le rechercher presque à chaque fois.

Glenn Jackman
la source
11

Vous pouvez définir une variable. Bien sûr, cela a pour effet secondaire de laisser les variables autour.

D=/folder1/folder2/folder3; mv $D/file.txt $D/file-2013.txt
sjbotha
la source
Et, bien sûr, vous pouvez éviter l'effet secondaire de laisser la (les) variable (s) en plaçant toute la ligne de commande dans un sous-shell:, (D="/folder1/folder2/folder3"; mv "$D"/file.txt "$D"/file-2013.txt)ou simplement en ajoutant une unsetcommande à la fin. (J'ai ajouté des guillemets en guise de «pratique exemplaire»; si vous prenez l'habitude de toujours utiliser des guillemets, vous ne devez pas vous arrêter et vous gratter la tête lorsqu'un chemin comportant des caractères spéciaux se présente.)
Scott
@ Scott Si vous utilisez un sous-shell pour éliminer les effets secondaires, il est plus facile cdde définir une variable. Pas beaucoup plus facile, je l'avoue.
Isaac Rabinovitch
2

J'aime les autres solutions, mais en voici une autre, implémentée sous forme de script avec des tableaux bash, 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
la source
1

Slhck répond directement à la question de la manière la plus simple possible, mais Valter apprécie également la réponse automatique, alors voici une réponse qui va dans le même sens.

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