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?
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.
La pequeña secuencia de comandos python a continuación hará exactamente lo que usted describe; va a:
results.txt
, de acuerdo con su directorio superiorEl resultado:
Esta:
directory
|
├── fred
│ └── results.txt
├── john
│ └── results.txt
└── mary
└── results.txt
se convertirá:
directory
|
├── fred.txt
│
├── john.txt
│
└── mary.txt
#!/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)
move_rename.py
Ejecútelo con el comando:
python3 /path/to/move_rename.py <directory_to_reorganize>
como siempre, primero pruébelo en una muestra pequeña
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
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
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