Comment puis-je utiliser deux paramètres à la fois avec xargs?

18

J'ai besoin de convertir des vidéos, mais je ne sais pas où elles se trouvent, j'ai donc besoin d' findeux. Comment puis-je donner le résultat et un nom de fichier de sortie à FFmpeg avec xargs?

J'ai déjà découvert que je peux construire les deux paramètres avec cette commande:

find . -iname "*.mov" -printf "%p %f\n"

Je ne trouve rien de connexe dans le xargsmanuel. Je veux quelque chose comme ça:

find . -iname "*.mov" -printf "%p %f\n" | xargs ffmpeg -i {param1} -f flv {param2}

Comment puis-je faire ceci?

kissgyorgy
la source
Ceux d'entre vous qui tombent sur cette question parce que vous cherchez une réponse à plusieurs arguments avec xargs en général pourraient vouloir consulter stackoverflow.com/questions/3770432/… .
Chris Oldwood

Réponses:

4

Quelque chose comme ça fera l'affaire et de préserver le chemin complet, l' espace de poignée, changement de nom folder/movie.movà folder/movie.flv, etc.

find . -name "*.mov" | while read movie;do
  ffmpeg -i "$movie" -f flv "${movie%.mov}.flv"
done

Et si je vous ai mal compris et que vous voulez tous les films .flv dans le répertoire courant, utilisez celui-ci à la place:

find . -name "*.mov" | while read movie;do
  ffmpeg -i "$movie" -f flv "$(basename "${movie%.mov}.flv")"
done
Mattias Ahnberg
la source
3
Si vous le voulez comme un oneliner, mettez simplement tout sur une seule ligne. ;)
Mattias Ahnberg
9

La raison pour laquelle les gens utilisent xargsen combinaison avec find est que plusieurs noms de fichiers seront transmis au même appel de programme quel que soit le xargslancement du programme . Par exemple, si findrenvoie les fichiers foo , bar et baz , les éléments suivants ne s'exécuteront mvqu'une seule fois:

find sourceDir [...] -print0 | xargs -0 mv -t destDir

En effet, il appelle mvcomme suit:

mv -t destDir foo bar baz

Si vous n'avez pas besoin ou ne voulez pas ce comportement (comme je suppose que c'est le cas ici), vous pouvez simplement utiliser find's' -exec.


Dans ce cas, une solution simple serait d'écrire un court script shell, comme suit:

#!/usr/bin/env bash
[[ -f "$1" ]] || { echo "$1 not found" ; exit 1 ; }
P="$1"
F="$( basename $P )"
ffmpeg -i "$P" -f flv "$F"

Enregistrez sous myffmpeg.shet exécutez chmod +x myffmpeg.sh. Ensuite, exécutez ce qui suit:

find . -iname "*.mov" -exec /path/to/myffmpeg.sh {} \;

Cela invoquera le script shell une fois pour chaque fichier trouvé. Le script shell extrait à son tour le nom de fichier du chemin complet et appelle ffmpegavec les arguments appropriés.

Daniel Beck
la source
4

Je n'ai pas obtenu la solution que j'attendais, alors j'ai découvert la mienne. @ La réponse de Daniel est bonne, mais elle nécessite un script shell. Un one liner est plus rapide, et je l'aime mieux :) aussi une solution plus simple que d'écrire un script.

Je pourrais utiliser un argument et le traiter avec basenameet en utilisantsh -c

find . -iname "*.mov" -print0 | xargs -0 -i sh -c 'ffmpeg -i {} -f flv `basename {}`'

Le -i indique à xargs de le remplacer {}par l'argument actuel.
La sortie de la commande à l'intérieur de `` imprimé sur la sortie standard (fonction bash) basename {}sera donc évaluée comme le nom de fichier nu et imprimée.
-0 pour gérer correctement les noms de fichiers spéciaux, mais vous devez passer les paramètres avec l'option -print0 avec find

kissgyorgy
la source
3

Vous devez le faire comme toutes les réponses là-haut, mais l'utilisation EXACTE de xargs sera quelque chose comme ceci:

echo argument1 argument2 argument3 | xargs -l bash -c 'echo this is first:$0 second:$1 third:$2' | xargs

Donc dans votre cas ce sera:

find . -iname "*.mov" -printf "%p %f\n" | xargs -l bash -c 'echo ffmpeg -i $0 -f flv $1' | xargs

PS: Cela répond à la question sur les xargs pour ceux qui recherchent une réponse exacte pour plusieurs xargsparamètres dans une seule xargscommande.

sauliux zyziux
la source
1

Pourquoi ne pas simplement déplacer les options de ffmpeg pour qu'elles correspondent au format des résultats de la commande find ?

find . -iname "*.mov" -printf "%p %f\n" | xargs -r -n2 ffmpeg -f flv -i

Notez l'ajout de l' option -r à xargs pour l'empêcher d'exécuter ffmpeg si aucun fichier .mov n'est trouvé.

J'ai ajouté l' option -n2 à xargs pour limiter le nombre d'éléments traités par xargs à deux à la fois. Dans ce cas, les éléments sont le chemin d'accès au fichier et le nom du fichier. Si aucune option -n n'est définie, xargs traitera autant d'éléments d'entrée que possible en une seule exécution.

ZaSter
la source
Cela ne fonctionnera pas une fois de plus qu'un fichier est trouvé AFAICT.
Daniel Beck
Cela ne fonctionne qu'une seule fois pour moi aussi, mais je ne comprends pas pourquoi cela s'arrête après le premier. Une idée ?
kissgyorgy
en fait ça ne marche pas
kissgyorgy
Daniel-Beck et @Walkman avaient raison au sujet de l'exécution unique. Pour résoudre ce problème, j'ai modifié cette réponse pour limiter le nombre d'éléments xargs processus à la fois via l' option -n2 . Sans -n2 , xargs s'exécute une seule fois s'il peut gérer le nombre d'éléments d'entrée qu'il reçoit via le canal. Il convient également de noter que cette solution ne fonctionne que lorsque les noms de fichiers et les chemins d'accès aux fichiers n'incluent pas d'espaces.
ZaSter
1

Je ne sais pas si vous le pouvez ou comment le faire avec xargs.

Mais quelque chose comme ça devrait fonctionner pour vous:

find . -iname "*.mov" -printf "%p %f\n" | while read -a HR ; do echo ffmpeg -i ${HR[0]} -f flv ${HR[1]} ;done
Zoredache
la source
1
Cela ne gère pas correctement les espaces dans les noms / chemins de fichiers, n'est-ce pas?
Daniel Beck
1

Une alternative à la réponse de Zoredache en utilisant la liaison de noms (et en utilisant différents séparateurs pour éviter le problème d'espaces dans les noms de fichiers):

IFS="\t" find -iname "*.mov" -printf "%p\t%f\n" | while read path file; do
    ffmpeg -i $path -f flv $file
done

Bien sûr, vous pouvez également utiliser les séparateurs avec l' find -a arrayargument que l'autre réponse avait utilisé, mais les arguments nommés sont parfois plus compréhensibles.

wolf1oo
la source
0

Vous pouvez exécuter ffmpeg directement à partir de la commande find comme ceci:

find . -iname "*.mov" -exec ffmpeg -i "%p" -f flv "%f" \;

Notez les guillemets autour des paramètres de ffmpeg, au cas où il y aurait des espaces dans le nom de fichier, et le point-virgule échappé marque la fin de la commande exécutée.

Randy Orrison
la source
Avez-vous une documentation sur le comportement de -exec qui interprète également les espaces réservés de formatage printf?
Daniel Beck
Nan. Brain fart: Je pensais que les% p et% f étaient des choses à trouver, pas des choses à imprimer. J'ai voté pour votre réponse.
Randy Orrison