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

10

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

$ ls -1 dir1
file 1
file 2
file 3

Eu posso copiar com sucesso todos eles para outro diretório como este:

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

No entanto, a saída de find dir1 -mindepth 1 contém espaços não ignorados:

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

Se eu usar print0 em vez de print , a saída ainda conterá espaços não escapados:

$ 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 os argumentos cp vêm de find , independentemente de eu usar + ou \; no final do comando.

Qual é o motivo disso?

    
por EmmaV 15.11.2015 / 04:58

3 respostas

7

O comando find 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 nomes de arquivos representados por {} na linha de comando find .

find passa o nome do arquivo bruto do disco diretamente para a lista de argumentos internos do comando -exec , no seu caso, o comando cp .

    
por 15.11.2015 / 05:02
4

A questão é em duas partes:

  • como o find consegue chamar programas usando -exec sem ter problemas com espaços incorporados em nomes de arquivos, e
  • qual é a opção -print0 ?

No primeiro, find está fazendo uma chamada de sistema, na verdade, um de um grupo de chamadas relacionadas, chamado de " exec ". Ele passa o nome do arquivo como um argumento diretamente para essa chamada, que é passada diretamente (depois de criar um novo processo) sem perder informações sobre o nome do arquivo.

O recurso POSIX find + é explicado da seguinte forma, no < href="http://pubs.opengroup.org/onlinepubs/9699919799/utilities/find.html#tag_20_47_18"> lógica :

A feature of SVR4's find utility was the -exec primary's + terminator. This allowed filenames containing special characters (especially newline characters) to be grouped together without the problems that occur if such filenames are piped to xargs. Other implementations have added other ways to get around this problem, notably a -print0 primary that wrote filenames with a null byte terminator. This was considered here, but not adopted. Using a null terminator meant that any utility that was going to process find's -print0 output had to add a new option to parse the null terminators it would now be reading.

Que " notavelmente a -print0 primary" refere-se ao GNU find e xargs que resolvem o problema de uma maneira diferente. Também é suportado pelo FreeBSD find e xargs . Se você adicionou uma opção -0 (consulte a página de manual ) ao xargs call, então esse programa aceita linhas terminadas por caracteres "byte nulo". Por sua vez, xargs chama as funções exec para fazer o trabalho its . A principal distinção entre o recurso -print0 e -0 versus o recurso + é que o primeiro passa os nomes de arquivo por um pipe, enquanto o segundo não passa. Os desenvolvedores encontram usos para quase todos os recursos; os canos não são exceção.

Voltar ao exemplo de OP, que usa a opção -t para cp : que não é encontrada em POSIX cp . Pelo contrário, é uma extensão (também conhecida como "recurso não padrão") fornecida pelo GNU cp . A extensão -0 de xargs não melhoraria este exemplo, mas há outros casos em que ela pode ser usada com eficácia, lembrando que existe a alternativa portátil + , que o GNU find aceita.

    
por 15.11.2015 / 11:22
0

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

Para quem gosta de experimentar:

Crie um script listando parâmetros posicionais passados, chame de 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 find com 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, se há espaços em seu nome ou não.

    
por 26.07.2018 / 11:17