Escape caracteres desconhecidos da string para -exec

3

Eu tenho um comando find que encontra certos arquivos e diretórios. Esse comando find executa o rsync com os arquivos e diretórios encontrados anteriormente como origem. O problema é que esses arquivos e diretórios pode ter todos uma espécie de caracteres como aspas simples e duplas para não mencionar caracteres ilegais do Windows etc ...

Como posso dinamicamente escapar uma string para uso em rsync ou outros comandos?

Este comando funciona por codificação duro aspas duplas para cadeia de origem rsync, mas ele vai quebrar se a cadeia tem aspas duplas na mesma.

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}" \; \)

resultado resultante:

rsync -i -dIWRpEAXogt --numeric-ids --inplace --dry-run "test/this " is an issue" /mnt/backing

Comando de trabalho após as informações nas respostas 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 {} \; \)
    
por DominicM 09.12.2017 / 16:54

3 respostas

3

Seu problema de citação está vindo de você tentando resolver um problema que você não tem. A necessidade de citar argumentos só entra em jogo quando você está lidando com um shell, e se find estiver chamando rsync diretamente, não há shell envolvido. Usar a saída visual não é uma boa maneira de saber se funciona ou não porque você não consegue ver onde cada argumento começa e termina.

Veja o que quero dizer:

# 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: -

Observe que não citei o {} no argumento para stat .

Agora, o seu comando será muito pouco performático, porque você está chamando rsync para cada arquivo correspondente. Existem duas maneiras de resolver isso.

Como outros indicaram, você pode usar o pipe da lista de arquivos para rsync em stdin:

# find . -type f -print0 | rsync --files-from=- -0 . dest/

# ls dest/
foo'"bar

Isso usará bytes nulos como o delimitador do nome do arquivo, pois os arquivos não podem conter bytes nulos em seu nome.

Se você estiver usando o GNU find , você tem outro método de invocar -exec , e isso é -exec {} + . Neste estilo find passará mais de um argumento por vez. No entanto, todos os argumentos são adicionados ao final do comando, não no meio. Você pode resolver isso passando os argumentos por meio de um pequeno shell:

# find . -type f -exec sh -c 'rsync "$@" dest/' {} +

# ls dest/
foo'"bar

Isso irá passar a lista de arquivos para o sh , que irá substituí-los pelo "$@"

    
por 09.12.2017 / 19:42
1

Use find de -print0 em combinação com rsync --files-from=- -0 :

find "${Share}" -depth -mindepth 1 \( \
-type f \! -exec fuser -s '{}' \; -o \
-type d \! -empty \) -print0 | rsync --files-from=- -0 "${Share}" ${POOL}

Agora, find gerará uma lista de arquivos encontrados separados por -print0 (devido a rsync ). O NUL não é um caractere válido para um nome de arquivo, portanto, é seguro usá-lo como um delimitador em uma lista. Essa lista é impressa na saída padrão e lida por rsync . -print0 geralmente obtém os arquivos de origem como argumentos, mas pode ler um arquivo:

find $PATH [your conditions here] -print > my-file-list.txt
rsync --files-from=my-file-list.txt $PATH $DESTINATION

É claro que, como usamos -print e não rsync , precisamos informar - que os arquivos são delimitados por --files-from :

find $PATH [your conditions here] -print0 > my-file-list.txt
rsync --files-from=my-file-list.txt -0 $PATH $DESTINATION

Podemos usar rsync como argumento para rsync para informar find que os arquivos devem ser lidos a partir de sua entrada padrão e se livrar do arquivo intermediário:

find $PATH [your conditions here] -print0 \
  | rsync --files-from=- -0 $PATH $DESTINATION

Observe que %code% ainda precisa de um caminho como origem, mas só transferirá os arquivos encontrados por %code% .

    
por 09.12.2017 / 18:04
0

Obter a citação certa neste contexto é diabolicamente difícil. Eu costumo desistir e deixar outras ferramentas experimentarem. Desculpe, isso não foi testado, eu não tenho a sua opinião para tentar, então este é um palpite que poderia ser descontroladamente quebrado :) Use print0 em find para terminar em nulo (em vez de terminar em branco) as saídas e, em seguida, -0 no xargs para dizer que espera esse tipo de entrada ... isso é para evitar quebrar em espaços em branco incorporados.

find "${Share}" -depth -mindepth 1 \( \
-type f \! -exec fuser -s '{}' \; -o \
-type d \! -empty \) -print0 | \
xargs -0 -I{} rsync -i -dIWRpEAXogt --numeric-ids --inplace --dry-run {} ${POOL}
    
por 09.12.2017 / 18:02