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


y a t-il intelligent façon de faire des opérations de copie et de déplacement ou une commande pour dupliquer un fichier, sans avoir à faire de cd, puis mv aprè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 vers lequel je déplace le fichier est le même, mais je dois remettre tout le chemin et parfois cela devient agaçant. Je suis curieux de savoir s'il existe une autre façon de le faire sans avoir à remettre tout le chemin, car l'opération se ferait dans le même chemin.


72
2018-05-17 12:22


origine


Je ne peux pas croire que cela a tant de votes positifs. C'est un duplicata unix.stackexchange.com/questions/35782/... et unix.stackexchange.com/questions/66889/ - user13107
@ user13107 Il y a plusieurs façons de poser une question, y compris différentes formulations. Et si vous ne savez pas que la réponse s'appelle "expansion de l'accolade", vous ne pourrez peut-être pas la trouver tout de suite. - slhck
@ user13107 ils sont sur un site différent donc pas de doublons - Mark
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
@ user13107, c'est ce que vous obtenez pour poster sur le bon site - Samuel Edwin Ward


Réponses:


Simplement utiliser extension d'accolade:

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 de fournir plus d'arguments, bien sûr. Vous pouvez même y passer des plages, par ex. pour créer quelques dossiers de test, vous pouvez exécuter mkdir test_{a..z}, et à partir de Bash 4, vous pouvez également créer des séquences à remplissage nul, comme dans touch foo{0001..3}, qui crée foo0001, foo0002 et foo0003. le Bash Hackers Wiki a un article avec quelques exemples pour vous.

Si vous devez utiliser deux commandes différentes, utilisez un sous-shell et cd là d'abord, comme dans la réponse de @ Ignacio.


124
2018-05-17 12:29



Je ne connaissais pas le brace l'expansion, 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
Êtes-vous sûr d'utiliser Bash, comme dans /bin/bash, et vous n'êtes pas dans un script qui a /bin/sh dans le shebang ou une autre coquille qui ne supporte pas l'expansion d'accolade? Si tu cours set, faire votre SHELLOPTScontenir braceexpand? - slhck
Notez qu'il ne devrait y avoir aucun espace entre file.txt, et file2.txt. - slhck
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


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.


74
2018-05-17 12:24



+1: J'aime celui-là, plus portable entre les coques que le truc d'expansion de l'accolade (qui est net, mais moins portable) - Olivier Dulac
@Olivier, qu'est-ce qui vous fait penser que l'expansion des corsets n'est pas portable? Quel shell avez-vous en tête qui ne le supporte pas? - alexis
L'expansion de @alexis Brace n'est pas spécifiée par POSIX, elle est donc non portable "par conception". ash, dash, ksh88 sans parler de la vieille coquille de bourne sont des exemples de coquilles ne le supportant pas. - jlliagre
@jlliagre Lesquels des shells que vous avez mentionnés sont entièrement conformes à POSIX? Cela signifie qu'ils n'auraient pas d'expansion d'accolade même si c'était POSIX. ksh88 était avant la ratification de POSIX; vous devriez passer à au moins ksh93. Les deux seuls utilisateurs de Linux à prendre en compte sont les cendres et les tirets car ils sont utilisés dans de petites distributions intégrées (busybox, iirc?) Et des disques de secours. - Kaz
@Kaz Je me soucie de la portabilité des commandes shell et le fait qu'elles soient interactives ou non n'a pas d'importance. Bien sûr, vous êtes certainement libre de ne pas vous soucier de cela, mais veuillez accepter que les gens pensent autrement. Le fait que vous utilisiez toujours bash ou un shell prenant en charge l'extension Barce ne signifie pas que ce soit le cas pour tout le monde. - jlliagre


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

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

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


21
2018-05-17 13:26





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

11
2018-05-17 18:01



Et, bien sûr, vous pouvez éviter les effets secondaires de laisser la ou les variables autour en plaçant l'intégralité de la ligne de commande dans un sous-shell: (D="/folder1/folder2/folder3"; mv "$D"/file.txt "$D"/file-2013.txt), ou simplement en ajoutant un unset commande à la fin. (J'ai ajouté des guillemets comme "meilleure pratique"; si vous prenez l'habitude de toujours utiliser des guillemets, vous n'aurez pas à vous arrêter et à vous gratter la tête lorsqu'un chemin contenant des caractères spéciaux apparaît). - Scott
@Scott si vous utilisez un sous-shell pour éliminer les effets secondaires, il est plus facile de faire un cd que de définir une variable. Pas un lot plus facile, je l'avoue. - Isaac Rabinovitch


J'aime les autres solutions, mais en voici une autre, implémentée en tant que 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

2
2018-05-17 20:30





Slhck répond directement à la question de la manière la plus simple possible, mais Valter aime aussi la réponse à l'autopop, alors en voici une qui va dans le même sens;

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

1
2018-05-21 22:29