Batch renombrando archivos con Bash
-
03-07-2019 - |
Pregunta
¿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 %%
, en vano.
Ejemplos:
Xft2-2.1.13.pkg
se convierte en Xft2.pkg
jasper-1.900.1.pkg
se convierte en jasper.pkg
xorg-libXrandr-1.2.3.pkg
se convierte en xorg-libXrandr.pkg
Solución
Puedes 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 citas para los nombres de archivo con espacios.
Otros consejos
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 canalizar hacia el shell. Es mejor ejecutar primero la tubería sin el final | sh
para verificar que el comando hace lo que quieres.
Para responder a través de varios directorios 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 son paréntesis precedidos por una barra invertida, \ (
y \)
, en lugar de corchetes simples (
y )
.
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 la función de búsqueda indicada por Javier.
EDIT : Además, esta versión no usa ninguna función específica de bash, como otras anteriores, lo que te lleva a una mayor portabilidad.
Aquí hay un POSIX casi equivalente a la respuesta actualmente aceptada . Esto intercambia la expansión del parámetro $ {variable / substring / replacement}
Bash-only por una que está disponible en cualquier shell compatible con Bourne.
for i in ./*.pkg; do
mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done
El parámetro de expansión $ {variable% pattern}
produce el valor de variable
con cualquier sufijo que coincida con el patrón
eliminado. (También hay $ {variable # patrón}
para eliminar un prefijo).
Mantuve el subpatrón - [0-9.] *
de la respuesta aceptada, aunque quizás sea engañoso. No es una expresión regular, sino un patrón global; por lo que no significa "un guión seguido de cero o más números o puntos". En su lugar, significa "un guión, seguido de un número o un punto, seguido de cualquier cosa". El " cualquier cosa " Será el partido más corto posible, no el más largo. (Bash ofrece ##
y %%
para recortar el prefijo o el sufijo más largo posible, en lugar del más corto).
es mejor usar sed
para esto, algo 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
el cálculo de la expresión regular se deja como un ejercicio (al igual que con los nombres de archivos que incluyen espacios)
Podemos asumir que sed
está disponible en cualquier * nix, pero no podemos estar seguros
admitirá sed -n
para generar comandos mv. ( NOTA: Solo GNU sed
hace esto)
Aun así, bash builtins y sed, podemos preparar 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: 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
sed_pattern files..."
fi
}
Uso
abspath () { case "$1" in
/*)printf "%s\n" "$1";;
*)printf "%s\n" "$PWD/$1";;
esac; }
Creando carpetas de destino:
Dado que mv
no crea automáticamente las carpetas de destino que no podemos usar
nuestra versión inicial de sedrename
.
Es un cambio bastante pequeño, por lo que sería bueno incluir esa función:
Necesitaremos una función de utilidad, abspath
(o ruta absoluta) desde bash
no tiene esta versión en.
# 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"
Una vez que tengamos eso, podemos generar las carpetas de destino para un sed / renombrar patrón que incluye una nueva estructura de carpetas.
Esto asegurará que sepamos los nombres de nuestras carpetas de destino. Cuando nosotros cambiar el nombre, tendremos que usarlo en el nombre del archivo de destino.
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: 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
sed_pattern files..."
fi
}
Aquí está la secuencia de comandos completa de la carpeta ...
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
Por supuesto, todavía funciona cuando no tenemos carpetas de destino específicas también.
Si quisiéramos poner todas las canciones en una carpeta, ./Beethoven/
podemos hacer esto:
Uso
<*>Ronda de bonificación ...
Usando este script 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:
<*>Nota sobre los patrones sed regex
Las reglas de patrón de sed regulares se aplican en este script, estos patrones no son
PCRE (Expresiones Regulares Compatibles con Perl). Podrias haber sed
sintaxis extendida de expresiones regulares, utilizando sed -r
o sed -E
Dependiendo de su plataforma.
Consulte el man re_format
compatible con POSIX para obtener una descripción completa de
Son patrones básicos y extensos de expresiones regulares.
Me parece que cambiar el nombre es una herramienta mucho más sencilla de usar para este tipo de cosas. Lo encontré en Homebrew para OSX
Por tu ejemplo haría:
rename 's/\d*?\.\d*?\.\d*?//' *.pkg
La 's' significa sustituto. El formulario es s / searchPattern / replacement / files_to_apply. Necesitas utilizar expresiones regulares para esto, que requiere un poco de estudio, pero vale la pena el esfuerzo.
Esto parece funcionar asumiendo que
- todo termina con $ pkg
- los números de su versión siempre comienzan con un " - "
quita el .pkg, luego quítalo - ..
for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done
Tenía varios archivos de * .txt
para ser renombrados 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
Gracias por estas respuestas. También tuve algún tipo de problema. Mover archivos .nzb.queued a archivos .nzb. Tenía espacios y otros nombres 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 todo el nombre de archivo y un grupo para la parte anterior a .nzb.queued y luego crea un comando de movimiento de shell. Con las cadenas citadas. Esto también evita la creación de un bucle en el script de shell porque esto ya lo ha hecho sed.