Question

Comment Bash peut-il renommer une série de packages pour supprimer leurs numéros de version? Je me suis amusé avec expr et %% , en vain.

Exemples:

Xft2-2.1.13.pkg devient Xft2.pkg

jasper-1.900.1.pkg devient jasper.pkg

xorg-libXrandr-1.2.3.pkg devient xorg-libXrandr.pkg

Était-ce utile?

La solution

Vous pouvez utiliser la fonctionnalité d'expansion des paramètres de bash

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

Des citations sont nécessaires pour les noms de fichiers avec des espaces.

Autres conseils

Si tous les fichiers sont dans le même répertoire, la séquence

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

fera votre travail. La commande sed va créer une séquence de commandes mv , que vous pouvez ensuite diriger vers le shell. Il est préférable de commencer par exécuter le pipeline sans le | sh afin de vérifier que la commande fait ce que vous voulez.

Pour recurse dans plusieurs annuaires, utilisez quelque chose comme

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

Notez que dans sed , la séquence de regroupement d'expressions régulières est constituée de crochets précédés d'une barre oblique inversée, \ ( et \) , plutôt que de simples crochets. ( et ) .

Je vais faire quelque chose comme ça:

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

suppose que tous vos fichiers sont dans le répertoire en cours. Sinon, essayez d’utiliser find comme conseillé par Javier ci-dessus.

MODIFIER : cette version n'utilise aucune fonctionnalité spécifique à bash, contrairement à ce qui précède, ce qui vous permet de bénéficier d'une plus grande portabilité.

Voici un équivalent POSIX de la réponse actuellement acceptée . Ceci échange le développement du paramètre $ {variable / sous-chaîne / remplacement} réservé à Bash par un développement disponible dans tout shell compatible avec Bourne.

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

Le paramètre expansion $ {variable% pattern} produit la valeur de variable avec tout suffixe correspondant au modèle supprimé. (Il existe également $ {variable # motif} pour supprimer un préfixe.)

J'ai gardé le sous-modèle - [0-9.] * de la réponse acceptée bien que ce soit peut-être trompeur. Ce n'est pas une expression régulière, mais un motif glob; cela ne signifie donc pas "un tiret suivi de zéro ou plusieurs chiffres ou points". Au lieu de cela, cela signifie "un tiret, suivi d'un nombre ou d'un point, suivi de rien". Le " n'importe quoi " sera le match le plus court possible, pas le plus long. (Bash propose ## et %% pour couper le préfixe ou le suffixe le plus long possible, plutôt que le plus court.)

mieux utiliser sed pour cela, quelque chose comme:

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

déterminer l'expression régulière est laissé comme un exercice (comme pour les noms de fichiers qui incluent des espaces)

Nous pouvons supposer que sed est disponible sur n'importe quel * nix, mais nous ne pouvons pas être sûrs cela supportera sed -n pour générer des commandes mv. ( REMARQUE: seulement sed GNU le fait).

Même dans ce cas, nous pouvons rapidement créer une fonction shell pour le faire.

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 }

Utilisation

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

Création des dossiers cible:

Étant donné que mv ne crée pas automatiquement les dossiers cibles que nous ne pouvons pas utiliser. notre version initiale de sedrename .

Il s’agit d’un changement relativement modeste, il serait donc bon d’inclure cette fonctionnalité:

Nous aurons besoin d'une fonction utilitaire, abspath (ou chemin absolu) depuis bash n'a pas cette construction.

# 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"

Une fois que nous avons cela, nous pouvons générer le ou les dossiers cibles d’un modèle sed / rename qui inclut une nouvelle structure de dossiers.

Cela nous permettra de connaître les noms de nos dossiers cibles. quand nous renommer nous devrons l’utiliser sur le nom du fichier cible.

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 }

Voici le script prenant en charge les dossiers complets ...

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

Bien sûr, cela fonctionne quand on n'a pas de dossiers cibles spécifiques aussi.

Si nous voulions mettre tous les morceaux dans un dossier, ./ Beethoven / , nous pouvons le faire:

Utilisation

<*>

Tour bonus ...

Utilisation de ce script pour déplacer des fichiers de dossiers vers un seul dossier:

En supposant que nous voulions rassembler tous les fichiers correspondants et les placer dans le dossier en cours, nous pouvons le faire:

<*>

Note sur les modèles regex sed

Les règles habituelles relatives aux modèles sed s'appliquent dans ce script. Ces modèles ne sont pas PCRE (expressions régulières compatibles Perl). Vous auriez pu sed syntaxe d'expression régulière étendue, en utilisant sed -r ou sed -E en fonction de votre plate-forme.

Voir le man re_format conforme à POSIX pour une description complète de Modèles regexp de base et étendus.

Je trouve que renommer est un outil beaucoup plus simple à utiliser pour ce genre de chose. Je l'ai trouvé sur Homebrew pour OSX

Pour votre exemple, je ferais:

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

Le 's' signifie substitut. Le formulaire est s / searchPattern / replacement / files_to_apply. Vous devez utiliser regex pour cela, ce qui nécessite un peu d’étude, mais cela en vaut la peine.

Cela semble fonctionner en supposant que

  • tout se termine par $ pkg
  • votre numéro de version commence toujours par un "- -"

éliminez le .pkg, puis éliminez - ..

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

J'avais plusieurs fichiers *. txt à renommer .sql dans le même dossier. ci-dessous a fonctionné pour moi:

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

Merci pour cette réponse. J'ai aussi eu une sorte de problème. Déplacement des fichiers .nzb.queued vers des fichiers .nzb. Il y avait des espaces et autres noms dans les noms de fichiers et cela a résolu mon problème:

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

Il est basé sur la réponse de Diomidis Spinellis.

L'expression rationnelle crée un groupe pour le nom de fichier complet et un groupe pour la partie précédant .nzb.queued, puis crée une commande de déplacement de shell. Avec les cordes citées. Cela évite également de créer une boucle dans un script car cela est déjà fait par sed.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top