Usar xargs con $ (): ¿precedencia del operador?

3

Inspirado por esta pregunta . La pregunta original, en definitiva, es "Cómo convertir todo :2fen -en todos los archivos dentro de una carpeta". por ejemplo, si tengo un archivo ./abc:2fdef, debería cambiarle el nombre ./abc-def.

Al principio pensé que era una tarea sencilla ... findtodos los archivos y luego sedpara reemplazar :2fa -. Cuando intento construir una línea, se me ocurre el comando:

find . -type f -name '*:2f*' | xargs -I {} mv {} $(echo {} | sed "s/:2f/-/ig")

Sin embargo no funciona. Después de hacer muchas pruebas, descubrí que el problema radica en la xargsparte: $ () se ejecutó antes de xargsreemplazar el lugar {}con los nombres de los archivos. Elaborar,

xargs -I {} mv {} $(echo {} | sed "s/:2f/-/ig")

fue evaluado como ( $(...)fue evaluado)

xargs -I {} mv {} {}

y entonces

mv ./abc:2fdef ./abc:2fdef

Lo cual no era lo que esperaba. Entonces, mi pregunta es, ¿puedo hacer que xargs sustituya todo {}en nombres de archivo antes de evaluar la $(...)parte?

Kenneth L
fuente

Respuestas:

5

No, porque xargsno puede sustituir algo antes de que el shell llame xargscon $(…)ya evaluado. Esto se debe al orden de expansión de la shell .

Probablemente lo haría así:

find . -type f -name '*:2f*' -exec bash -c 'mv -- "$0" "${0//:2f/-}"' {} \;

Aquí, la llamada de shell se evalúa a partir de cada archivo encontrado por find, y el reemplazo de cadena se realiza con características de shell en lugar de otra llamada a sed.

Además, este enfoque es más seguro porque los espacios en los nombres de archivo se manejan adecuadamente (entre comillas dobles para los argumentos de la mvllamada). Más sobre eso aquí .


Para estas tareas, probablemente desee una herramienta más eficiente como zmv, que está disponible en Zsh, o Perl rename, que se incluye con muchas distribuciones de Linux:

rename 's/:2f/-/' *
slhck
fuente
+1 para "orden de expansión de shell" y "reemplazo de cadena como características de shell". ¡Gracias!
Kenneth L