Tengo un comando de búsqueda que encuentra ciertos archivos y directorios. Ese comando de búsqueda luego ejecuta rsync con los archivos y directorios encontrados previamente como fuente. El problema es que esos archivos y directorios pueden tener todo tipo de caracteres, como comillas simples y dobles, sin mencionar los caracteres ilegales de Windows, etc...
¿Cómo puedo escapar dinámicamente una cadena para usarla en rsync u otros comandos?
Este comando funciona codificando comillas dobles para la cadena de origen de rsync, pero se romperá si la cadena tiene comillas dobles.
find "/mnt/downloads/cache/" -depth -mindepth 1 \( \
-type f \! -exec fuser -s '{}' \; -o \
-type d \! -empty \) \
\( -exec echo rsync -i -dIWRpEAXogt --numeric-ids --inplace --dry-run '"{}"' "${POOL}" \; \)
salida resultante:
rsync -i -dIWRpEAXogt --numeric-ids --inplace --dry-run "test/this " is an issue" /mnt/backing
Comando de trabajo después de la información en las respuestas aplicadas:
find "/mnt/downloads/cache/" -depth -mindepth 1 \( \
-type f \! -exec fuser -s {} \; -o \
-type d \! -empty \) \
\( -exec rsync -i -dIWRpEAXogt --remove-source-files-- "${POOL} \; \) \
-o \( -type d -empty -exec rm -d {} \; \)
Respuesta aceptada:
Tu problema de cotización proviene de que intentas resolver un problema que no tienes. La necesidad de citar argumentos solo entra en juego cuando se trata de un shell, y si find
está llamando a rsync
directamente, no hay caparazón involucrado. El uso de resultados visuales no es una buena manera de saber si funciona o no porque no puede ver dónde comienza y termina cada argumento.
Esto es lo que quiero decir:
# touch "foo'\"bar"
# ls
foo'"bar
# find . -type f -exec stat {} \;
File: ‘./foo'"bar’
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: fd00h/64768d Inode: 1659137 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1004/ phemmer) Gid: ( 1004/ phemmer)
Access: 2017-12-09 13:21:28.742597483 -0500
Modify: 2017-12-09 13:21:28.742597483 -0500
Change: 2017-12-09 13:21:28.742597483 -0500
Birth: -
Tenga en cuenta que no cité el {}
en el argumento de stat
.
Dicho esto, su comando va a tener un rendimiento muy bajo, porque está llamando a rsync
para cada archivo coincidente. Hay 2 formas de resolver esto.
Como otros han indicado, puede usar canalizar la lista de archivos a rsync
en entrada estándar:
# find . -type f -print0 | rsync --files-from=- -0 . dest/
# ls dest/
foo'"bar
Esto usará bytes nulos como delimitador de nombre de archivo, ya que los archivos no pueden contener bytes nulos en su nombre.
Relacionado:¿Cómo solucionar el error "El dispositivo USB desconocido necesita más energía de la que el puerto puede suministrar"?
Si está utilizando GNU find
, tiene otro método para invocar -exec
, y eso es -exec {} +
. En este estilo find
pasará más de un argumento a la vez. Sin embargo, todos los argumentos se agregan al final del comando, no en el medio. Puede abordar esto pasando los argumentos a través de un pequeño shell:
# find . -type f -exec sh -c 'rsync "[email protected]" dest/' {} +
# ls dest/
foo'"bar
Esto pasará la lista de archivos a sh
que luego los sustituirá por el "[email protected]"