Retirar linhas de 'Acesso negado'

9

Quando eu uso find para ver todos os arquivos pdf no diretório /home , estou vendo access denied . Para eliminá-los eu tentei:

find /home -iname "*.pdf" | grep -v "access denied"

No entanto, o resultado é o mesmo. Como posso me livrar dessas linhas?

    
por solfish 09.08.2017 / 13:25

3 respostas

19

O que você tentou não funcionou porque a saída access denied é um erro e enviada em STDERR em vez de STDOUT, que é canalizada para grep .

Você pode evitar ver esses erros redirecionando apenas STDERR

find /home -iname "*.pdf" 2>/dev/null

Ou como David Foerster comentou podemos dizer de forma mais sucinta fechar STDERR

find /home -iname "*.pdf" 2>&-

No entanto, suspeito que na verdade você só quer pesquisar em sua casa em vez de em 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.

    
por Zanna 09.08.2017 / 13:29
8

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

Tente isto:

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

O 2>&1 redireciona a saída de stderr para stdout , de modo que grep -v possa fazer seu trabalho. (Por padrão, | apenas canaliza stdout e não stderr .)

    
por Android Dev 09.08.2017 / 13:30
4

Você provavelmente quer dizer "Permissão negada", que é o que find no 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 é a mesma) é:

(find 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied') 3>&1 1>&2 2>&3 3>&-

(Normalmente você quer passar alguns argumentos para find . Esses vão antes do primeiro redirecionamento 3>&1 .)

No entanto, muitas vezes você poderá usar algo mais simples. Por exemplo, você provavelmente pode usar a substituição de processos . Detalhes seguem.

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

As duas abordagens típicas são jogar fora stderr (como em Zanna's answer ) ou para redirecionar o stderr para stdout e stdout do filtro (como em resposta do Android Dev ). Embora tenham a vantagem de serem simples de escrever e geralmente sejam escolhas razoáveis, essas abordagens não são ideais.

Descarta tudo o que foi enviado para stderr , redirecionando-o para o dispositivo nulo com 2>/dev/null ou fechando-a com 2>&- - corre o risco de perder erros diferentes de "Permissão negada".

"Permissão negada" é provavelmente o erro mais comum visto ao executar find , mas está longe do único erro possível, e se outro ocorrer, você pode querer saber sobre isso. Em particular, find reporta "Nenhum tal arquivo ou diretório" se um ponto inicial 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 existirem, mas b não, find a b c -name x imprimirá resultados em a , "Nenhum arquivo ou diretório" para b resultará em c .

Combinar stdout e stderr juntos em stdout e canalizá-lo para grep ou algum outro comando para filtrá-lo - como acontece 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 as linhas que contêm "Permissão negada", você também excluirá os resultados da pesquisa que exibem nomes de arquivo como "Permissão negada messages.txt". Isso provavelmente aconteceria por acidente, embora também seja possível que um arquivo receba um nome especialmente criado para impedir suas pesquisas.

Filtrar os fluxos combinados tem outro problema, que não pode ser mitigado filtrando de forma mais seletiva (como com grep -vx 'find: .*: Permission denied' no lado direito do canal). Algumas ações find , incluindo a ação -print que está implícita quando você não especifica nenhuma ação, determinam como gerar nomes de arquivos com base em se stdout a> é um terminal.

  • Se não for um terminal, os nomes dos arquivos serão emitidos como estão, mesmo que contenham caracteres estranhos, como novas linhas e caracteres de controle, que possam alterar o comportamento do seu terminal. Se for um terminal, esses caracteres serão suprimidos e ? será impresso.
  • Geralmente é o que você quer. Se você for processar mais nomes de arquivos, eles devem ser impressos literalmente. No entanto, se você for exibi-los, um nome de arquivo com uma nova linha poderia imitar vários nomes de arquivos, e um nome de arquivo com uma sequência de caracteres de retrocesso poderia parecer um nome diferente. Outros problemas também são possíveis, como nomes de arquivos contendo seqüências de escape que alteram as cores no seu terminal.
  • Mas canalizar os resultados da pesquisa por meio de outro comando (como grep ) faz com que find não veja mais um terminal. (Mais precisamente, faz com que seu stdout não seja um terminal.) Então, caracteres estranhos são produzidos literalmente. Mas se todo o comando do lado direito do pipe for (a) remove linhas que se parecem com mensagens "Permissão negada" e (b) imprime o que resta, então você ainda está sujeito ao tipo de travessuras que a detecção de terminal do find deve evitar.
  • Veja a seção UNENUAL FILENAMES de man find para mais informações, incluindo o comportamento de cada uma das ações que imprimem nomes de arquivos. ( "Muitas das ações de encontrar resultam na impressão de dados que está 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 se refere a descoberta GNU , que é a implementação find em Sistemas GNU / Linux incluindo o Ubuntu.

Deixando a saída padrão sozinha durante o filtro de erro padrão

O que você realmente quer aqui é deixar stdout intacto enquanto canaliza stderr para grep . Infelizmente não há sintaxe simples para isso. | pipes stdout e alguns shells (incluindo bash ) suportam |& para canalizar os dois fluxos - ou você pode redirecionar stderr para stdout primeiro com 2>&1 | , que tem o mesmo efeito. Mas os shells comumente usados não fornecem uma sintaxe apenas para o stderr do pipe.

Você ainda pode fazer isso. É apenas estranho. Uma maneira é trocar stdout por stderr , para que os resultados da pesquisa estejam em stderr e os erros estejam em stdout e, em seguida, canalize stdout para grep para filtragem:

find args 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'

Normalmente, você passará argumentos para find , como pontos iniciais (os locais de pesquisa, que geralmente são diretórios) e predicados (testes e ações). Estes substituem args acima.

Isso funciona introduzindo um novo descritor de arquivo para conter um dos dois fluxos padrão que você deseja trocar, executando redirecionamentos para trocá-los e fechar o novo descritor de arquivos.

  • O descritor de arquivo 1 é stdout e 2 é stderr (e 0 não-redirecionado é stdin ) . Mas você também pode redirecionar usando outros descritores de arquivos. Isso pode ser usado para abrir ou manter aberto um arquivo ou dispositivo.
  • 3>&1 redireciona o descritor de arquivo 3 para stdout, de modo que quando o stdout (descritor de arquivo 1) for subsequentemente redirecionado, o stdout original ainda pode ser gravado facilmente.
  • 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 arquivos 3, que não é mais necessário.
  • Para obter mais informações, consulte Como canalizar stderr e não stdout? e Redirecionamento IO - Troca de stdout e stderr (Avançado) e especialmente canalize apenas o stderr através de um filtro .

No entanto, esse método tem a desvantagem de que os resultados de pesquisa são enviados para stderr e os erros são enviados para stdout . Se você estiver executando este comando diretamente em um shell interativo e não canalizar ou redirecionar a saída, isso não importa realmente. Caso contrário, isso pode ser um problema. Se você colocar esse comando em um script e, em seguida, alguém (talvez você, mais tarde) redirecionar ou canalizar sua saída, ele não se comportará como esperado. .

A solução é trocar os fluxos de volta depois que você terminar de filtrar a saída . Aplicar os mesmos redirecionamentos mostrados acima no lado direito do pipeline não conseguirá isso, porque | apenas canaliza stdout, de modo que o lado do pipeline recebe apenas a saída que foi originalmente enviada para stderr (porque os fluxos foram trocados) e não a saída stdout original. Em vez disso, você pode usar ( ) para executar o comando acima em uma subshell ( related ), depois aplique os redirecionamentos de troca para isso:

(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 subnível, que faz este 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 trabalhosa: substituição de processos

Alguns shells, incluindo o Bash em sistemas que podem suportá-lo (incluindo sistemas GNU / Linux como o Ubuntu), permitem que você execute substituição de processos , que permite que você execute um comando e redirecione para / de um de seus streams. Você pode redirecionar o stderr do comando find para um comando grep que o filtra, e redirecionar o stdout do comando grep para stderr.

find args 2> >(grep -Fv 'Permission denied' >&2)

O crédito vai para ao Android Dev para essa ideia.

Embora o bash suporte processo de substituição, sh no Ubuntu é dash , o que não funciona. Ele lhe dará "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 o processo a substituição está desativada.

Uma situação em que bash é executado no modo POSIX é quando é invocada como sh 1 . Portanto, em um sistema operacional como o Fedora, onde bash fornece /bin/sh , ou se você fez o link /bin/sh apontar para bash no Ubuntu, a substituição de processo ainda não funciona em um script sh , sem um comando anterior para desativar o modo POSIX. Sua melhor aposta, se você quiser usar este método em um script, é colocar #!/bin/bash na parte superior em vez de #!/bin/sh , se você ainda não estiver.

1 : Nesta situação, bash ativa 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, eu crio um subdiretório tmp do diretório atual e preencho-o com alguns arquivos e diretórios, removendo as permissões de um deles para acionar um erro de "Permissão negada" em 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 de 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

Pipar stdout e stderr para grep e filtrar as linhas que contêm "Permission denied" faz com que a mensagem de erro desapareça, mas também oculta o resultado da pesquisa para o arquivo com essa 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 para 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

Verificando se a saída padrão de find 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 diretório tmp .)

Crie um arquivo com uma nova linha em seu nome:

touch $'abc\ndef'

Normalmente, usamos diretórios como pontos de partida para find , mas os arquivos também funcionam:

$ find abc*
abc?def

A saída da tubulação para outro comando faz com que a nova linha seja exibida 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 o stderr não causa esse problema:

$ find abc* 2>/dev/null
abc?def

Nem fecha:

$ find abc* 2>&-
abc?def

Piping para grep faz 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 stdout de piping faz não causar o problema— o stdout de find se torna stderr, que não é canalizado:

$ find abc* 3>&1 1>&2 2>&3 3>&- | grep -Fv 'Permission denied'
abc?def

Agrupar esse comando e trocar os fluxos de volta não causa 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.)

Usar a substituição de processo para filtrar stderr também não causa o problema:

$ find abc* 2> >(grep -Fv 'Permission denied' >&2)
abc?def
    
por Eliah Kagan 10.08.2017 / 15:16