Soy relativamente nuevo en Bash y estoy tratando de hacer algo que en la superficie parecía bastante sencillo: ejecutar find en una jerarquía de directorios para obtener todos los archivos * .wma, canalizar esa salida a un comando donde los convierto a mp3 y guarde el archivo convertido como .mp3. Pensé que el comando debería tener el siguiente aspecto (he dejado el comando de conversión de audio y en su lugar estoy usando echo como ilustración):
$ find ./ -name '*.wma' -type f -print0 | xargs -0 -I f echo ${f%.*}.mp3
Según tengo entendido, el argumento -print0 me permitirá manejar nombres de archivos que tienen espacios (que muchos de estos hacen como archivos de música). Entonces espero (como resultado de xargs) que cada ruta de archivo de find se capture en f, y que usando la coincidencia / eliminación de la subcadena desde el final de la cadena, debería hacer eco de la ruta de archivo original con un mp3 extensión en lugar de wma. Sin embargo, en lugar de este resultado, estoy viendo lo siguiente:
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
*.mp3
...
Entonces, mi pregunta (aparte del 'qué estoy haciendo mal aquí') es esta: los valores que son el resultado de una operación de tubería deben tratarse de manera diferente en las operaciones de manipulación de cadenas que los que son el resultado de una asignación variable ?

xargsconfind. Viene con una-execopción. ¿Puedes agregar el comando que vas a usar a tu pregunta y alguien puede mostrarte elfindcomando correcto ?{}miembro)xargses más adecuado queexec. Vea este stackpost stackoverflow.com/questions/896808/find-exec-cmd-vs-xargs para ver un caso en cuestión.Respuestas:
Como otras respuestas ya han identificado,
${f%.*}el shell lo expande antes de ejecutar elxargscomando. Necesita que esta expansión suceda una vez para cada nombre de archivo, con la variable de shellfestablecida en el nombre del archivo (pasar-I fno hace eso:xargsno tiene noción de variable de shell, busca la cadenafen el comando, por lo que si desea usado, por ejemploxargs -I e echo …, habría ejecutado comandos como./somedir/somefile.wmacho .mp3).Siguiendo con este enfoque, diga
xargsque invoque un shell que pueda realizar la expansión. Mejor, digafind:xargses una herramienta en gran parte obsoleta y es difícil de usar correctamente, ya que las versiones modernas de find tienen una construcción que hace el mismo trabajo (y más) con menos dificultades de plomería. En lugar defind … -print0 | xargs -0 command …correrfind … -exec command … {} +.El argumento
_está$0en la cáscara; los nombres de archivo se pasan como argumentos posicionales que sefor f; do …repiten. Una versión más simple de este comando ejecuta un shell separado para cada archivo, que es equivalente pero un poco más lento:En realidad, no necesita usar
findaquí, suponiendo que esté ejecutando un shell razonablemente reciente (ksh93, bash ≥4.0 o zsh). En bash, ponshopt -s globstartu.bashrcpara activar el**/patrón glob para que se repita en subdirectorios (en ksh, eso esset -o globstar). Entonces puedes correr(Si tiene directorios llamados
*.wma, agregue[ -f "$f" ] || continueal comienzo del ciclo).fuente
En el caso de la evaluación de su solución de $ {f%. *}. Mp3 se realiza en el shell en el que está invocando el comando completo, no en el shell bifurcado por los xargs . Y en su shell no hay una variable f , se sustituye por una cadena vacía.
Solución usando xargs con -I f :
Pero lo haría de esta manera:
fuente