Comment Bash peut-il renommer une série de packages pour supprimer leurs numéros de version? J'ai joué 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
Vous pouvez utiliser la fonction d'extension 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.
Si tous les fichiers se trouvent 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 créera une séquence de commandes mv, que vous pourrez ensuite diriger vers le shell. Il est préférable d'exécuter d'abord le pipeline sans le _ | sh
afin de vérifier que la commande fait ce que vous voulez.
Pour récuser dans plusieurs répertoires, 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 des crochets précédés d'une barre oblique inverse, \(
et \)
, plutôt que des crochets simples (
et )
.
Je vais faire quelque chose comme ça:
for file in *.pkg ; do
mv $file $(echo $file | rev | cut -f2- -d- | rev).pkg
done
supposé que tous vos fichiers se trouvent dans le répertoire courant. Sinon, essayez d'utiliser find comme indiqué ci-dessus par Javier.
EDIT : De plus, cette version n'utilise pas de fonctionnalités spécifiques à bash, comme les autres ci-dessus, ce qui vous amène à plus de portabilité.
Voici un quasi-équivalent POSIX du réponse actuellement acceptée . Cela échange le Bash uniquement ${variable/substring/replacement}
extension des paramètres pour celui qui est disponible dans n'importe quel shell compatible Bourne.
for i in ./*.pkg; do
mv "$i" "${i%-[0-9.]*.pkg}.pkg"
done
L'expansion des paramètres ${variable%pattern}
produit la valeur de variable
avec tout suffixe correspondant à pattern
supprimé. (Il y a aussi ${variable#pattern}
pour supprimer un préfixe.)
J'ai gardé le sous-modèle -[0-9.]*
de la réponse acceptée bien qu'elle soit peut-être trompeuse. Ce n'est pas une expression régulière, mais un motif global; cela ne signifie donc pas "un tiret suivi de zéro ou plusieurs nombres ou points". Au lieu de cela, cela signifie "un tiret, suivi d'un nombre ou d'un point, suivi de n'importe quoi". Le "n'importe quoi" sera la correspondance la plus courte possible, pas la plus longue. (Bash propose ##
et %%
pour rogner le préfixe ou le suffixe le plus long possible, plutôt que le plus court.)
Nous pouvons supposer que sed
est disponible sur n'importe quel * nix, mais nous ne pouvons pas être sûrs qu'il supportera sed -n
pour générer des commandes mv. (REMARQUE: Seulement GNU sed
fait cela.)
Même ainsi, bash builtins et sed, nous pouvons rapidement concocter une fonction Shell pour ce 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: $0 sed_pattern files..."
fi
}
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
Étant donné que mv
ne crée pas automatiquement les dossiers cibles, nous ne pouvons pas utiliser notre version initiale de sedrename
.
C'est un assez petit changement, donc ce serait bien d'inclure cette fonctionnalité:
Nous aurons besoin d'une fonction utilitaire, abspath
(ou chemin absolu) car bash n'a pas cette build in.
abspath () { case "$1" in
/*)printf "%s\n" "$1";;
*)printf "%s\n" "$PWD/$1";;
esac; }
Une fois que nous avons cela, nous pouvons générer le ou les dossiers cibles pour un modèle sed/rename qui inclut une nouvelle structure de dossiers.
Cela garantira que nous connaissons les noms de nos dossiers cibles. Lorsque nous renommerons, nous devrons l'utiliser sur le nom du fichier cible.
# 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"
Voici le script complet du dossier ...
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: $0 sed_pattern files..."
fi
}
Bien sûr, cela fonctionne toujours lorsque nous n'avons pas de dossiers cibles spécifiques.
Si nous voulions mettre toutes les chansons dans un dossier, ./Beethoven/
nous pouvons le faire:
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
Utilisation de ce script pour déplacer des fichiers de dossiers dans un seul dossier:
En supposant que nous voulions rassembler tous les fichiers correspondants et les placer dans le dossier actuel, nous pouvons le faire:
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
Les règles de modèle sed standard s'appliquent dans ce script, ces modèles ne sont pas PCRE (Perl Compatible Regular Expressions). Vous pourriez avoir sed la syntaxe d'expression régulière étendue, en utilisant soit sed -r
ou sed -E
selon votre plateforme.
Voir la conformité POSIX man re_format
pour une description complète des modèles regexp basiques et étendus de sed.
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
comprendre l'expression régulière est laissé comme un exercice (tout comme pour les noms de fichiers qui incluent des espaces)
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 l'expression régulière pour cela, ce qui nécessite un peu d'étude, mais cela en vaut la peine.
J'ai eu plusieurs *.txt
fichiers à 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
Cela semble fonctionner en supposant que
retirer le .pkg, puis retirer - ..
for x in $(ls); do echo $x $(echo $x | sed 's/\.pkg//g' | sed 's/-.*//g').pkg; done
Merci pour ces réponses. J'ai aussi eu une sorte de problème. Déplacement de fichiers .nzb.queued vers des fichiers .nzb. Il y avait des espaces et d'autres creux 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 régulière crée un groupe pour le nom de fichier entier et un groupe pour la partie avant .nzb.queued, puis crée une commande de déplacement du shell. Avec les cordes citées. Cela évite également de créer une boucle dans le script Shell car cela est déjà fait par sed.