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

¿Fue útil?

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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top