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