it-swarm-es.com

Cambiar el nombre de un archivo al nombre del directorio principal en la terminal

Tengo que lidiar con una gran cantidad de archivos anidados dentro de los directorios (un archivo por directorio), parecido a algo como esto:

fred/result.txt
john/result.txt
mary/result.txt
...

Actualmente estoy usando el siguiente comando para procesar cada uno de esos archivos:

find . -maxdepth 1 -type d \( ! -name . \) -exec bash -c \"cd '{}' && processResult result.txt\" \;

Estoy buscando algo que pueda agregar al final de este comando que cambie los nombres de archivo a fred.txt, etc. y luego mueva el archivo al directorio principal para eliminar la capa adicional de directorios.

Cuál sería la mejor forma de hacer esto?

5
DTR

Podemos usar el enfoque clásico: find con while read combo:

Manifestación:

$ ls *
fred:
results.txt

jane:
results.txt

john:
results.txt

$> find . -type f -name "results.txt" -printf "/%P\n" | while read FILE ; do DIR=$(dirname "$FILE" );\                            
>  mv ."$FILE" ."$DIR""$DIR".txt;done                                                                                             
$> ls
fred/  jane/  john/
$> ls *
fred:
fred.txt

jane:
jane.txt

john:
john.txt

Use echo antes de usar mv para probar la línea con sus archivos.

Mejora menor

Podemos incrustar la llamada a bash en exec, con expansión de parámetros (específicamente eliminación de prefijos). Básicamente está incrustando un script en la llamada ejecutiva.

$> find "$PWD" -type f -name "results.txt" -exec bash -c ' DIR=$( dirname "{}"  ); echo "{}" "$DIR"/"${DIR##*/}".txt  ' \;                
/home/xieerqi/TESTDIR/fred/results.txt /home/xieerqi/TESTDIR/fred/fred.txt
/home/xieerqi/TESTDIR/jane/results.txt /home/xieerqi/TESTDIR/jane/jane.txt
/home/xieerqi/TESTDIR/john/results.txt /home/xieerqi/TESTDIR/john/john.txt

Para hacer que el comando sea general, (trabajando para cualquier archivo, no solo results.txt) simplemente elimine la parte -name "results.txt". También vea esta publicación para la solución alternativa Python a esto.

2

La pequeña secuencia de comandos python a continuación hará exactamente lo que usted describe; va a:

  • cambiar el nombre de cualquier archivo en el directorio dado, llamado results.txt, de acuerdo con su directorio superior
  • moverlo un (directorio) nivel arriba
  • elimine el directorio (ahora vacío) donde se encontraba inicialmente el archivo

El resultado:

Esta:

directory
|
├── fred
│   └── results.txt
├── john
│   └── results.txt
└── mary
    └── results.txt

se convertirá:

directory
|
├── fred.txt
│   
├── john.txt
│ 
└── mary.txt

La secuencia de comandos

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

dr = sys.argv[1]

for root, dirs, files in os.walk(dr):
    for file in files:
        if file == "results.txt":
            spl = root.split("/"); newname = spl[-1]; sup = ("/").join(spl[:-1])
            shutil.move(root+"/"+file, sup+"/"+newname+".txt"); shutil.rmtree(root)

Usar

  • Copie el siguiente código en un archivo vacío, guárdelo como move_rename.py
  • Ejecútelo con el comando:

    python3 /path/to/move_rename.py <directory_to_reorganize>
    

Nota

como siempre, primero pruébelo en una muestra pequeña

4
Jacob Vlijm

Creo que esto es legítimo (usando el comando rename basado en Perl)

find dir -name 'results.txt' -exec rename -nv -- 's|/(.*)/(.*)$|/$1/$1.txt|' {} +

p.ej. dado

$ tree dir
dir
├── fred
│   └── results.txt
├── john
│   └── results.txt
└── mary
    └── results.txt

3 directories, 3 files

entonces

$ find dir -name 'results.txt' -exec rename -v -- 's|/(.*)/(.*)$|/$1/$1.txt|' {} +
dir/fred/results.txt renamed as dir/fred/fred.txt
dir/john/results.txt renamed as dir/john/john.txt
dir/mary/results.txt renamed as dir/mary/mary.txt

resultando en

$ tree dir
dir
├── fred
│   └── fred.txt
├── john
│   └── john.txt
└── mary
    └── mary.txt

3 directories, 3 files

Si solo necesita pasar 1 nivel por debajo de dir, puede simplemente hacer

rename -vn -- 's|/(.*)/(.*)$|/$1/$1.txt|' dir/*/results.txt

Como siempre, la opción -n se deja para fines de prueba

4
steeldriver

Hacerlo a través de buscar parece demasiado complicado. ¿Por qué no usar un simple for loop?

for folder in *; do
    (cd "$folder" && processResult result.txt)  # This replaces your previous find(1)
    mv "$folder/result.txt" "${folder}.txt"
    rmdir "$folder"
done
2
Ángel

Si tiene varios archivos en carpetas con las mismas extensiones, algunos de los comandos anteriores sobrescribirán todos menos uno, renombrando todos los archivos con el mismo nombre.

folder1/file1.txt file2.txt --> folder1/folder1.txt

Para evitar esto, puede usar este comando o equivalente:

for f in */*.txt ;do fp=$(dirname "$f"); fl=$(basename "$f"); mv "$fp/$fl" "$fp/$fp"_"$fl"; done

Al mantener el nombre de archivo completo $ fl en lugar de solo la extensión, conserva todos los archivos.

folder1/file1.txt file2.txt --> folder1/folder1_file1.txt folder1_file2.txt
2
tofner