bash - recursivamente - renombrar archivos linux masivo
Lotes de cambio de nombre de archivos con Bash (9)
Aquí hay un POSIX casi equivalente a la respuesta actualmente aceptada . Esto intercambia la expansión de parámetros Bash-only ${variable/substring/replacement}
por uno que está disponible en cualquier shell compatible con Bourne.
for i in ./*.pkg; do
mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done
La expansión de parámetro ${variable%pattern}
produce el valor de variable
con cualquier sufijo que coincida con el pattern
eliminado. (También hay ${variable#pattern}
para eliminar un prefijo).
Mantuve el subpatrón -[0-9.]*
De la respuesta aceptada aunque tal vez sea engañoso. No es una expresión regular, sino un patrón glob; por lo que no significa "un guión seguido de cero o más números o puntos". En cambio, significa "un guion, seguido de un número o un punto, seguido de cualquier cosa". El "cualquier cosa" será la coincidencia más corta posible, no la más larga. (Bash ofrece ##
y %%
para recortar el prefijo o sufijo más largo posible, en lugar del más corto).
¿Cómo puede Bash cambiar el nombre de una serie de paquetes para eliminar sus números de versión? He estado jugando con expr
y %%
, sin éxito.
Ejemplos:
Xft2-2.1.13.pkg
convierte en Xft2.pkg
jasper-1.900.1.pkg
convierte en jasper.pkg
xorg-libXrandr-1.2.3.pkg
convierte en xorg-libXrandr.pkg
Esto parece funcionar suponiendo que
- todo termina con $ pkg
- sus versiones # siempre comienzan con un "-"
quita el .pkg, luego quítatelo ...
for x in $(ls); do echo $x $(echo $x | sed ''s//.pkg//g'' | sed ''s/-.*//g'').pkg; done
Gracias por estas respuestas. También tuve algún tipo de problema. Mover archivos .nzb.queued a archivos .nzb. Tenía espacios y otros cruces en los nombres de archivo y esto resolvió mi problema:
find . -type f -name "*.nzb.queued" |
sed -ne "s/^/(/(.*/).nzb.queued/)$/mv -v /"/1/" /"/2.nzb/"/p" |
sh
Se basa en la respuesta de Diomidis Spinellis.
La expresión regular crea un grupo para el nombre de archivo completo, y un grupo para la parte anterior .nzb.queued y luego crea un comando de movimiento de shell. Con las cuerdas citadas. Esto también evita crear un bucle en el script de shell porque esto ya lo hace sed.
Haré algo como esto:
for file in *.pkg ; do
mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done
supuso que todos sus archivos están en el directorio actual. Si no es así, intente utilizar Find como se indicó anteriormente por Javier.
EDITAR : Además, esta versión no usa ninguna característica específica de bash, como las anteriores, lo que te lleva a una mayor portabilidad.
Podemos suponer que sed
está disponible en cualquier * nix, pero no podemos estar seguros de que admita sed -n
para generar comandos mv. ( NOTA: solo GNU sed
hace esto).
Aun así, bash builtins y sed, podemos acelerar rápidamente una función de shell para hacer esto.
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
mv -v "$file" "$(sed $sed_pattern <<< $file)"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
Uso
sedrename ''s|/(.*/)/(-[0-9.]*/.pkg/)|/1/2|'' *.pkg
before:
./Xft2-2.1.13.pkg
./jasper-1.900.1.pkg
./xorg-libXrandr-1.2.3.pkg
after:
./Xft2.pkg
./jasper.pkg
./xorg-libXrandr.pkg
Crear carpetas de destino:
Como mv
no crea automáticamente carpetas de destino, no podemos usar nuestra versión inicial de sedrename
.
Es un cambio bastante pequeño, por lo que sería bueno incluir esa característica:
Necesitaremos una función de utilidad, abspath
(o ruta absoluta) ya que bash no tiene esta compilación.
abspath () { case "$1" in
/*)printf "%s/n" "$1";;
*)printf "%s/n" "$PWD/$1";;
esac; }
Una vez que tenemos eso, podemos generar la (s) carpeta (s) de destino para un patrón sed / rename que incluye una nueva estructura de carpeta.
Esto asegurará que conocemos los nombres de nuestras carpetas de destino. Cuando cambiemos el nombre, tendremos que usarlo en el nombre del archivo de destino.
# generate the rename target
target="$(sed $sed_pattern <<< $file)"
# Use absolute path of the rename target to make target folder structure
mkdir -p "$(dirname $(abspath $target))"
# finally move the file to the target name/folders
mv -v "$file" "$target"
Aquí está la secuencia de comandos completa de la carpeta ...
sedrename() {
if [ $# -gt 1 ]; then
sed_pattern=$1
shift
for file in $(ls $@); do
target="$(sed $sed_pattern <<< $file)"
mkdir -p "$(dirname $(abspath $target))"
mv -v "$file" "$target"
done
else
echo "usage: $0 sed_pattern files..."
fi
}
Por supuesto, todavía funciona cuando no tenemos carpetas específicas de destino.
Si queríamos poner todas las canciones en una carpeta, ./Beethoven/
podemos hacer esto:
Uso
sedrename ''s|Beethoven - |Beethoven/|g'' *.mp3
before:
./Beethoven - Fur Elise.mp3
./Beethoven - Moonlight Sonata.mp3
./Beethoven - Ode to Joy.mp3
./Beethoven - Rage Over the Lost Penny.mp3
after:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
Ronda de bonificación ...
Usando esta secuencia de comandos para mover archivos de carpetas a una sola carpeta:
Suponiendo que quisiéramos reunir todos los archivos coincidentes y colocarlos en la carpeta actual, podemos hacerlo:
sedrename ''s|.*/||'' **/*.mp3
before:
./Beethoven/Fur Elise.mp3
./Beethoven/Moonlight Sonata.mp3
./Beethoven/Ode to Joy.mp3
./Beethoven/Rage Over the Lost Penny.mp3
after:
./Beethoven/ # (now empty)
./Fur Elise.mp3
./Moonlight Sonata.mp3
./Ode to Joy.mp3
./Rage Over the Lost Penny.mp3
Nota sobre los patrones de sedgexpress
Las reglas de patrón de sed regular se aplican en este script, estos patrones no son PCRE (expresiones regulares compatibles con Perl). Podría haber extendido la sintaxis de expresiones regulares, utilizando sed -r
o sed -E
dependiendo de su plataforma.
Ver el man re_format
conformes con POSIX man re_format
para una descripción completa de los patrones de man re_format
básicas y extendidas.
Podría usar la función de expansión de parámetros de bash
for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done
Se necesitan presupuestos para nombres de archivos con espacios.
Si todos los archivos están en el mismo directorio, la secuencia
ls |
sed -n ''s//(.*/)/(-[0-9.]*/.pkg/)/mv "/1/2" "/1.pkg"/p'' |
sh
hará tu trabajo El comando sed creará una secuencia de comandos mv , que luego puede conectar al shell. Lo mejor es ejecutar primero la tubería sin el seguimiento | sh
| sh
para verificar que el comando hace lo que quiere.
Para recurse a través de directorios múltiples, use algo como
find . -type f |
sed -n ''s//(.*/)/(-[0-9.]*/.pkg/)/mv "/1/2" "/1.pkg"/p'' |
sh
Tenga en cuenta que en sed, la secuencia de agrupación de expresiones regulares es corchetes precedidos por una barra invertida, /(
y /)
, en lugar de corchetes individuales (
y )
.
Tenía varios archivos *.txt
para renombrarlos como .sql
en la misma carpeta. a continuación funcionó para mí:
for i in /`ls *.txt | awk -F "." ''{print $1}''/` ;do mv $i.txt $i.sql; done
mejor uso sed
para esto, algo así como:
find . -type f -name "*.pkg" |
sed -e ''s/((.*)-[0-9.]*/.pkg)//1 /2.pkg/g'' |
while read nameA nameB; do
mv $nameA $nameB;
done
descifrar la expresión regular se deja como un ejercicio (como se trata de nombres de archivos que incluyen espacios)