Usando conectores após o comando find

10

Quero que o meu bash imprima 'encontrado' apenas se algo for encontrado, usando o comando find. Mas usar && não ajuda: mesmo que não tenha encontrado nada, estou sendo 'encontrado' impresso. Exemplo:

$ pwd
/data/data/com.termux/files/home/test/test1/test4
$ ls
xaa  xab
$ find . -name xac && echo 'found'
found
$ find . -name xaa && echo 'found'
./xaa
found
Josef Klimuk
fonte

Respostas:

18

Você pode findimprimir found:

find . -name xac -printf "found\n" -quit

A opção -quitserá find encerrada após a primeira partida , portanto foundsó será impressa uma vez.

Em um segmento semelhante no Unix e Linux ( fazer a descoberta falhar quando nada foi encontrado ), eu costumava grep -qzretornar um status de saída diferente de zero se findnão encontrasse nada:

find /some/path -print0 -quit | grep -qz .

Que você pode usar para construir comandos compostos usando &&ou if:

find /some/path -print0 -quit | grep -qz . && echo found
muru
fonte
Eu tive que encarar isso por algum tempo. /some/pathdiz para descobrir onde começar a procurar, mas nada diz o que procurar. O mesmo na sua resposta vinculada. O que funciona para mim é find /some/path -name xac -print0 -quit | grep -qz . && echo found. Perdi algo?
Joe
@ Joe, o que importa aqui é o -print0 -quit. O que você coloca antes disso depende do que deseja encontrar. Eu escolhi omitir isso aqui.
Muru
13

A resposta de muru é apropriada e adequada para os casos em que queremos imprimir algo se o arquivo for encontrado. Para casos gerais, quando queremos executar um comando externo, como echo, poderíamos usar -execflag.

$ find . -name 'xac' -exec echo "I found " {} \; -quit             
I found  ./xac

A {}parte passa o nome do arquivo para o comando entre -exece \;como argumentos. Observe o \antes ;- ele impede que o shell o interprete errado ; no ponto e vírgula de fechamento do shell significa fim do comando, mas quando escapado com barra, o shell o tratará como texto literário a ser passado ao findcomando e, para encontrar o comando, ele serve como -execargumento de fechamento da flag.


Para construir condicionais do if found do this; else do thattipo, poderíamos fazer uso da subestação de comando $()e testcomando (aka [):

$ [ "x$(find . -name 'noexist' -print -quit)" != "x" ] && echo "found" || echo "not found"                                                                                              
not found

$ [ "x$(find . -name 'xac' -print -quit)" != "x" ] && echo "found" || echo "not found"                                                                                                  
found

Dirigindo-se ao comentário de Dan

Dan nos comentários perguntou:

O eco "Encontrei {}" não seria melhor do que o eco "Encontrei" {}? Talvez para eco seja bom, mas se alguém copia o comando e substitui o eco por outro comando, pode haver um problema

Vamos entender o problema primeiro. Normalmente, em shells existe o conceito de divisão de palavras, o que significa que variáveis ​​não citadas e parâmetros posicionais serão expandidos e tratados como itens separados. Por exemplo, se você tiver variável vare contém hello worldtexto, quando você faz touch $varo shell vai dividi-la em dois itens separados helloe worlde touchvai entender que, como se estivesse tentando criar 2 arquivos separados; se o fizer touch "$var", o shell tratará hello worldcomo uma unidade e touchcriará apenas um arquivo. É importante entender que isso acontece apenas devido à forma como as conchas funcionam.

Por outro lado, findnão sofre com esse comportamento, porque os comandos são processados ​​por findsi só e executados por execvp()chamada do sistema, portanto, não há shell envolvido. Enquanto os chavetas têm um significado especial nas conchas, porque elas aparecem no meio do findcomando e não no começo, elas não possuem significado especial para descascar nesse caso. Aqui está um exemplo. Vamos criar alguns nomes de arquivos difíceis e tentar transmiti-los como argumento para statcomando.

$ touch with$'\t'tab.txt with$' 'space.txt with$'\n'newline.txt

$ find -type f -exec stat -c "%F" {} \; -print                                                                                                                         
regular empty file
./with?newline.txt
regular empty file
./with space.txt
regular empty file
./with?tab.txt

Como você pode ver, statrecebe nomes de arquivos difíceis perfeitamente find, o que é um dos principais motivos pelos quais é recomendado para uso em scripts portáteis e especialmente útil quando você está percorrendo a árvore de diretórios e deseja fazer algo com nomes de arquivos que possam ter potencialmente caracteres especiais neles. Portanto, não é necessário citar chaves para comandos executados no find.

É uma história diferente quando a shell se envolve. Às vezes, você precisa usar um shell para processar o nome do arquivo. Nesse caso, a citação será realmente importante, mas é importante perceber que o problema não é do achado - é o shell que faz a divisão de palavras.

$ find -type f -exec bash -c "stat {}" sh \;   
stat: cannot stat './with': No such file or directory
sh: line 1: newline.txt: command not found
stat: cannot stat './with': No such file or directory
stat: cannot stat 'space.txt': No such file or directory
stat: cannot stat './with': No such file or directory
stat: cannot stat 'tab.txt': No such file or directory

Então, quando citamos dentro do shell , ele funcionará. Mas, novamente, isso é importante para o shell, não find.

$ find -type f -exec bash -c "stat -c '%F' '{}'" sh \;                                                                                                                 
regular empty file
regular empty file
regular empty file
Sergiy Kolodyazhnyy
fonte
Não echo "I found {}"seria melhor do que echo "I found " {}? Talvez para eco seja bom, mas se alguém copia o comando e substitui o eco por outro comando, pode haver um problema.
Dan
@ Dan o tópico foi muito longo para discutir nos comentários, então eu fiz uma edição na minha resposta. Por favor, veja-o
Sergiy Kolodyazhnyy 2/11
1
Obrigado por finalmente me fazer entender por que esse ponto e vírgula precisava estar lá. Além disso, ótima explicação para citar.
Joe
1
Não esperava tantos detalhes como resposta ao meu comentário. Eu aprecio muito essa explicação, obrigado!
Dan