Quando uso find
para ver todos os arquivos pdf no /home
diretório, estou vendo access denied
. Para eliminá-los, tentei:
find /home -iname "*.pdf" | grep -v "access denied"
No entanto, o resultado é o mesmo. Como posso me livrar dessas linhas?
Quando uso find
para ver todos os arquivos pdf no /home
diretório, estou vendo access denied
. Para eliminá-los, tentei:
find /home -iname "*.pdf" | grep -v "access denied"
No entanto, o resultado é o mesmo. Como posso me livrar dessas linhas?
Respostas:
O que você tentou não funcionou porque a access denied
saída é um erro e é enviada no STDERR em vez do STDOUT ao qual é canalizado grep
.
Você pode evitar ver esses erros redirecionando apenas STDERR
find /home -iname "*.pdf" 2>/dev/null
Ou, como comentou David Foerster , podemos fechar de forma mais sucinta o STDERR
find /home -iname "*.pdf" 2>&-
No entanto, eu suspeito que você realmente deseja pesquisar sua casa em vez de outros usuários, então talvez você realmente queira
find ~ -iname "*.pdf"
Se isso gerar erros, pode haver algumas propriedades erradas na sua configuração local, que você deve investigar.
sudo chown $USER: ~/.gvfs ~/.dbus
2>&-
. A descoberta do GNU não será finalizada se tentar gravar mensagens de erro em um descritor de arquivo disfuncional. Pois os problemas de propriedade sudo chown -R $USER: ...
seriam mais eficazes no caso de mais arquivos que não pertencem a eles $USER
.
O acesso negado provavelmente está sendo impresso em stderr
vez de stdout
.
Tente o seguinte:
find /home -iname "*.pdf" 2>&1 | grep -v "access denied"
O 2>&1
redireciona a saída de stderr
para stdout
, para que ele grep -v
possa fazer seu trabalho. (Por padrão, |
apenas tubos stdout
e não stderr
.)
2>&1
... Eu não sou um especialista em bash, por isso, se isso é incorrecta, por favor, diga :)
bash
no Ubuntu, exceto no modo POSIX . Eu acho que é a melhor solução - um arquivo com o nome malicioso access denied
ainda aparecerá.
Você provavelmente quer dizer "Permissão negada" - que é o que o find
Ubuntu mostra quando você não pode acessar algo por causa de permissões de arquivo - em vez de "acesso negado".
Um comando totalmente geral que faz isso corretamente (e, como bônus, é portátil para outros * nix es, desde que a mensagem de erro seja a mesma):
(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
(Geralmente, você deseja passar alguns argumentos find
. Esses são anteriores ao primeiro redirecionamento 3>&1
.)
No entanto, muitas vezes você poderá usar algo mais simples. Por exemplo, você provavelmente pode usar substituição de processo . Detalhes a seguir.
As duas abordagens típicas são jogar fora o stderr (como na resposta de Zanna ) ou redirecionar o stderr para o stdout e filtrar o stdout (como na resposta do desenvolvedor do Android ). Embora tenham a vantagem de serem simples de escrever e geralmente sejam escolhas razoáveis, essas abordagens não são ideais.
Descartar tudo o que é enviado ao stderr - como redirecioná-lo para o dispositivo nulo com 2>/dev/null
ou ao fechar 2>&-
- corre o risco de perder erros que não sejam "Permissão negada".
"Permissão negada" é provavelmente o erro mais comum visto durante a execução find
, mas está longe de ser o único erro possível e, se ocorrer outro, talvez você queira saber sobre ele. Em particular, find
informa "Não existe esse arquivo ou diretório" se um ponto de partida não existir. Com vários pontos de partida, find
ainda pode retornar alguns resultados úteis e parecer funcionar. Por exemplo, se a
e c
existe, mas b
não existe, find a b c -name x
imprime os resultados e a
, em seguida, "Não existe esse arquivo ou diretório" para b
, então resulta em c
.
Combinar stdout e stderr juntos em stdout e canalizá-lo para grep
ou algum outro comando para filtrá-lo - como com 2>&1 | grep ...
ou |& grep ...
- corre o risco de filtrar acidentalmente um arquivo cujo nome contém a mensagem que está sendo filtrada.
Por exemplo, se você filtrar linhas que contenham "Permissão negada", você também eliminará os resultados da pesquisa mostrando nomes de arquivos como "Permissão negada messages.txt". Isso provavelmente aconteceria por acidente, embora também fosse possível que um arquivo recebesse um nome especialmente criado para impedir suas pesquisas.
A filtragem dos fluxos combinados tem outro problema, que não pode ser mitigado com a filtragem mais seletiva (como grep -vx 'find: .*: Permission denied'
no lado direito do tubo). Algumas find
ações, incluindo a -print
ação implícita quando você não especifica nenhuma ação, determinam como gerar nomes de arquivos com base no fato de o stdout ser ou não um terminal.
?
vez disso, são impressos.grep
) faz com que find
não seja mais visto um terminal. (Mais precisamente, faz com que o stdout não seja um terminal.) Em seguida, caracteres estranhos são exibidos literalmente. Mas se todo o comando no lado direito do canal é: (a) remover linhas que se parecem com mensagens "Permissão negada" e (b) imprimir o que resta, você ainda está sujeito ao tipo de travessuras que find
são terminais A detecção visa impedir.man find
para obter mais informações, incluindo o comportamento de cada uma das ações que imprimem nomes de arquivos. ( "Muitas das ações de busca resultam na impressão de dados que estão sob o controle de outros usuários ..." ) Veja também as seções 3.3.2.1 , 3.3.2.2 e 3.3.2.3 do manual de referência do GNU Findutils .A discussão acima sobre nomes de arquivos incomuns pertence à localização do GNU , que é a find
implementação em sistemas GNU / Linux, incluindo o Ubuntu.
O que você realmente quer aqui é deixar stdout intacta, enquanto tubulação stderr para grep
. Infelizmente, não existe uma sintaxe simples para isso. |
pipes stdout e algumas conchas (incluindo bash
) suportam o |&
pipe de ambos os fluxos - ou você pode redirecionar stderr para stdout primeiro com 2>&1 |
, o que tem o mesmo efeito. Mas os shells comumente usados não fornecem uma sintaxe apenas para o pipe stderr.
Você ainda pode fazer isso. É apenas estranho. Uma maneira é trocar o stdout pelo stderr , para que os resultados da pesquisa estejam no stderr e os erros no stdout e, em seguida, canalize o stdout grep
para filtragem:
find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
Geralmente, você passa argumentos para find
, como pontos de partida (os locais para pesquisar, que geralmente são diretórios) e predicados (testes e ações). Estes vão no lugar do args
acima.
Isso funciona introduzindo um novo descritor de arquivo para manter um dos dois fluxos padrão que você deseja trocar, executando redirecionamentos para trocá-los e fechando o novo descritor de arquivo.
3>&1
redireciona o descritor de arquivo 3 para o stdout, para que quando o stdout (descritor de arquivo 1) for subsequentemente redirecionado, o stdout original ainda possa ser gravado com facilidade.1>&2
redireciona stdout para stderr. Como o descritor de arquivo 3 ainda é o stdout original, ele ainda pode ser acessado.2>&3
redireciona o stderr para o descritor de arquivo 3, que é o stdout original.3>&-
fecha o descritor de arquivo 3, que não é mais necessário.No entanto, esse método tem a desvantagem de que os resultados da pesquisa são enviados ao stderr e os erros são enviados ao stdout . Se você estiver executando este comando diretamente em um shell interativo e não canalizar ou redirecionar mais a saída, isso realmente não importa. Caso contrário, pode ser um problema. Se você colocar esse comando em um script e alguém (talvez você mais tarde) redirecionar ou canalizar sua saída, ele não se comportará conforme o esperado .
A solução é trocar os fluxos de volta depois que você terminar de filtrar a saída . A aplicação dos mesmos redirecionamentos mostrados acima no lado direito do pipeline não conseguirá isso, porque |
apenas canaliza stdout, para que o lado do pipeline receba apenas a saída que foi originalmente enviada ao stderr (porque os fluxos foram trocados) e não o original saída stdout. Em vez disso, você pode usar (
)
para executar o comando acima em um subshell ( relacionado ) e aplicar os redirecionamentos de troca a esse:
(find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
É o agrupamento, não especificamente o subshell, que faz esse trabalho. Se preferir, você pode usar {
;}
:
{ find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'; } 3>&1 1>&2 2>&3 3>&-
Alguns shells, incluindo o Bash em sistemas que podem suportá-lo (incluindo sistemas GNU / Linux como o Ubuntu), permitem executar a substituição do processo , o que permite executar um comando e redirecionar para / de um de seus fluxos. Você pode redirecionar o find
stderr do comando para um grep
comando que o filtra e redirecionar grep
o stdout desse comando para o stderr.
find args 2> >(grep -Fv 'Permission denied' >&2)
O crédito é destinado ao desenvolvedor do Android para essa ideia.
1>&2
. Eu usei >&2
, o que é equivalente ( relacionado ). Use o que quiser.Embora bash
suporte a substituição de processos, sh
no Ubuntu é dash
, o que não. Ele fornecerá "Erro de sintaxe: redirecionamento inesperado" se você tentar usar esse método, enquanto o método de troca de stdout e stderr ainda funcionará. Além disso, quando bash
executado no modo POSIX , o suporte para substituição de processos é desativado.
Uma situação em que bash
é executado no modo POSIX é quando é chamado como sh
1 . Portanto, em um sistema operacional como o Fedora onde bash
fornece /bin/sh
, ou se você /bin/sh
apontou o link simbólico para bash
você no Ubuntu, a substituição do processo ainda não funciona em um sh
script, sem um comando anterior para desativar o modo POSIX. Sua melhor aposta, se você deseja usar esse método em um script, é colocar #!/bin/bash
no topo em vez de #!/bin/sh
, se você ainda não estiver.
1 : Nessa situação, bash
ativa o modo POSIX automaticamente após executar os comandos em seus scripts de inicialização.
É útil poder testar esses comandos. Para fazer isso, crio um tmp
subdiretório do diretório atual e o preencho com alguns arquivos e diretórios, retirando as permissões de um deles para acionar um erro "Permissão negada" find
.
mkdir tmp; cd tmp; mkdir a b c; touch w a/x 'a/Permission denied messages.txt' b/y c/z; chmod 0 b
Um dos diretórios que é acessível inclui um arquivo com "Permissão negada" em seu nome. A execução find
sem redirecionamentos ou canais mostra esse arquivo, mas também mostra o erro "Permissão negada" real para outro diretório que não está acessível:
ek@Io:~/tmp$ find
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘./b’: Permission denied
O pipeline de stdout e stderr grep
e filtragem de linhas que contêm "Permissão negada" faz com que a mensagem de erro desapareça, mas também oculta o resultado da pesquisa do arquivo com a frase em seu nome:
ek@Io:~/tmp$ find |& grep -Fv 'Permission denied'
.
./a
./a/x
./c
./c/z
./w
./b
find 2>&1 | grep -Fv 'Permission denied'
é equivalente e produz a mesma saída.
Os métodos mostrados acima para filtrar "Permissão negada" somente de mensagens de erro - e não de resultados de pesquisa - são bem-sucedidos. Por exemplo, aqui está o método em que stdout e stderr são trocados:
ek@Io:~/tmp$ (find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find args 2> >(grep -Fv 'Permission denied' >&2)
produz a mesma saída.
Você pode acionar uma mensagem de erro diferente para garantir que as linhas enviadas ao stderr que não contenham o texto "Permissão negada" ainda sejam permitidas. Por exemplo, aqui eu executei find
com o diretório atual ( .
) como um ponto de partida, mas o diretório inexistente foo
como outro:
ek@Io:~/tmp$ (find . foo 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
.
./a
./a/Permission denied messages.txt
./a/x
./c
./c/z
./w
./b
find: ‘foo’: No such file or directory
find
a saída padrão ainda é um terminalTambém podemos ver quais comandos fazem com que caracteres especiais, como novas linhas, sejam exibidos literalmente. (Isso pode ser feito separadamente da demonstração acima e não precisa estar no tmp
diretório.)
Crie um arquivo com uma nova linha em seu nome:
touch $'abc\ndef'
Normalmente, usamos diretórios como pontos de partida find
, mas os arquivos também funcionam:
$ find abc*
abc?def
Piping stdout para outro comando faz com que a nova linha seja emitida literalmente, criando a impressão falsa de dois resultados de pesquisa separados abc
e def
. Podemos testar isso com cat
:
$ find abc* | cat
abc
def
Redirecionar apenas stderr não causa esse problema:
$ find abc* 2>/dev/null
abc?def
Nem fechá-lo:
$ find abc* 2>&-
abc?def
Tubulação para grep
não causar o problema:
$ find abc* |& grep -Fv 'Permission denied'
abc
def
(Substituir |&
por 2>&1 |
é equivalente e produz a mesma saída.)
Trocar stdout e stderr e piping stdout não causa o problema find
- o stdout se torna stderr, que não é canalizado:
$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def
O agrupamento desse comando e a troca dos fluxos de volta não causam o problema:
$ (find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-
abc?def
(A {
;}
versão produz a mesma saída.)
O uso da substituição do processo para filtrar o stderr também não causa o problema:
$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def