web-dev-qa-db-fra.com

Puis-je renommer des fichiers d'un dossier portant le nom du dossier parent?

c'est une situation unique, qui peut être mieux expliquée avec un exemple

Exemple 1 Supposons que le dossier A, B.mkv et C.srt soient deux fichiers dans le dossier. Je souhaite renommer ces fichiers en A.mkv et A.srt.

par exemple, un dossier appelé American Sniper (2014) et American.Sniper.2014.1080p.BluRay.x264.YIFY.mkv et American.Sniper.2014.Subtitle.BluRay.ShaAnig.srt sont deux fichiers dans le dossier. Vous souhaitez renommer ces fichiers en American Sniper (2014).mkv et American Sniper (2014).srt.

Exemple 2

Cela devrait être récursif, disons que j'ai un dossier appelé A, B et C sont deux sous-dossiers du dossier A. le contenu de B et C est comme suit

Example

Devrait être converti en

Result

Même quand j'exécute l'algorithme/script/commande sur le dossier A

La 3ème chose à faire, c'est d'ignorer les fichiers cachés.

exemple

Example

devrait aboutir à

Result

enfin, il devrait fonctionner sur plusieurs dossiers à la fois

2
Sumeet Deshmukh

Il y a plusieurs façons d'aborder cette question. Veuillez lire attentivement les instructions pour de meilleurs résultats.

Dans cette réponse:

  1. Approche python
  2. find + bash approche
  3. approche bash uniquement avec globstar

1. Python solution

Python est un langage assez puissant pour l’administration système et permet de parcourir l’arborescence de répertoires à l’aide de la fonction os.walk(). Dans le script présenté ci-dessous, c'est exactement ce que nous faisons. Nous trouvons tous les fichiers et les exploitons sur chacun d'entre eux en déterminant le chemin complet de chaque fichier, son extension et en le renommant via la fonction os.rename().

Contenu du script

#!/usr/bin/env python3
import os,sys

def get_all_files(treeroot):
    for dir,subdirs,files in os.walk(treeroot):
         for f in files: 
             if f in __file__: continue
             fullpath = os.path.realpath( os.path.join(dir,f) )
             extension = fullpath.split('.')[-1]
             newpath = os.path.join(dir, dir + '.' + extension)
             print('Move ' + fullpath + ' to ' + newpath   )
             # os.rename(fullpath,newpath)


def main():
    top_dir="."
    # If directory not given, assume cwd
    if len(sys.argv) == 2: top_dir=sys.argv[1]
    get_all_files(top_dir)

if __== '__main__' : main()

NOTE: très très important est que pour renommer les fichiers, vous devez supprimer # avant # os.rename(fullpath,newpath).

Mise en place du script

Toutes les règles standard pour les scripts s'appliquent: - enregistrez-le sous le nom add_location_name.py dans le répertoire situé le plus haut - créez un exécutable avec chmod +x ./add_location_name.py - exécutez-le avec ./add_location_name.py

Test du script

Voici un exemple de la façon dont cela fonctionne dans la pratique. J'ai créé un répertoire avec deux autres, Movie A (2016) et Movie B (2016). À l'intérieur, ils ont tous deux deux fichiers. Notre script habite dans le même répertoire:

$ tree                                                        
.
├── add_location_name.py
├── Movie A (2014)
│   ├── filea.mkv
│   └── fileb.srt
└── Movie B (2016)
    ├── filea.mkv
    └── fileb.srt

Ainsi, lorsque nous exécuterons le script, nous verrons le résultat suivant:

$ ./add_location_name.py                                                                                              
Move /home/xieerqi/testdir/Movie A (2014)/fileb.srt to ./Movie A (2014)/./Movie A (2014).srt
Move /home/xieerqi/testdir/Movie A (2014)/filea.mkv to ./Movie A (2014)/./Movie A (2014).mkv
Move /home/xieerqi/testdir/Movie B (2016)/fileb.srt to ./Movie B (2016)/./Movie B (2016).srt
Move /home/xieerqi/testdir/Movie B (2016)/filea.mkv to ./Movie B (2016)/./Movie B (2016).mkv

2. Solution via la commande find et l'indicateur -exec

La commande find est utile à bien des égards, notamment lorsque vous effectuez des opérations sur plusieurs niveaux d'arborescence de répertoires. Dans ce cas particulier, nous pouvons l’utiliser pour filtrer tous les fichiers et les renommer.

Tout d’abord, laissez-moi vous donner la solution:

find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}" \;

Ça a l'air long et effrayant, non? Mais ne vous inquiétez pas, voyons comment cela fonctionne.

Disséquer la commande

Tout d’abord, vous devez reconnaître qu’il y a deux choses qui se passent: la commande find localise tous les fichiers et la partie bash permet de renommer la partie. À l'extérieur, nous voyons la commande simple:

find -type f -exec <COMMAND> \;

Cela ne trouve que tous les fichiers (pas de répertoires ni de liens symboliques) et appelle une autre commande chaque fois qu’il trouve un fichier. Dans ce cas, le COMMAND spécifique que nous avons est bash.

Alors, que fait la partie bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";mv "$1" "$fp"/"$fn"."$px"' sh "{}"? Bien, reconnaissons tout d’abord la structure: bash -c 'command1;command2' arg0 arg1. Chaque fois que le drapeau -c est utilisé, le premier argument de ligne de commande arg0 sera défini comme $0, nom de shell, de sorte qu'il n'a pas d'importance, mais "{}" est important. Il s’agit de l’espace réservé cité pour le nom de fichier que find passera en tant qu’argument à bash.

À l'intérieur de la commande bash, nous extrayons le chemin du fichier fp=$(dirname "$1"), le nom du répertoire fn=$(basename "$fp") et l'extension du fichier ou le préfixe px="${1##*.}". Tout cela fonctionne très bien puisque nous exécutons la commande à partir du répertoire le plus haut (très important!). Enfin, le mv "$1" "$fp"/"$fn"."$px"' sh "{}" renomme le fichier d'origine que find nous a attribué au nouveau nom de fichier que nous construisons avec "$fp"/"$fn"."$px" en utilisant toutes ces variables.

Exemple de fonctionnement

Le test de la commande est effectué sur le même répertoire que précédemment:

$ tree
.
├── Movie A (2014)
│   ├── filea.mkv
│   └── fileb.srt
└── Movie B (2016)
    ├── filea.mkv
    └── fileb.srt

2 directories, 4 files

Et si nous exécutons la commande, avec echo au lieu de mv, nous pouvons voir que chaque nom de fichier est renommé, respectivement.

$ find -type f -exec bash -c 'fp=$(dirname "$1");fn=$(basename "$fp");px="${1##*.}";echo "$1" "$fp"/"$fn"."$px"' sh ">
./Movie A (2014)/fileb.srt ./Movie A (2014)/Movie A (2014).srt
./Movie A (2014)/filea.mkv ./Movie A (2014)/Movie A (2014).mkv
./Movie B (2016)/fileb.srt ./Movie B (2016)/Movie B (2016).srt
./Movie B (2016)/filea.mkv ./Movie B (2016)/Movie B (2016).mkv

Rappelez-vous: la commande ci-dessus utilise echo uniquement à des fins de test. Lorsque vous utilisez mv, il n'y a pas de sortie, la commande est donc silencieuse.


Approche 3.Simpler: Bash et glob star

Les deux approches ci-dessus utilisent une traversée d'arbre récursive. Comme dans votre exemple, vous n’avez que deux niveaux dans l’arborescence (répertoire movie et fichiers), nous pouvons simplifier notre commande Shell précédente comme suit:

for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done

Encore une fois, même idée - remplacez echo par mv lorsque vous êtes sûr qu’il fonctionne correctement.

Les résultats du test sont les mêmes:

$ tree                                                                                                                
.
├── add_location_name.py
├── Movie A (2014)
│   ├── filea.mkv
│   └── fileb.srt
└── Movie B (2016)
    ├── filea.mkv
    └── fileb.srt

2 directories, 5 files
$ for f in */* ;do fp=$(dirname "$f"); ext="${f##*.}" ; echo "$f" "$fp"/"$fp"."$ext" ;done                            
Movie A (2014)/filea.mkv Movie A (2014)/Movie A (2014).mkv
Movie A (2014)/fileb.srt Movie A (2014)/Movie A (2014).srt
Movie B (2016)/filea.mkv Movie B (2016)/Movie B (2016).mkv
Movie B (2016)/fileb.srt Movie B (2016)/Movie B (2016).srt
3

Donné

$ tree
.
└── A
    ├── B
    │   ├── somefile
    │   ├── T.txt
    │   ├── X.srt
    │   └── Z.mkv
    └── C
        ├── somefile
        ├── T.txt
        ├── W.mkv
        └── Y.srt

3 directories, 8 files

ensuite

$ find A -type f \( -name '*.mkv' -o -name '*.srt' \) -not -name '.*' -exec sh -c '
  for f; do 
    dir="${f%/*}" ; ext="${f##*.}" ; new="${dir##*/}"
    echo mv -- "$f" "${dir}/${new}.${ext}"
  done' sh {} +
mv -- A/C/Y.srt A/C/C.srt
mv -- A/C/W.mkv A/C/C.mkv
mv -- A/B/Z.mkv A/B/B.mkv
mv -- A/B/X.srt A/B/B.srt

(retirez la echo une fois que vous êtes bien sûr il va faire ce que vous voulez).

3
steeldriver