Comment supprimer le (1) des noms de fichiers à l'aide de la commande find

13

J'ai récemment converti tous mes fichiers FLAC en un taux d'échantillonnage inférieur de 44,1 kHz et une profondeur de 24 bits (car l'iPhone / iPod ne prend en charge rien de plus) en utilisant XLD sur mon Mac OS 10.7 (Lion).

Bien que j'aie dit à XLD d'écraser tous les fichiers précédents, XLD a ajouté un (1)à la fin du fichier comme

some_song.m4a

à

some_song(1).m4a

Alors maintenant, je veux supprimer cela (1)de tous les fichiers FLAC que j'ai convertis.

Je sais que j'aurais probablement pu utiliser un programme ou même un AppleScript pour renommer les fichiers, mais je voulais apprendre à utiliser l'ancienne méthode de ligne de commande.

Je sais que find . -name *\(1\).m4ava récupérer tout le fichier FLAC converti.

Ensuite, je sais que je dois faire quelque chose avec -execet mvrenommer tous les fichiers trouvés. Mais ce que je ne peux pas comprendre, c'est comment conserver le nom de fichier d'origine et supprimer uniquement le (1).

Peut-être que je dois faire une capture regex de groupe pour stocker la partie du nom de fichier que je ne veux pas modifier? Ou peut-être qu'il n'est pas possible de tout faire sur une seule ligne et que je devrais créer un script shell (ce que je ne suis pas si à l'aise de faire, mais je suis prêt à l'essayer).

Tous les conseils ou suggestions sont les bienvenus! Merci!

hobbes3
la source
5
Pourquoi le downvote? Cela semble être une question valable ...
sans rapport avec votre question particulière (on find) mais qui peut résoudre votre problème réel (conversion de fichiers audio), vous pouvez être intéressé par jeter un œil à audiotools.sourceforge.net et à cet exemple de cas (pour macosx lion) invibe.net/ LaurentPerrinet / SciBlog / 2012-04-22
meduz

Réponses:

13

N'essayez pas d'analyser la findsortie, sauf en dernier recours. Il est important de réaliser que sur les systèmes de fichiers Unix, les noms de fichiers ne sont pas des chaînes (une idée fausse courante) mais plutôt des blobs binaires qui peuvent contenir n'importe quel caractère sauf /et le caractère nul. Analyser correctement et en toute sécurité les noms de fichiers est assez pénible que 99% du temps, vous voudrez simplement éviter de le faire complètement (regardez simplement à quel point l' sedexpression dans @ yarek est poilue et même cela ne couvre pas tous les cas) . Heureusement, dans ce cas, il existe une approche beaucoup plus simple:

find . -name '*(1).m4a' -execdir sh -c \
'for arg; do mv "$arg" "${arg%(1).m4a}".m4a; done' _ {} \+
jw013
la source
approche soignée mais aussi velue que l'expression sed :) +1
yrk
1
il manque quelque chose ici ... où est done?
yrk
@yarek Merci pour la capture. La plus grande différence entre cela et l' sedapproche des tuyaux est que c'est beaucoup plus sûr. Il est toujours plus facile à lire que la soupe anti-slash regex, à condition que vous compreniez quelques constructions de script shell de base.
jw013
Tu as raison; c'est beaucoup plus propre. Et la $argvariable le rend beaucoup plus facile à lire.
hobbes3
1
@DQdlM L'extrait que j'ai posté ci-dessus est juste en train d'être findinvoqué shavec une syntaxe POSIX assez portable. Pour plus de détails, vous pouvez consulter la section sur l'expansion des paramètres , la section sur les commandes composées qui explique la forboucle et la spécification POSIXfind . En plus de ces ressources, je serais heureux de répondre à toutes vos questions spécifiques .
jw013
6

Sur Debian et Ubuntu, je peux utiliser rename 's/\(1\)//' *.m4apour résoudre votre problème.

Dom
la source
Bizarre, je peux le faire man rename, mais je n'ai pas rename: -bash: rename: command not found( which renamene montre rien).
hobbes3
@ hobbes3 sur le mac man -a renametrouve ici rename (2) - l'appel système et rename (n) - la commande TCL. Il n'y a ni rename (1) ni l'utilitaire lui-même.
yrk
1
IIRC, renameest un script perl inclus avec les exemples inclus dans certaines installations de perl, et quelques distributions l'incluent dans le chemin car c'est une commande pratique. (@Hobbes peut-être que vous pouvez le trouver sur Internet)
Kevin
@Kevin sur mon système renameest un binaire normal (pas perl). Cependant, une autre mise en garde est que toutes les versions des renameexpressions régulières ne sont pas prises en charge. La version que j'ai par exemple ne vous permet que de remplacer directement les chaînes, par exemplerename '(1)' '' *.m4a
Patrick
1
Le renamescript Perl n'est installé que par Debian et ses dérivés. D'autres systèmes Linux ont un renameutilitaire différent (montré par Patrick). OSX n'a ​​ni l'un ni l'autre.
Gilles 'SO- arrête d'être méchant'
4

Dans zsh, en utilisant zmv :

autoload zmv      # you can put this line in your .zshrc
zmv '(*)\(1\)(*)' '$1$2'

Dans le deuxième argument (le nouveau nom), $1et $2faites référence aux groupes entre parenthèses (PATTERN)dans le modèle source. Une autre façon d'écrire ce changement de nom est

zmv '(*)' '${1/\(1\)/}'
Gilles 'SO- arrête d'être méchant'
la source
3

L'approche suivante vous donne la possibilité de prévisualiser / élaguer les commandes générées avant de les exécuter, et elle est très portable: elle devrait fonctionner non seulement sur un mac, pas seulement avec bash, et pas seulement avec GNU sed; même sur les systèmes sans findcommande (1), il est possible de le remplacer par du(1) sans problème.

find . -name '*(1).m4a' |
sed 's/\(.*\)(1).m4a$/mv & \1.m4a/' 

Si vous êtes satisfait des commandes imprimées, réexécutez avec en | sh -xannexe.

Si vous vous souciez des espaces dans les noms de fichiers, ajoutez un autre s pour échapper à tous les espaces:

find . -name '*(1).m4a' |
sed -e 's/ /\\ /g' -e 's/\(.*\)(1).m4a$/mv & \1.m4a/'

Si d'autres caractères spéciaux sont attendus, cela devient un peu plus délicat:

find . -name '*(1).m4a' |
sed -e "s/'/'\\\\''/g" -e 's/\(.*\)(1).m4a$/mv '\''&'\'' '\''\1.m4a'\'/

La première fonction convertit tout 'en une forme telle que celles-ci sont prises littéralement lorsqu'elles se trouvent au milieu d' '...'une chaîne échappée. La deuxième fonction génère des commandes mv dont les arguments sont inclus à l'intérieur '...'.

yrk
la source
1
Cette commande n'échappe pas le nom de fichier (les espaces, (et tous les autres caractères spéciaux) ou n'ajoute pas de guillemets autour du nom de fichier. J'ai essayé de modifier votre commande, mais je n'ai pas pu comprendre comment ajouter des guillemets autour des deux noms de fichiers pour la mvcommande. L'une des sorties résultantes de votre commande est mv ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us(1).m4a ./The Beatles - Let It Be (MFSL LP 1-109) 24-96 Vinyl Rip/01 Two Of Us.m4a. Et si j'exécute cette commande, j'obtiens -bash: syntax error near unexpected token '('.
hobbes3
c'est censé être un devoir :) mais ok, je mettrai à jour la réponse
yrk
Soit dit en passant, GNU sed peut exécuter la commande elle-même en ajoutant une ecommande après substitution. Par exemple find . -name '*(1).m4a' | sed 's/\(.*\)(1).m4a$/mv & \1.m4a/e', la commande sera exécutée sans canalisation vers sh -x.
rush
@Rush, c'est le seul sed qui fait ça; et il doit quand même démarrer le shell, mais un nouveau pour chaque commande (rappelle le problème de la marque , n'est-ce pas?)
yrk
2
Je ne pense pas que nous devrions réduire les votes. C'est une solution valable après tout.
hobbes3
1

Voici un petit script qui le fait:

for var in `find . -type f -name "*(1).m4a"`; do
    new=`echo $var | cut -d'(' -f1`;
    mv $var $new.m4a;
done
anupam
la source
celui-ci s'étouffera sur les fichiers avec des espaces dans leurs noms; il nécessite également un stockage temporaire suffisamment grand pour`find ...`
yrk