Je suis relativement nouveau dans Bash et j'essaie de faire quelque chose qui à première vue semblait assez simple - exécuter la recherche sur une hiérarchie de répertoires pour obtenir tous les fichiers * .wma, diriger cette sortie vers une commande où je les convertis en mp3 et enregistrez le fichier converti au format .mp3. Ma pensée était que la commande devrait ressembler à ce qui suit (j'ai laissé la commande de conversion audio et j'utilise plutôt echo pour l'illustration):
$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3
Si je comprends bien, l'argument -print0 me permettra de gérer les noms de fichiers qui ont des espaces (ce que beaucoup d'entre eux font car ce sont des fichiers musicaux). Je m'attends ensuite (à la suite de xargs) à ce que chaque chemin de fichier de find soit capturé en f, et qu'en utilisant la sous-chaîne match / delete à la fin de la chaîne, je devrais faire écho au chemin de fichier d'origine avec un mp3 extension au lieu de wma. Cependant, au lieu de ce résultat, je vois ce qui suit:
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...
Donc ma question (en dehors de la spécification `` qu'est-ce que je fais mal ici '') est la suivante: les valeurs qui sont le résultat d'une opération de tuyau doivent-elles être traitées différemment dans les opérations de manipulation de chaînes que celles qui sont le résultat d'une affectation de variable ?
xargs
avecfind
. Il est livré avec une-exec
option. Pouvez-vous simplement ajouter la commande que vous allez utiliser à votre question, et quelqu'un peut vous montrer lafind
commande correcte ?{}
membre)xargs
est plus approprié queexec
. Voir ce stackpost stackoverflow.com/questions/896808/find-exec-cmd-vs-xargs pour un cas d'espèce.Réponses:
Comme d'autres réponses l'ont déjà identifié,
${f%.*}
est développé par le shell avant d'exécuter laxargs
commande. Vous avez besoin que cette expansion se produise une fois pour chaque nom de fichier, avec la variable shellf
définie sur le nom du fichier (le passage-I f
ne fait pas cela:xargs
n'a aucune notion de variable shell, il recherche la chaînef
dans la commande, donc si vous utilisé par exemple,xargs -I e echo …
il aurait exécuté des commandes comme./somedir/somefile.wmacho .mp3
).En gardant cette approche, dites
xargs
d'invoquer un shell qui peut effectuer l'expansion. Mieux, ditesfind
-xargs
est un outil largement obsolète et difficile à utiliser correctement, car les versions modernes de find ont une construction qui fait le même travail (et plus) avec moins de difficultés de plomberie. Au lieu defind … -print0 | xargs -0 command …
, exécutezfind … -exec command … {} +
.L'argument
_
est$0
dans le shell; les noms de fichiers sont passés comme arguments positionnels quifor f; do …
bouclent. Une version plus simple de cette commande exécute un shell séparé pour chaque fichier, ce qui est équivalent mais légèrement plus lent:Vous n'avez pas vraiment besoin d'utiliser
find
ici, en supposant que vous exécutez un shell raisonnablement récent (ksh93, bash ≥4.0 ou zsh). En bash, insérezshopt -s globstar
votre.bashrc
pour activer le**/
modèle glob à récurrer dans les sous-répertoires (en ksh, c'estset -o globstar
). Ensuite, vous pouvez exécuter(Si vous avez des répertoires appelés
*.wma
, ajoutez-les[ -f "$f" ] || continue
au début de la boucle.)la source
Dans le cas de l'évaluation de votre solution de $ {f%. *}. Mp3 se fait dans le shell dans lequel vous invoquez la commande entière, pas dans le shell forké par les xargs . Et dans votre shell il n'y a pas de variable f , elle est remplacée par une chaîne vide.
Solution utilisant xargs avec -I f :
Mais je le ferais de cette façon:
la source