Bash Script: Impossible d’exécuter la commande mencoder!

voici un script .. je veux juste pratiquer quelques compétences basiques et faire un rapide util pour mon lecteur mp4 chinois =)

#!/bin/bash ##################################### # RockChip 4gb Player mencoder preset ##################################### TOOL='mencoder' OUTS='./out/' OPT1='-noodml' OPT2="-of avi -ofps 22 -vf-add scale=320:-2,expand=320:240 -srate 44100 -ovc xvid -xvidencopts bitrate=400:max_bframes=0:quant_type=s16le -oac lavc -lavcopts acodec=mp2:abitrate=128" bold=`tput bold` normal=`tput sgr0` # check does argument exists if test -z "$1"; then echo "There's no file given =)" fi # Check is it dir or file if [ -d $1 ]; then echo "Directory is given: $1" # Test if output argument is given if [ -z $2 ]; then echo "No output argument given using default: ${bold}${red}$OUTS${normal}" mkdir out else # test is given path a directory if [ -d $2 ]; then OUT="$2" else echo "Output argument is not a directory" fi fi OLD_IFS=IFS; IFS=$'\n' for file in `find . -name "*.*" -type f | sed 's!.*/!!'` ; do file=`printf "%q" "$file"` echo ${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2} done IFS=OLD_IFS fi 

Le problème est cette ligne:

 echo ${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2} 

Lorsque vous supprimez echo, pour exécuter la commande, la commande échoue, mais si vous copiez ce script renvoyé et l’exécutez manuellement, tout fonctionne.

Lors de l’exécution d’une commande à partir du script du shell, la sortie est la suivante:

 MEncoder 1.0rc4-4.2.1 (C) 2000-2010 MPlayer Team 158 audio & 340 video codecs -of avi -ofps 22 -vf-add scale=320:-2,expand=320:240 -srate 44100 -ovc xvid -xvidencopts bitrate=400:max_bframes=0:quant_type=s16le -oac lavc -lavcopts acodec=mp2:abitrate=128 is not an MEncoder option Exiting... (error parsing command line) 

comme je l’ai mentionné avant d’exécuter la commande manuellement, tout fonctionne par exemple:

 mencoder -noodml 12\ I\ Love\ You\ 1\ \ I\ Love\ You\ 2\ \ I\ Love\ You\ 3.avi -o ./out/12\ I\ Love\ You\ 1\ \ I\ Love\ You\ 2\ \ I\ Love\ You\ 3.avi -of avi -ofps 22 -vf-add scale=320:-2,expand=320:240 -srate 44100 -ovc xvid -xvidencopts bitrate=400:max_bframes=0:quant_type=s16le -oac lavc -lavcopts acodec=mp2:abitrate=128 

maintenant tout ce que je peux faire est de copier coller les commandes générées .. où est le problème? J’ai essayé de google vraiment dur .. sans résultat … (je sais que mencoder ont des profils .. ce n’est pas le cas où je les veux)

Vous avez cette déclaration avant votre boucle for :

 IFS=$'\n' 

Cela définit votre séparateur de champ interne sur newlines, au lieu de la valeur par défaut de n’importe quel espace. Cela modifie la façon dont les parameters substitués sont analysés. Dans la ligne de construction de la commande:

 ${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2} 

ce qui va arriver est que chacune de ces expressions ${variable} sera développée, puis le shell essaiera de les séparer sur \n , pas sur des espaces comme vous vous en doutez normalement. Cela vous donnera un résultat similaire à ce qui suit (à moins que l’une de ces variables ne contienne une nouvelle ligne):

 "${TOOL}" "${OPT1}" "${file}" -o "${OUTS}${file}" "${OPT2}" 

Vous pouvez voir ici que vous transmettez l’intégralité de votre chaîne ${OPT2} en un seul paramètre, plutôt que de permettre à Bash de le diviser en espaces et de transmettre chaque indicateur individuellement. mencoder alors dérouté par ce paramètre énorme qu’il ne sait pas gérer. Bien sûr, les espaces étant tous présents, ils seront imprimés par la commande echo et fonctionneront correctement dans un shell dans lequel $IFS n’a pas été réinitialisé.

Vous pouvez démontrer cet effet assez facilement en définissant une fonction simple qui imprimera chacun de ses arguments sur une ligne distincte:

 $ print_args() { for arg; do echo $arg; done } $ foo="1 2" $ print_args ${foo} ${foo} 1 2 1 2 $ IFS=$'\n' $ print_args ${foo} ${foo} 1 2 1 2 

Je recommande de ne pas utiliser le truc $IFS pour votre boucle for . Au lieu de cela, vous pouvez utiliser while read file pour parcourir chaque ligne de l’entrée. Je recommande également de ne pas utiliser printf "%q" pour les espaces d’échappement, mais plutôt de citer l’argument de mencoder , qui transmettra le tout en un seul argument. Notez que je cite les ${file} et ${OUTS}${file} pour m’assurer qu’ils sont passés en tant qu’argument unique, mais sans citer ${OPT1} et ${OPT2} pour leur permettre être analysé comme des arguments distincts par le shell.

 find . -name "*.*" -type f | sed 's!.*/!!' | while read -r file do "${TOOL}" ${OPT1} "${file}" -o "${OUTS}${file}" ${OPT2} done 

Au fait, je vous recommande d’utiliser $() pour la substitution de commandes plutôt que `` ; Il y a de nombreuses raisons pour lesquelles il est préférable, comme la lisibilité, des citations et des règles d’échappement plus saines, et la possibilité d’imbriquer plusieurs niveaux de substitution de commandes. Et les problèmes que soulignent Jonathan et Wes sont bons à noter, même s’ils ne sont pas à l’origine de votre problème immédiat.

Vous avez (ligne 37 je crois):

 OUT="$2" 

mais je pense que vous vouliez dire:

 OUTS="$2" 

Je ne suis pas tout à fait sûr, mais peut-être vaut-il mieux citer le nom de fichier avec des guillemets doubles ( " ) au lieu de faire printf "%q" "$file" .

Alors remplacez:

 file=`printf "%q" "$file"` ${TOOL} ${OPT1} ${file} -o ${OUTS}${file} ${OPT2} 

avec

 ${TOOL} ${OPT1} "${file}" -o "${OUTS}${file}" ${OPT2} 

D’abord, utilisez $() au lieu des tics arrière.

 bold=$(tput bold) normal=$(tput sgr0) 

OLD_IFS=IFS; IFS=$'\n' OLD_IFS=IFS; IFS=$'\n' devrait être OLD_IFS=$IFS . vous voulez obtenir la valeur de l’IFS, alors mettez un signe dollar.

Vous n’avez pas besoin d’appeler sed pour obtenir le nom de base des fichiers

 while read -r file do filename="${file##*/}" filename=$(printf "%q" $filename) echo mencoder "${OPT1}" "${file}" -o "${OUTS}${file}" "${OPT2}" done < <(find . -name "*.*" -type f) 

enfin,

IFS=OLD_IFS devrait être IFS=$OLD_IFS

En utilisant ... | xargs bash -c '...' ... | xargs bash -c '...' il suffit d’échapper aux guillemets simples intégrés dans le nom du fichier.

 TOOL='mencoder' OUTS='./out/' OPT1='-noodml' OPT2='-of avi -ofps 22 -vf-add scale=320:-2,expand=320:240 -srate 44100 -ovc xvid -xvidencopts bitrate=400:max_bframes=0:quant_type=s16le -oac lavc -lavcopts acodec=mp2:abitrate=128' # test cases file='12 I Love You 1 I Love You 2 I Love You 3.avi' file='12 I Lo"ve You 1 I Love You 2 I Love You 3.avi' # one embedded double quote file='12 I Lo"ve You 1 I Lo"ve You 2 I Love You 3.avi' # two embedded double quotes file="12 I Lo've You 1 I Love You 2 I Love You 3.avi" # one embedded single quote file="12 I Lo've You 1 I Lo've You 2 I Love You 3.avi" # two embedded single quotes # escape embedded single quotes in $file escsquote="'\''" file="${file//\'/${escsquote}}" # we're passing ${TOOL} as arg0 to bash -c (which then is available as "$0") # put the variables in the printf line into single quotes to preserve spaces # remove no-op ':' to execute the command printf '%s\n' "${file}" printf '%s' "${OPT1} '${file}' -o '${OUTS}${file}' ${OPT2}" | xargs bash -c 'set -xv; printf "%s\n" "$0" "$@" | nl' "${TOOL}" printf '%s' "${OPT1} '${file}' -o '${OUTS}${file}' ${OPT2}" | xargs bash -c 'set -xv; : "$0" "$@"' "${TOOL}"