bash - programa - script linux ejemplos
Creación de scripts para la gestión de archivos con una gran cantidad de archivos (3)
Tengo una configuración de máquina OSX tres que estaba utilizando syncthing para mantener las unidades compartidas sincronizadas de forma remota. Alguien cometió algunos errores y muchos archivos terminaron siendo renombrados.
Así que a lo largo de este disco tengo situaciones donde hay un archivo de tamaño 0KB llamado, por ejemplo, file.jpg
y otro archivo con tamaño real llamado file.sync-confilct201705-4528.jpg
. Necesito buscar todo el disco recursivamente y mientras encuentro un archivo con la cadena de conflicto de sincronización, verifique si hay el mismo archivo sin la cadena ''conflicto de sincronización'' junto con un tamaño de 0 KB. Si es así, necesito cambiar el nombre del archivo de conflicto de sincronización para sobrescribir el archivo de 0 KB.
He considerado abordar esto con un script bash o un script Perl. Usar bash Creo que usar el comando ''find'' con -regex me ayudaría a comenzar, pero realmente no sé cómo procesar los resultados y ejecutar la próxima prueba de búsqueda. Estoy estudiando y trabajando en eso.
Mismo problema con Perl. Puedo avanzar en el primer paso usando File :: Find: buscar y seleccionar lo que necesito usando regex para filtrar los archivos, pero una vez más estoy atascado para llegar al siguiente paso, que sería encontrar el archivo original en el mismo directorio y realizando la función de movimiento de archivo necesaria.
En ambos casos, estoy dispuesto a dedicar tiempo para resolverlo, pero me pregunto cuáles serán las advertencias. ¿Pueden estos dos escenarios manejar el reaprovisionamiento de una gran cantidad de archivos sin excepción? ¿Hay quizás un mejor enfoque que cualquiera pueda recomendar?
Crea una función para arreglar el nombre:
$ function fixname() { file="$1"; newname=$( echo "$file" | sed "s/sync-conflict.*/.jpg$/.jpg/" ); if [ -f "$newname" -a ! -s "$newname" ]; then mv "$file" "$newname"; fi; }
O extiéndase un poco:
function fixname() {
file="$1"
newname=$( echo "$file" | sed "s/sync-conflict.*/.jpg$/.jpg/" )
# If empty file exists
if [ -f "$newname" -a ! -s "$newname" ]; then
mv "$file" "$newname"
fi
}
Exportar la función:
$ export -f fixname
Ejecuta find para ejecutar la función:
$ find . -type f -name /*sync-conflict/*.jpg -exec bash -c ''fixname {}'' bash /;
Advertencia: no funcionará con espacios o caracteres funky en los nombres de archivo.
find
es genial Pero como habrás notado, necesitas más.
Lo que te atrapa en este escenario es la capacidad de buscar recursivamente y unir ciertos patrones. Tal como sucede en la versión 4 de Bash, puedes hacerlo bien en el shell.
(Tenga en cuenta que macOS se envía con la versión 3 de bash, por lo que para esta solución, deberá instalar bash 4 desde Macports , Homebrew o Fink ).
$ shopt -s globstar nullglob
$ for file in **/*sync-confilct2017*.*; do echo mv -v "$file" "${file%sync-conf*}${file##*.}"; done
mv -v file.sync-confilct201705-4528.jpg file.jpg
mv -v foo/bar.sync-confilct201705-4528.ext foo/bar.ext
Puede eliminar el echo
para ejecutar realmente el comando mv
.
La forma en que esto funciona es que el doble asterisco, **
, es tratado por bash como un *
que se repite. Estamos usando la expansión de parámetros para quitar las partes del nombre de archivo que queremos para construir el nombre de archivo "objetivo".
Una buena herramienta en Perl para esto es File :: Find :: Rule .
Encuentre todos sync-conflict
archivos de sync-conflict
, luego pruebe si los archivos correspondientes existen y son de tamaño cero
use warnings;
use strict;
use FindBin qw($RealBin);
use File::Copy qw(move);
use File::Find::Rule;
my $dir = shift || ''.''; # top of hierarchy to search (from command line, or ./)
my @conflict_files = File::Find::Rule
->file->name(''*sync-conflict*.jpg'')->in($dir);
foreach my $conflict (@conflict_files)
{
my ($file) = $conflict =~ m|(.*)/.sync-conflict|;
$file .= ''.jpg'';
if (-z "$RealBin/$file") {
print "Rename $conflict to $file/n"
#move($conflict, $file) or warn "Can''t move $conflict to $file: $!";
}
}
Esto crea el archivo de nombre del file
para cada file
file.sync-conflict
y aplica -z
file test (-X) , que prueba tanto la existencia como el tamaño cero. Luego cambia el nombre del archivo utilizando el núcleo File :: Copy .
Tenga en cuenta que los operadores de pruebas de archivos necesitan la ruta completa, mientras que File::Find::Rule
devuelve la ruta relativa al $dir
que busca. Yo uso $RealBin
proporcionado por FindBin , que es la ruta al directorio donde se inició el script con todos los enlaces resueltos, para compilar la ruta completa de -z
.
Descomente la línea de move
después de realizar suficientes pruebas (y de haber hecho una copia de seguridad primero).
El código hace algunas suposiciones sobre los nombres de los archivos, por favor ajuste según sea necesario. Se espera que el $dir
proporcionado en la línea de comando sea relativo al directorio del script.