Removendo linhas de 'Acesso negado "

9

Quando uso findpara ver todos os arquivos pdf no /homediretó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?

peixe-sol
fonte

Respostas:

19

O que você tentou não funcionou porque a access deniedsaí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.

Zanna
fonte
3
Grrr, por que as pessoas sempre me vencem por 30 segundos? : \
AGitForNotUsingGit
find: “/home/ihsan/.gvfs”: acesso negado find: “/home/ihsan/.dbus”: acesso negado, para o comando ~
solfish
há algo de errado nisso? Sim, eu também quero outro diretório home do usuário que também é criado por mim para teste
solfish
2
@solfish Até onde eu sei, esses arquivos devem pertencer a você. Você pode querersudo chown $USER: ~/.gvfs ~/.dbus
Zanna
11
Deve ser o suficiente para fechar o stderr 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.
precisa
8

O acesso negado provavelmente está sendo impresso em stderrvez de stdout.

Tente o seguinte:

find /home -iname "*.pdf" 2>&1 | grep -v "access denied"

O 2>&1redireciona a saída de stderrpara stdout, para que ele grep -vpossa fazer seu trabalho. (Por padrão, |apenas tubos stdoute não stderr.)

Você éAGitForNotUsingGit
fonte
mas para isso 2> & 1 significa se existe stderr enviar para stdout?
solfish
@solfish Sim, isso é exatamente o ponto :)
You'reAGitForNotUsingGit
o que eu não entendo é antes "|" como uma saída; temos apenas stderr certo? e depois de "|" como uma entrada que tem esse
solfish
@ Wolfish Bem, eu me deparei com esse problema há cerca de um ano e meio atrás, e pude corrigi-lo usando um método diferente . Mas, em seguida, um comentário abaixo a minha resposta sugeriu simplesmente usar 2>&1... Eu não sou um especialista em bash, por isso, se isso é incorrecta, por favor, diga :)
You'reAGitForNotUsingGit
@AndroidDev Sugiro adicionar esse método diferente a esta resposta como alternativa. A crítica de Etan Reisner foi que a substituição de processos não é portátil. Mas bashno Ubuntu, exceto no modo POSIX . Eu acho que é a melhor solução - um arquivo com o nome malicioso access deniedainda aparecerá.
Eliah Kagan
4

Você provavelmente quer dizer "Permissão negada" - que é o que o findUbuntu 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.

Os métodos mais comuns e suas limitações

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/nullou 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, findinforma "Não existe esse arquivo ou diretório" se um ponto de partida não existir. Com vários pontos de partida, findainda pode retornar alguns resultados úteis e parecer funcionar. Por exemplo, se ae cexiste, mas bnão existe, find a b c -name ximprime 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 grepou 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 findações, incluindo a -printaçã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.

  • Se não for um terminal, os nomes dos arquivos serão exibidos como são, mesmo que contenham caracteres estranhos, como novas linhas e caracteres de controle, que podem mudar o comportamento do seu terminal. Se for um terminal, esses caracteres são suprimidos e, em ?vez disso, são impressos.
  • Isso geralmente é o que você deseja. Se você deseja processar mais os nomes de arquivos, eles devem ser exibidos literalmente. No entanto, se você for exibi-los, um nome de arquivo com uma nova linha poderá imitar vários nomes de arquivos, e um nome de arquivo com uma sequência de caracteres de backspace pode parecer um nome diferente. Outros problemas também são possíveis, como nomes de arquivos que contêm sequências de escape que alteram as cores do seu terminal.
  • Mas canalizar os resultados da pesquisa por outro comando (como grep) faz com que findnã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 findsão terminais A detecção visa impedir.
  • Consulte a seção UNUSUAL FILENAMES de man findpara 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 findimplementação em sistemas GNU / Linux, incluindo o Ubuntu.

Deixando a saída padrão sozinha ao filtrar o erro padrão

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 greppara 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 argsacima.

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.

  • O descritor de arquivo 1 é stdout e 2 é stderr (e o 0 não redirecionado é stdin ). Mas você também pode redirecionar usando outros descritores de arquivo. Isso pode ser usado para abrir ou manter aberto um arquivo ou dispositivo.
  • 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>&2redireciona 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.
  • Para obter mais informações, consulte Como canalizar stderr e não stdout? e Redirecionamento de E / S - Trocar stdout e stderr (Avançado) e, principalmente, canalizar apenas stderr através de um filtro .

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>&-

Uma maneira menos complicada: Substituição de processos

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 findstderr do comando para um grepcomando que o filtra e redirecionar grepo 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.

Embora bash suporte a substituição de processos, shno 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 bashexecutado no modo POSIX , o suporte para substituição de processos é desativado.

Uma situação em que bashé executado no modo POSIX é quando é chamado como sh1 . Portanto, em um sistema operacional como o Fedora onde bashfornece /bin/sh, ou se você /bin/shapontou o link simbólico para bashvocê no Ubuntu, a substituição do processo ainda não funciona em um shscript, 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, bashativa o modo POSIX automaticamente após executar os comandos em seus scripts de inicialização.

Um exemplo

É útil poder testar esses comandos. Para fazer isso, crio um tmpsubdiretó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 findsem 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 grepe 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 findcom o diretório atual ( .) como um ponto de partida, mas o diretório inexistente foocomo 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

Verificar se finda saída padrão ainda é um terminal

També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 tmpdiretó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 abce 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
Eliah Kagan
fonte