Excluir diretórios na pesquisa de localização

12

Uma pesquisa com locateencontra caminhos no sistema de arquivos.
Muitas vezes, você sabe a priori que está interessado apenas em arquivos ou somente em diretórios.
Uma pesquisa 'localizar' geralmente retorna muitos resultados. Seria útil incluir apenas um dos tipos no resultado, pois ajuda a diminuir a saída.

Mas há um argumento mais interessante para excluir arquivos ou diretórios: porque a lista de caminhos de resultados pode ser ambígua - não apenas na teoria.

O exemplo abaixo é um caso do mundo real, e não incomum:

$ locate --regex --basename "xfce4-keyboard-overlay$"
/usr/local/bin/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay

Ok, encontramos algo! Mas ... arquivos ou diretórios?

$ file /usr/local/bin/xfce4-keyboard-overlay 
/usr/local/bin/xfce4-keyboard-overlay:   bash script

Então esse é um arquivo ...

$ file /usr/local/share/xfce4-keyboard-overlay
/usr/local/share/xfce4-keyboard-overlay: directory

enquanto o segundo não é.

Essa ambiguidade está dificultando a leitura de longas listas de caminhos; portanto, seria muito bom filtrar diretórios, por exemplo, usando uma opção de linha de comman para locate.

Será que algo assim existe? Mesmo se o filtro para diretórios estiver separado da localização?

Pelo menos, pode-se usar um script para iterar todos os nomes de arquivos a serem verificados - o que pode ser lento.

Volker Siegel
fonte

Respostas:

3

Com zsh:

print -rl ${(0)^"$(locate -0 ...)"}(N.)

(0)é um sinalizador de expansão de parâmetro que se divide em caracteres NUL (como usamos locate -0), abreviação de (ps:\0:).

Com ^, em vez de adicionar (N.)no final da matriz, nós a adicionamos a cada elemento.

(N.)é um qualificador global, .para corresponder apenas aos arquivos regulares, Npara remover o elemento se ele não corresponder (não existe ou não é um arquivo comum ou não podemos verificar). Você também pode usar em ^/vez de .para corresponder a não diretórios, em vez de apenas arquivos regulares.

print -rlimprime cada argumento bruto em uma linha separada .

Você pode usar qualquer zshqualificador de glob, mas observe que os pedidos não terão efeito, pois estamos expandindo um glob por arquivo aqui, portanto, há apenas um arquivo para classificar para cada um.

(observe que pode falhar se o último arquivo relatado locateterminar com caracteres de nova linha (uma falha de substituição de comando presente em todas as shells)).

Stéphane Chazelas
fonte
3

Isso é tão deselegante quanto as outras respostas, mas talvez menos ineficiente:

locate --regex --basename "xfce4-keyboard-overlay$" | 
        while IFS= read -r f; do [ -f "$f" ] && printf "%s\n" "$f"; done

(dividido em duas linhas para facilitar a leitura). O acima irá lidar com nomes contendo espaços. O IFS=parece ser necessário para lidar com nomes com à direita espaços, e, claro, o -rpermite lidar com barras invertidas.

A locateabordagem "vamos entrar em algo" pode estar fadada ao fracasso se houver nomes de caminho que contenham novas linhas.


Para obter mais informações IFS, leia sh(1)ou bash(1) (digitando man shou man bashem um sistema * nix e / ou lendo aqui , aqui , aqui e / ou aqui ). Em seguida, leia Entendendo o IFS e o Bash: leia linha por linha, com o IFS no Stack Exchange (concentre-se nas respostas com mais de 5 votos) e, se você ainda não tiver o suficiente, consulte o IFS nos resultados de pesquisa Wiki e IFS de Greg no Wiki do Bash Hackers (não no Stack Exchange).

G-Man diz que 'restabelece Monica'
fonte
você pode adicionar algumas informações sobre o que o "IFS =" após a sua whiledeclaração?
robert
Eu fiz isso.
G-Man diz 'Reinstate Monica'
as barras invertidas ainda serão um problema com muitas implementações de eco. Você deve usar printfpara dados arbitrários .
Stéphane Chazelas 27/03
pode haver uma solução para o seu problema de novas linhas usando o parâmetro "--null" e aumentando o locateseu, readconforme sugerido aqui transnum.blogspot.ie/2008/11/…
robert
@ StéphaneChazelas: Bom ponto. Fixo.
G-Man diz 'Restabelecer Monica'
2
locate --null --regex --basename "xfce4-keyboard-overlay$" |
  xargs -r0 sh -c 'find "$@" -prune ! -type d' sh
FloHimself
fonte
Na verdade, é ainda mais sujo do que parece ... mas uma boa inspeção. Vamos fingir que é pseudo-código, então é :) útil
Volker Siegel
1
Volker @: Concordo que é ruim: ele irá listar /usr/local/share/xfce4-keyboard-overlay e todos os subdiretórios , no seu exemplo. Adicionando -maxdepth 0ajuda.
G-Man diz 'Reinstate Monica'
Vai ainda melhor ...: D locate --regex --basename "xfce4-keyboard-overlay$" | xargs -I % sh -c "test -d % && echo %"
FloHimself
1
Usar xargscom findfoi uma boa ideia, editei para torná-lo robusto. Espero que você não se importe.
Stéphane Chazelas 27/03
1

xargsrepetirá o comando para cada linha se você especificar -L 1ou -iparâmetro.

Veja aqui

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i bash -c '(test -d "{}" && echo "{}")'

É certo que ele oferece um novo shell para cada arquivo, mas tem o benefício de ser agradável e compacto.

Edição: Eu não estava muito feliz com essa resposta porque estava chutando um novo shell para cada arquivo. Isso deve ter apenas dois processos:

$ locate --regex --basename "xfce4-keyboard-overlay$" | xargs -i echo 'test -d "{}" && echo "{}"' | bash

Claro que seria bom se pudéssemos evitar o chute de um intérprete por completo, mas xargsparece estar comprometido em sua capacidade de encadear comandos.

Robert
fonte
3
Aquele acabou de reiniciar minha máquina (havia um arquivo chamado /home/evil/$(reboot)/xfce4-keyboard-overlaye eu tolamente o executei como root).
Stéphane Chazelas 27/03
2
@ StéphaneChazelas +1 para a coragem de correr "codez aleatória das internets" como root;) (SCNR)
Volker Siegel
0

Meus dois centavos:

while IFS= read i; \
do \
  if [ -f "$i" ]; \
  then \
    echo "$i"; \
  fi; \
done < <(locate --regex --basename "xfce4-keyboard-overlay$")

É mais ou menos assim que o G-Man fez isso, combinado com a substituição do processo.

Tristan Storch
fonte
Na verdade, é mais ou menos assim que eu fiz, combinado com a substituição do processo, menos a capacidade de lidar com nomes de arquivos contendo barras invertidas ou com espaço em branco à direita. Além disso, observe que o título da pergunta diz "excluir diretórios" e esta resposta inclui apenas diretórios.
G-Man diz 'Reinstate Monica'
Desculpe. Meu erro. Corrigido.
Tristan Storch
-1

E se você combinar locatecom filee grep? ...

$ for f in `locate --regex --basename "xfce4-keyboard-overlay$"`; do file $f; done | grep -vi directory
petry
fonte
Não testei, mas acho que pode ser lento, porque cria um processo filepara cada caminho. Observe que muitas vezes existem muitas linhas de resultados para localização. Meu teste atual está procurando por "gnome", fornecendo cerca de 73000 caminhos para testar.
Volker Siegel
2
@ Volker: É pior do que isso: para cada $farquivo que é um arquivo, o fileprograma abrirá esse arquivo e o lerá . Isso é extremamente caro quando tudo que você precisa fazer é a stat(). ………… Além disso, isso fornecerá resultados incorretos para arquivos que contenham "diretório" em seus nomes (como "diretório_de_elefone"). …………… (Além disso, a for f in `…`; do …sintaxe não pode lidar com nomes que contenham espaços.)
G-Man diz 'Reinstate Monica'