Existe algum motivo para verificar novamente os resultados de 'find -type -f' com o operador bash '-f'?

2

Em este blog , encontrei um bash script (seção "Snippets") que contém a seguinte estrutura para fornecer arquivos individuais de .bash_profile :

if [ -d ~/.bash_profile.d ]; then
  for file in $( find ~/.bash_profile.d -type f )
  do
    if [ -f "$file" ]; then
      source "$file"
    fi
  done
fi

Faz algum sentido primeiro filtrar os arquivos com find -type f e, em seguida, verificá-los novamente com o operador bash -f ou é algum tipo de colcha de retalhos?

O script a seguir não é o mesmo?

if [ -d ~/.bash_profile.d ]; then
  for file in $( find ~/.bash_profile.d -type f )
  do
    source "$file"
  done
fi

Se não, qual é a diferença?

    
por techraf 01.04.2016 / 17:30

6 respostas

1

Há algumas coisas erradas com este trecho, mas, surpreendentemente, a verificação de arquivo duplo não é uma delas.

Primeiro, a construção $() em torno de find está errada, como mostra @EricRenouf. A maneira correta de fazer isso seria usar algo como

while read filename
do
    source $filename
done < <(find ....)

que as chamadas localizam em um subshell e lê nomes de arquivos em uma variável filename no shell principal (é a idéia de find | while read x em sua cabeça, mas infelizmente não é tão portátil)

Em segundo lugar, o teste do diretório -d é redundante; find não encontrará nenhum arquivo se o diretório não existir, então testar sua existência não faz sentido.

No entanto, o argumento -type f para find não testa exatamente a mesma coisa que o argumento -f para test . O POSIX tem as seguintes para dizer sobre test :

-f pathname

True if pathname resolves to an existing directory entry for a regular file. False if pathname cannot be resolved, or if pathname resolves to an existing directory entry for a file that is not a regular file.

e tem para dizer sobre find :

-type c

The primary shall evaluate as true if the type of the file is c, where c is 'b', 'c', 'd', 'l', 'p', 'f', or 's' for block special file, character special file, directory, symbolic link, FIFO, regular file, or socket, respectively.

Portanto, se você tiver um arquivo regular que "não pode ser resolvido", terá um arquivo para o qual test -f falhará, mas find -type f será bem-sucedida. Uma maneira de fazer isso é ter um arquivo em um diretório que você possa ler, mas que você não pode acessar:

wouter@gangtai:~$ ls -ld foo
drw-r--r--. 2 wouter wouter 4096 apr  1 18:38 foo
wouter@gangtai:~$ find foo -type f | while read file; do if [ -f $file ]; then   echo $file tests -f; else   echo $file does not test -f; fi; done
foo/bar does not test -f
wouter@gangtai:~$ ls -l foo
ls: cannot access 'foo/bar': Permission denied
total 0
-????????? ? ? ? ?            ? bar

O tipo do arquivo (seja um diretório, um arquivo regular ou qualquer outro) pode ser detectado se você tiver r , mas não x , permissões no diretório em qual o arquivo é armazenado. No entanto, para poder "resolvê-lo" (ou seja, chamar uma das funções stat() nesse arquivo), você precisa do bit de permissão x nesse diretório.

Naturalmente, se fazer um teste em um shell script para seu .bashrc é útil é uma questão diferente. Eu diria que não. No entanto, isso não significa que os dois testes são os mesmos, e são casos em que essa diferença é importante ...

    
por 01.04.2016 / 18:48
1
for file in ~/.bash_profile.d/*

Isso não encontrará nenhum arquivo que comece com um ponto (.). Para pegar casos de borda, é melhor fazer a descoberta.

    
por 01.04.2016 / 17:34
1

~/.bash_profile.d/* também listará quaisquer subdiretórios, mas não os inserirá para obter arquivos dentro deles.% find with -type f excluirá os subdiretórios e os inserirá e buscará arquivos deles também. Além disso, find incluirá arquivos ocultos, o que ~/.bash_profile.d/* não irá.

Editar:

A verificação de [ -f "$file" ] é redundante.

    
por 01.04.2016 / 17:41
1

Normalmente, você pode evitar muitos problemas ao usar a opção find -exec ou até mesmo com xargs .

No entanto, fazer o sourcing de um arquivo seria inútil em um subshell, então você precisa usar o shell superior.

Testar se o arquivo encontrado ainda é um arquivo parece realmente inútil nesse caso específico. Se o arquivo for rapidamente substituído por outra coisa durante o processamento de localização, na pior das hipóteses você terá um erro.

Se você quiser simplificar ainda mais, você também pode remover o teste inicial como se não houvesse nenhum diretório chamado .bash_profile.d , não pode haver arquivos sob ele de qualquer maneira:

for file in $( find ~/.bash_profile.d -type f 2>/dev/null); do
    . $file
done
    
por 01.04.2016 / 17:59
1

Você pode querer verificar aqui porque está processando a saída de find , o que significa que a saída estará sujeita à divisão de palavras. Se find imprimir um nome com espaço, o $file não passará no teste -f , pois ele será apenas parte de um nome de arquivo.

Por exemplo:

$ touch "~/.bash_profile.d/with space"
$ for f in $( find ~/.bash_profile.d -type f 2>/dev/null );do printf -- "--%s--\n" "$f"; done
--~/.bash_profile.d/with--
--space--

então, nesse caso, if [ -f "$f" ] não retornará true, pois $f não aponta para um arquivo

Este é outro exemplo do famoso problema Não analisar o

    
por 01.04.2016 / 18:16
1

Não há uma boa razão para a verificação -f adicional (além do que Eric mencionou), mas há uma razão lógica para usar a -x check. Isso garantirá que somente scripts executáveis possam ser obtidos. Isso facilita a desativação de fontes sem removê-las.

Fazendo eco aos pensamentos de Eric, a expressão find ... -print ¦ while read file; do if test -x $file... é um pouco melhor, mas como mostra a referência do seu blog, isso se sufoca em arquivos com novas linhas incorporadas. Minha réplica é qualquer um estúpido o suficiente para incorporar novas linhas em nomes de arquivos UNIX não deveria estar mexendo com scripts.

    
por 01.04.2016 / 18:40