Exclure des répertoires dans la recherche de localisation

12

Une recherche avec locatetrouve des chemins dans le système de fichiers.
Souvent, vous savez a priori que vous vous intéressez uniquement aux fichiers ou aux répertoires.
Une recherche «localiser» renvoie souvent de nombreux résultats. Il serait utile d'inclure un seul des types dans le résultat, car cela permet de raccourcir la sortie.

Mais il y a un argument plus intéressant pour laisser de côté les fichiers ou les répertoires: parce que la liste des chemins de résultats peut être ambiguë - pas seulement en théorie.

L'exemple ci-dessous est un cas réel, et n'est pas inhabituel:

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

Ok, on a trouvé quelque chose! Mais ... des fichiers ou des répertoires?

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

Voilà donc un fichier ...

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

tandis que le second ne l'est pas.

Cette ambiguïté rend les longues listes de chemins difficiles à lire, il serait donc très agréable de filtrer les répertoires, par exemple en utilisant une option de ligne de comman pour locate.

Existe-t-il quelque chose comme ça? Même si le filtre des répertoires est distinct de localiser?

Au moins, on pourrait utiliser un script pour parcourir tous les noms de fichiers à vérifier - ce qui peut être lent.

Volker Siegel
la source

Réponses:

3

Avec zsh:

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

(0)est un indicateur d'extension de paramètre qui se divise en caractères NUL (comme nous utilisons locate -0), abréviation de (ps:\0:).

Avec ^, au lieu d'ajouter (N.)à la fin du tableau, nous l'ajoutons à chaque élément.

(N.)est un qualificatif glob, .pour faire correspondre uniquement les fichiers normaux, Npour supprimer l'élément s'il ne correspond pas (n'existe pas ou n'est pas un fichier normal, ou nous ne pouvons pas vérifier). Vous pouvez également utiliser ^/au lieu de .pour faire correspondre des non-répertoires au lieu de seulement des fichiers normaux.

print -rlimprime chaque argument brut sur une ligne distincte .

Vous pouvez utiliser n'importe quel zshqualificatif de glob, mais notez que ceux de commande n'auront aucun effet, puisque nous développons ici un glob par fichier, il n'y a donc qu'un seul fichier à trier pour chacun.

(notez qu'il peut échouer si le dernier fichier signalé par locatese termine par des caractères de nouvelle ligne (une erreur de substitution de commande présente dans tous les shells)).

Stéphane Chazelas
la source
3

C'est à peu près aussi inélégant que les autres réponses, mais peut-être moins inefficace:

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

(divisé en deux lignes pour plus de lisibilité). Ce qui précède gérera les noms contenant des espaces. Le IFS=semble être nécessaire pour gérer les noms avec des espaces de fin , et, bien sûr, -rvous permet de gérer les barres obliques inverses.

L' locateapproche «Let's pipe into something» peut être vouée à l'échec si des chemins d'accès contenant des nouvelles lignes sont présents.


Pour plus d'informations sur IFS, lisez sh(1)ou bash(1) (en tapant man shou man bashdans un système * nix, et / ou en le lisant ici , ici , ici et / ou ici ). Ensuite, lisez Comprendre IFS et Bash: lisez ligne par ligne, avec IFS sur Stack Exchange (concentrez-vous sur les réponses avec plus de 5 votes), et, si vous n'en avez pas encore assez, consultez IFS sur le wiki de Greg et les résultats de recherche IFS sur le wiki Bash Hackers (pas sur Stack Exchange).

G-Man dit «Réintègre Monica»
la source
pouvez-vous ajouter quelques informations sur ce que "IFS =" après votre whiledéclaration fait?
robert
Je l'ai fait.
G-Man dit `` Réintègre Monica ''
les contre-obliques seront toujours un problème avec de nombreuses implémentations d'écho. Vous devez utiliser printfpour des données arbitraires .
Stéphane Chazelas
il peut y avoir une solution à votre problème de nouvelle ligne en utilisant le paramètre "--null" pour locateet augmentez votre readcomme suggéré ici transnum.blogspot.ie/2008/11/…
robert
@ StéphaneChazelas: Bon point. Fixé.
G-Man dit `` Réintègre Monica ''
2
locate --null --regex --basename "xfce4-keyboard-overlay$" |
  xargs -r0 sh -c 'find "$@" -prune ! -type d' sh
FloHimself
la source
En fait, c'est encore plus sale qu'il n'y paraît ... mais une bonne inspiration. Imaginons que ce soit un pseudocode, alors c'est utile :)
Volker Siegel
1
@Volker: Je suis d'accord que c'est mauvais: il listera /usr/local/share/xfce4-keyboard-overlay et chaque sous-répertoire de celui - ci , dans votre exemple. L'ajout d' -maxdepth 0aides.
G-Man dit `` Réintègre Monica ''
Ça va encore mieux ...: D locate --regex --basename "xfce4-keyboard-overlay$" | xargs -I % sh -c "test -d % && echo %"
FloHimself
1
Utiliser xargsavec findétait une bonne idée, je l'ai édité pour le rendre robuste. J'espère que cela ne vous dérange pas.
Stéphane Chazelas
1

xargsrépétera la commande pour chaque ligne si vous spécifiez -L 1ou -iparamètre.

Vois ici

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

Certes, il s'agit d'un nouveau shell pour chaque fichier, mais il a l'avantage d'être agréable et compact.

EDIT: Je n'étais pas très satisfait de cette réponse, car il s'agissait d'un nouveau shell pour chaque fichier. Cela ne devrait avoir que deux processus:

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

Bien sûr, ce serait bien si nous pouvions éviter de donner un coup de pied à un interprète, mais il xargssemble être paralysé dans sa capacité à enchaîner les commandes.

robert
la source
3
Celui-ci vient de redémarrer ma machine (il y avait un fichier appelé /home/evil/$(reboot)/xfce4-keyboard-overlayet je l'ai couru bêtement comme ça root).
Stéphane Chazelas
2
@ StéphaneChazelas +1 pour le courage d'exécuter "codez aléatoire depuis les internets" en tant que root;) (scnr)
Volker Siegel
0

Mes deux centimes:

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

C'est plus ou moins la façon dont G-Man l'a fait combiné avec la substitution de processus.

Tristan Storch
la source
En fait, c'est plus ou moins la façon dont je l' ai fait, combinée à la substitution de processus, moins la capacité de gérer les noms de fichiers contenant des barres obliques inverses ou ayant un espace blanc à la fin. Notez également que le titre de la question indique «exclure les répertoires» et que cette réponse inclut uniquement les répertoires.
G-Man dit `` Réintègre Monica ''
Pardon. Mon erreur. Corrigée.
Tristan Storch
-1

Et si vous combinez locateavec fileet grep? ...

$ for f in `locate --regex --basename "xfce4-keyboard-overlay$"`; do file $f; done | grep -vi directory
petry
la source
Je n'ai pas testé, mais je pense que cela peut être lent, car cela crée un processus filepour chaque chemin. Notez qu'il existe souvent de nombreuses lignes de résultats à localiser. Mon test actuel recherche "gnome", donnant environ 73 000 chemins à tester.
Volker Siegel
2
@Volker: C'est pire que ça: pour tout ce $fqui est un fichier, le fileprogramme ouvrira ce fichier et le lira . C'est très cher quand tout ce que vous avez à faire est stat(). ………… En outre, cela donnera des résultats erronés pour les fichiers qui contiennent «répertoire» dans leurs noms (comme «répertoire_téléphone»). …………… (De plus, la for f in `…`; do …syntaxe ne peut pas gérer les noms contenant des espaces.)
G-Man dit 'Reinstate Monica'