Domanda

Come può Bash rinominare una serie di pacchetti per rimuovere i loro numeri di versione? Ho giocato con expr e %% , senza risultati.

Esempi:

Xft2-2.1.13.pkg diventa Xft2.pkg

jasper-1.900.1.pkg diventa jasper.pkg

xorg-libXrandr-1.2.3.pkg diventa xorg-libXrandr.pkg

È stato utile?

Soluzione

Puoi usare la funzione di espansione dei parametri di bash

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

Le virgolette sono necessarie per i nomi di file con spazi.

Altri suggerimenti

Se tutti i file si trovano nella stessa directory, la sequenza

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

farà il tuo lavoro. Il comando sed creerà una sequenza di comandi mv , che sarà quindi possibile reindirizzare nella shell. È meglio eseguire prima la pipeline senza il | finale sh per verificare che il comando faccia quello che vuoi.

Per ricorrere attraverso più directory usa qualcosa come

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

Nota che in sed la sequenza di raggruppamento delle espressioni regolari sono parentesi precedute da una barra rovesciata, \ ( e \) , anziché parentesi singole ( e ) .

Farò qualcosa del genere:

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

suppone che tutti i tuoi file siano nella directory corrente. In caso contrario, prova a utilizzare find come consigliato sopra da Javier.

MODIFICA : inoltre, questa versione non utilizza alcuna funzionalità specifica di bash, come altre sopra, che ti porta a una maggiore portabilità.

Ecco un quasi equivalente POSIX della risposta attualmente accettata . Questo scambia l'espansione del parametro $ {variabile / sottostringa / sostituzione} solo per Bash con uno che è disponibile in qualsiasi shell compatibile con Bourne.

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

L'espansione dei parametri $ {variabile% pattern} produce il valore di variabile con qualsiasi suffisso che corrisponde a pattern rimosso. (C'è anche $ {variabile # modello} per rimuovere un prefisso.)

Ho tenuto il sottotatro - [0-9.] * dalla risposta accettata sebbene sia forse fuorviante. Non è un'espressione regolare, ma un modello glob; quindi non significa "un trattino seguito da zero o più numeri o punti". Invece, significa "un trattino, seguito da un numero o un punto, seguito da qualsiasi cosa". Il "tutto" sarà la partita più breve possibile, non la più lunga. (Bash offre ## e %% per tagliare il prefisso o il suffisso più lungo possibile, piuttosto che il più corto.)

usare meglio sed per questo, qualcosa come:

find . -type f -name "*.pkg" |
 sed -e 's/((.*)-[0-9.]*\.pkg)/\1 \2.pkg/g' |
 while read nameA nameB; do
    mv $nameA $nameB;
 done

capire l'espressione regolare è lasciato come un esercizio (come si tratta di nomi di file che includono spazi)

Possiamo supporre che sed sia disponibile su qualsiasi * nix, ma non possiamo esserne sicuri supporterà sed -n per generare comandi mv. ( NOTA: solo GNU sed fa questo.)

Anche così, bash builtin e sed, possiamo rapidamente attivare una funzione shell per fare questo.

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; }

Creazione di cartelle di destinazione:

Poiché mv non crea automaticamente cartelle target che non possiamo usare la nostra versione iniziale di sedrename .

È una modifica abbastanza piccola, quindi sarebbe bello includere quella funzione:

Avremo bisogno di una funzione di utilità, abspath (o percorso assoluto) da bash non ha questo build in.

# 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 volta che abbiamo che possiamo generare le cartelle di destinazione per a modello sed / rename che include la nuova struttura di cartelle.

Questo ci assicurerà di conoscere i nomi delle nostre cartelle di destinazione. Quando noi rinominare avremo bisogno di usarlo sul nome del file di destinazione.

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 }

Ecco lo script compatibile con la cartella completa ...

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

Ovviamente funziona ancora quando non abbiamo cartelle di destinazione specifiche anche.

Se volessimo mettere tutti i brani in una cartella, ./Beethoven/ possiamo farlo:

Uso

<*>

Round bonus ...

Usando questo script per spostare i file dalle cartelle in una singola cartella:

Supponendo di voler raccogliere tutti i file corrispondenti e posizionarli nella cartella corrente, possiamo farlo:

<*>

Nota sui modelli sed regex

In questo script si applicano le normali regole del modello sed, questi modelli no PCRE (espressioni regolari compatibili Perl). Potresti avere sed sintassi estesa delle espressioni regolari, usando sed -r o sed -E a seconda della tua piattaforma.

Vedi man re_format conforme a POSIX per una descrizione completa di sed modelli di regexp di base ed estesi.

Trovo che rinominare sia uno strumento molto più semplice da usare per questo genere di cose. L'ho trovato su Homebrew per OSX

Per il tuo esempio farei:

rename 's/\d*?\.\d*?\.\d*?//' *.pkg

La 's' significa sostituto. Il modulo è s / searchPattern / sostituzione / files_to_apply. Devi usare regex per questo che richiede un po 'di studio ma ne vale la pena.

Questo sembra funzionare supponendo che

  • tutto termina con $ pkg
  • la tua versione # inizia sempre con un " - "

rimuovi il .pkg, quindi rimuovi - ..

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

Avevo più file * .txt da rinominare come .sql nella stessa cartella. sotto ha funzionato per me:

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

Grazie per questa risposta. Ho anche avuto una sorta di problema. Spostamento di file .nzb.queued in file .nzb. Aveva spazi e altre cruft nei nomi dei file e questo ha risolto il mio problema:

find . -type f -name "*.nzb.queued" |
sed -ne "s/^\(\(.*\).nzb.queued\)$/mv -v \"\1\" \"\2.nzb\"/p" |
sh

Si basa sulla risposta di Diomidis Spinellis.

Il regex crea un gruppo per l'intero nome file e un gruppo per la parte prima di .nzb.queued e quindi crea un comando di spostamento della shell. Con le stringhe citate. Questo evita anche di creare un ciclo nello script della shell perché questo è già fatto da sed.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top