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 ?
xargs
confind
. Viene con una-exec
opción. ¿Puedes agregar el comando que vas a usar a tu pregunta y alguien puede mostrarte elfind
comando correcto ?{}
miembro)xargs
es 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 elxargs
comando. Necesita que esta expansión suceda una vez para cada nombre de archivo, con la variable de shellf
establecida en el nombre del archivo (pasar-I f
no hace eso:xargs
no tiene noción de variable de shell, busca la cadenaf
en 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
xargs
que invoque un shell que pueda realizar la expansión. Mejor, digafind
:xargs
es 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á$0
en 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
find
aquí, suponiendo que esté ejecutando un shell razonablemente reciente (ksh93, bash ≥4.0 o zsh). En bash, ponshopt -s globstar
tu.bashrc
para 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" ] || continue
al 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