2009-03-02 7 views
73

¿Cómo puede Bash cambiar el nombre de una serie de paquetes para eliminar sus números de versión? Estuve jugando con expr y %%, sin éxito.Archivos de cambio de nombre de lote con Bash

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 convierte xorg-libXrandr.pkg

+1

Tengo la intención de utilizar esto regularmente, como una secuencia de escritura de uso de una sola vez. Cualquier sistema que voy a utilizar tendrá bash, así que no le temo a los bashisms que son bastante útiles. –

+0

Más en general, vea también https://stackoverflow.com/questions/28725333/looping-over-pairs-of-values-in-bash – tripleee

Respuesta

138

Usted puede usar la función de la expansión de parámetros de fiesta se necesitan

for i in ./*.pkg ; do mv "$i" "${i/-[0-9.]*.pkg/.pkg}" ; done 

Presupuestos para nombres de archivo con espacios.

+1

¡Me gusta este el mejor hasta ahora! –

+1

También me gusta, pero usa funciones específicas de bash ... Normalmente no los uso a favor de sh "estándar". –

+2

Para material interactivo desechable de una sola línea, siempre uso esto en lugar de sed-fu. Si fuera un guión, sí, evita los basismos. – richq

3

mejor uso 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 

calcular la expresión regular se deja como ejercicio (como se trata de nombres de archivos que incluyen espacios)

+0

Gracias: Los espacios no se utilizan en los nombres. –

1

Esto parece funcionar si se asume que

  • todo termina con $ PKG
  • su versión # 's siempre comienzan con un "-"

tira de la .pkg, entonces quitarse - ..

for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done 
+0

Hay algunos paquetes que tienen un guión en su nombre (vea el tercer ejemplo). ¿Esto afecta tu código? –

+1

Puede ejecutar múltiples expresiones sed usando '-e'. P.ej. 'sed-e 's/\. pkg // g' -e 's /-.*// g''. – nojak

+0

[No analizar el resultado de 'ls'] (https://mywiki.wooledge.org/ParsingLs) y [citar las variables.] (/ Q/10067266) Consulte también http://shellcheck.net/ para diagnosticar las causas comunes problemas y antipatrones en scripts de shell. – tripleee

7

voy a hacer algo como esto:

for file in *.pkg ; do 
    mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg 
done 

supone toda su archivo está en el directorio actual. Si no es así, intente utilizar Find como se indicó anteriormente por Javier.

EDIT: Además, esta versión no usa ninguna característica específica de bash, como las anteriores, lo que le lleva a una mayor portabilidad.

+0

Todos están en el mismo directorio. ¿Qué opinas de la respuesta de rq? –

+0

Me gusta no usar funciones específicas de bash, sino más sh estándar. –

+0

Supuse que esto era para cambiar el nombre rápidamente, no para escribir la aplicación empresarial portátil de plataforma cruzada de próxima generación ;-) – richq

23

Si todos los archivos están en el mismo directorio de la secuencia

ls | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh 

va a hacer su trabajo. El comando sed creará una secuencia de comandos mv, que luego puede conectar al shell. Lo mejor es ejecutar primero la canalización sin el seguimiento | sh para verificar que el comando hace lo que desea.

Para Recurse a través de múltiples directorios usar algo como

find . -type f | 
sed -n 's/\(.*\)\(-[0-9.]*\.pkg\)/mv "\1\2" "\1.pkg"/p' | 
sh 

Nótese que en sed la expresión regular agrupación secuencia es entre paréntesis precedidos por una barra invertida, y \(\), en lugar de brazo simple y ().

+0

Perdona mi ingenuidad, pero ¿no escaparía del paréntesis con las barras diagonales que los traten como personajes literales? – devios1

+2

En sed, la secuencia de agrupación de expresiones regulares es \ (, en lugar de un solo paréntesis. –

+0

no funcionó para mí, estaría encantado de obtener su ayuda .... El sufijo del archivo FROM se adjunta al archivo TO: $ find. -type d | sed -n 's/\ (. * \) \/\ (. * \) anysoftkeyboard \ (. * \)/mv "\ 1 \/\ 2anysoftkeyboard \ 3" "\ 1 \/\ 2effectedkeyboard \ 3 "/ p '| sh >>>>>>> OUTPUT >>>> mv: cambie el nombre ./base/src/main/java/com/anysoftkeyboard/base/dictionaries a ./base/src/ main/java/com/effectedkeyboard/base/dictionaries/diccionarios: Ningún archivo o directorio –

0

Gracias por estas respuestas. También tuve algún tipo de problema. Mover archivos .nzb.queued a archivos .nzb. Tenía espacios y otra costra en los nombres de archivo y esto resuelve 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 .nzb.queued y luego crea un comando de desplazamiento de shell. Con las cuerdas citadas. Esto también evita crear un bucle en el script de shell porque esto ya lo hace sed.

+0

Cabe señalar que esto solo es cierto para GNU sed. En BSDs sed no se interpretará la opción '-n' de la misma manera. – ocodo

1

Tenía varios archivos *.txt que se cambiaron el nombre como .sql en la misma carpeta. continuación trabajó para mí:

for i in \`ls *.txt | awk -F "." '{print $1}'\` ;do mv $i.txt $i.sql; done 
+2

Puede mejorar su respuesta resumiéndola en el caso de uso real del OP. – dakab

+0

Esto tiene múltiples problemas además de una aparente sintaxis x error Visita http://shellcheck.net/ para empezar. – tripleee

1

Aquí es un equivalente aproximado en POSIX del currently accepted answer. Esto comercializa la expansión de parámetro ${variable/substring/replacement} Bash-only para 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 parámetro ${variable%pattern} produce el valor de variable con cualquier sufijo que coincide pattern eliminado. (También hay ${variable#pattern} 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 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 de la más corta.)

0

podemos suponer sed está disponible en cualquier * nix, pero no podemos estar seguros de que va a apoyar a sed -n generar comandos mv. (NOTA: Sólo GNUsed hace esto.)

Aun así, órdenes internas de bash y sed, que puede azotar rápidamente una función de shell para hacer esto.

sedrename() { 
    if [ $# -gt 1 ]; then 
    sed_pattern=$1 
    shift 
    for file in $(ls [email protected]); 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 

Creación de carpetas de destino:

Desde mv no crea automáticamente carpetas de destino que no podemos utilizando nuestra versión inicial de sedrename.

Es un cambio bastante pequeño, por lo que sería bueno para incluir esa característica:

necesitaremos una función de utilidad, abspath (o ruta absoluta) desde el golpe no tiene esta estructura en.

abspath() { case "$1" in 
       /*)printf "%s\n" "$1";; 
       *)printf "%s\n" "$PWD/$1";; 
      esac; } 

una vez que tenemos que podemos generar la carpeta (s) objetivo para una sED/cambiar el nombre de modelo que incluye la nueva estructura de carpetas.

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 carpeta de scripts conscientes completo ...

sedrename() { 
    if [ $# -gt 1 ]; then 
    sed_pattern=$1 
    shift 
    for file in $(ls [email protected]); 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 de destino específica también.

Si quisiéramos 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 ...

El uso de este script para mover archivos de carpetas en una sola carpeta:

Suponiendo que quisiéramos reunir todos los archivos coincidentes, y colocarlos en la f actual mayor, 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 expresiones regulares de sed se aplican

reglas de patrones regulares de sed en este script, estos patrones no son PCRE (Las expresiones regulares compatibles con Perl). Puede haber sed sintaxis de expresión regular extendida, usando sed -r o sed -E dependiendo de su plataforma.

Consulte la norma POSIX man re_format para obtener una descripción completa de sed basic y extended regexp patterns.

Cuestiones relacionadas