Como 'find -exec' passa nomes de arquivos com espaços?

14

Se eu tiver um diretório que contenha alguns arquivos cujos nomes tenham espaços, por exemplo,

$ ls -1 dir1
file 1
file 2
file 3

Posso copiar todos eles com êxito para outro diretório como este:

$ find dir1 -mindepth 1 -exec cp -t dir2 {} +

No entanto, a saída de find dir1 -mindepth 1contém espaços sem escape:

$ find dir1 mindepth 1
dir1/file 1
dir1/file 3
dir1/file 3

Se eu usar em print0vez de print, a saída ainda conterá espaços sem escape:

$ find dir1 mindepth 1 -print0
dir1/file 1dir1/file 2dir1/file 3

Para copiar esses arquivos manualmente usando cp, eu precisaria escapar dos espaços; mas parece que isso é desnecessário quando cpsurgem os argumentos find, independentemente de eu usar +ou \;no final do comando.

Qual o motivo disso?

EmmaV
fonte

Respostas:

8

O findcomando executa o comando diretamente. O comando, incluindo o argumento filename, não será processado pelo shell ou qualquer outra coisa que possa modificar o nome do arquivo. É muito seguro.

Você está certo de que não há necessidade de escapar de nomes de arquivos representados por {}na findlinha de comando.

findpassa o nome do arquivo bruto do disco diretamente para a lista de argumentos internos do -execcomando, no seu caso, o cpcomando.

RobertL
fonte
1
Em poucas palavras, find..execpode lidar com nomes estranhos em seu próprio ..
heemayl
2
A primeira regra do clube linux é que você não analisa sl
Sergiy Kolodyazhnyy 15/11
5

A questão é dividida em duas partes:

  • como não find conseguem programas de chamadas usando -execsem funcionar em problemas com espaços incorporados em nomes de arquivos, e
  • que bom é a -print0opção?

Para o primeiro, findestá fazendo uma chamada de sistema, na verdade uma de um grupo de chamadas relacionadas, chamadas de "exec" . Ele passa o nome do arquivo como argumento diretamente para esta chamada, que é passada diretamente (após a criação de um novo processo) sem perder informações sobre o nome do arquivo.

O findrecurso POSIX +é explicado da seguinte forma, na lógica :

Um recurso do findutilitário do SVR4 era o -execterminador + do primário. Isso permitiu que nomes de arquivos contendo caracteres especiais (especialmente caracteres de nova linha ) fossem agrupados sem os problemas que ocorrem se esses nomes de arquivo forem canalizados xargs. Outras implementações adicionaram outras maneiras de contornar esse problema, principalmente o -print0primário que gravou nomes de arquivos com um terminador de bytes nulos. Isso foi considerado aqui, mas não adotado. O uso de um terminador nulo significava que qualquer utilitário que processasse a -print0saída da localização precisava adicionar uma nova opção para analisar os terminadores nulos que agora estaria lendo.

Que " notavelmente um -print0primário" se refere ao GNU finde xargsque resolve o problema de uma maneira diferente. Também é suportado pelo FreeBSD finde xargs. Se você adicionou uma -0opção (consulte a página do manual ) à xargschamada, esse programa aceita linhas terminadas com caracteres de "byte nulo". Por sua vez, xargschama exec -funtions para fazer seu trabalho. A distinção principal entre a -print0e -0característica versus a +característica é que o primeiro passa os nomes de arquivo ao longo de um tubo, enquanto que o último não o faz. Os desenvolvedores encontram usos para quase qualquer recurso; os tubos não são excepção.

De volta ao exemplo do OP, que usa uma -topção para cp: que não é encontrada no POSIX cp . Pelo contrário, é uma extensão (também conhecida como "característica não padronizada") fornecida pelo GNU cp . A -0extensão de xargsnão melhoraria este exemplo, mas há outros casos em que ele pode ser usado com eficiência - tendo em mente que existe a alternativa portátil +, que o GNU findaceita.

Thomas Dickey
fonte
-1

( Isso deve ser um comentário, mas é muito grande. )

Para quem gosta de experimentar as coisas:

Crie um script listando os parâmetros posicionais passados, chame-o list_positional_parameters.sh.

#!/bin/bash

# http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_09_07.html
# Try globbing patterns, e.g. "X[[:digit:]][[:digit:]]" to see what happens

if [ $# -lt 1 ]; then
   echo "Usage: $0 and then at least one parameter"
   exit 1
fi

counter=1

while (($#)); do
   echo "$counter = '$1'"
   # pop positional argument 1 off the stack of positional arguments
   shift
   (( counter++ ))
done

Execute findcom ele em algum diretório $ dir:

find "$dir" -exec ./list_positional_parameters.sh '{}' ';' | less

Como esperado, existe apenas um único parâmetro em todas as chamadas, o nome do arquivo, independentemente de haver espaços em seu nome ou não.

David Tonhofer
fonte
1
Você também pode usar printfcomo printf '"%s"\n' "$@"imprimir todos os argumentos posicionais citados, para inspeção visual.
Kusalananda