Quand la chaîne vide indique-t-elle le répertoire actuel?

8

Dans un script que j'utilise findpour collecter des fichiers dans le répertoire courant, comme dans

$ find . -name "*.h"
./foo.h

Maintenant, je voudrais juste le sortir foo.h, sans le ./préfixe. Je pensais que la chaîne vide ""dénotait le répertoire courant dans les commandes shell. Mais cela donne:

$ find "" -name "*.h"
find: ftsopen: No such file or directory

J'avais donc tort. Maintenant ma question est quand / comment / où / .. une "chaîne vide (?)" Désigne-t-elle le répertoire courant dans les commandes qui attendent un nom de fichier ou un chemin? Y a-t-il une explication nette et éclairante?

Une question secondaire est de savoir si la découverte au-dessus peut être résolue simplement, sans manipulation de chaîne à la ${parameter#word}ou cutou sed?

phs
la source
1
finda le paramètre -printf pour manipuler la façon dont les résultats sont affichés. find . -name '*.h' -printf '%P\n'supprimera le ./préfixe. Voyez ce que find . -name '*.h' -printça fait.
Valentin Bajrami
Merci! Dommage que ma version de find ne supporte pas l' -printfoption. FWIW, stringssur ma trouvaille donne @(#)PROGRAM:find PROJECT:shell_cmds-175?!?
phs
5
@phs c'est le genre de chose qui fait qu'il est essentiel que vous mentionniez toujours votre système d'exploitation.
terdon

Réponses:

11

Il y a longtemps (dans la 7e édition , 32V , 4.2BSD , 4.3BSD ), au niveau de l'appel système, un chemin d'accès de longueur nulle désignait le répertoire de travail actuel (lorsqu'il était utilisé pour la recherche; il n'était pas autorisé lors de la tentative de création ou de suppression d'un fichier ou répertoire). Dans System III , c'était une erreur d'utiliser un chemin d'accès de longueur nulle dans toutes les circonstances, et la norme POSIX a ceci à dire à propos de la résolution du nom de chemin:

Un chemin d'accès nul ne doit pas être résolu avec succès.

Mark Plotnick
la source
3
Une chaîne vide dans $ PATH, $ MANPATH, $ LD_LIBRARY_PATH (et d'autres mais pas nécessairement tous les $ * PATH) indique le répertoire courant. dir/est essentiellement le même quedir/.
Stéphane Chazelas
4

Vous pouvez utiliser:

find * -name "*.h"

Notez que les fichiers du répertoire actuel dont le nom commence par .seront omis et que les fichiers dont le nom commence par -seront interprétés comme des options par findet provoquent des ravages, ce n'est donc pas un équivalent général de find . ….

L'absence d'une chaîne se terminant par " /" dans le cadre d'un nom de fichier implique le répertoire en cours, mais cela ne signifie pas que le répertoire en cours est indiqué par une chaîne vide (ce qui n'est sans doute pas la même chose que l'absence d'une chaîne, bien qu'il puisse ressembler à l'impression).

Anthon
la source
4

En général, la chaîne vide ne désigne pas le répertoire courant, ni pour les commandes shell ni dans les appels système. C'était le cas sur certains systèmes plus anciens, mais pas sur les systèmes compatibles POSIX .

Parfois, vous trouverez un programme qui utilise le répertoire actuel lorsque vous passez une chaîne vide et que le programme attend un nom de répertoire. Cela est parfois délibéré, et parfois un effet secondaire de l'ajout du chemin absolu du répertoire actuel lorsque la chaîne donnée ne commence pas par une barre oblique.

La meilleure chose pour vous serait de quitter le ./. Cela ne fait aucun mal.

Si la liste des fichiers est pour

find . … | sed 's!^\./!!'

Notez que cela modifie certains noms de fichiers contenant des retours à la ligne. Ce n'est généralement pas un problème pour la consommation humaine, et la sortie de findne convient pas à la consommation de programme car elle est ambiguë. Si vous utilisez -print0, ce qui convient à la consommation du programme, vous ne vous souciez probablement pas du ./préfixe de toute façon.

Vous pouvez utiliser à la find * …place de find . …, mais notez qu'il find *présente un certain nombre de défauts qui le rendent généralement inapproprié:

  • . est omis.
  • Tous les fichiers de points (fichiers dont le nom commence par .ou ..`) sont omis.
  • S'il y a un nom de fichier dans le répertoire courant dont le nom commence par -(ou un fichier appelé !ou (...), il sera interprété comme une option ou un prédicat par find.

Le premier point n'a pas d'importance si votre filtre exclut le répertoire actuel. Pour le deuxième point, vous pouvez utiliser les modèles ..?* .[!.]* *pour faire correspondre tous les fichiers du répertoire en cours, mais vous devrez vérifier si chaque modèle correspond à au moins un fichier et l'omettre s'il ne le fait pas. C'est possible mais très lourd. Le dernier point est un bouchon. Cela find *peut donc convenir à une utilisation en ligne de commande rapide, mais ne l'utilisez pas dans un script.

Une autre approche consiste à utiliser la fonction de globulation récursive du shell, par exemple

printf '%s\n' **/*.h

Cela doit être activé par shopt -s globstardans bash et par set -o globstardans ksh93, et n'existe pas dans un shell POSIX de base tel que dash. Les fichiers de points ne seront pas traversés par défaut; pour les inclure, assurez-vous d'abord de ne pas ignorer les fichiers dot avec shopt -s dotgloben bash ou FIGNORE='@(.|..)'en ksh93. De plus, s'il n'y a pas de correspondance, cette commande imprime le motif; exécutez shopt -s nullgloben bash pour imprimer une ligne vide à la place et utilisez le modèle ~(N)**/*.hdans ksh.

Dans zsh, la globalisation récursive est activée par défaut. Utilisez le qualificatif glob Dpour inclure des fichiers de points et Npour imprimer une ligne vide s'il n'y a pas de correspondance (par défaut, zsh renvoie une erreur si un motif ne correspond à aucun fichier). Vous pouvez utiliser printfcomme ci-dessus ou

print -rl -- **/*.h(DN)
Gilles 'SO- arrête d'être méchant'
la source
Merci pour tous ces conseils. La plupart des défauts avec find *sont nouveaux pour moi, et certains sont effrayants !!
phs
2

Je ne peux penser à aucun exemple où la chaîne vide indique le répertoire courant .. Vous pensez peut-être à des invocations comme ls, mais c'est parce que lssuppose le répertoire courant si aucun paramètre n'est donné, et en fait il ne prendra pas la chaîne vide:

ulmi@silberfisch:~$ ls ""
ls: cannot access : No such file or directory
Ulrich Schwarz
la source
3
Un exemple où la chaîne vide indique le répertoire courant est en tant que PATHcomposant.
hvd
0

Il existe différentes astuces que vous pouvez utiliser pour obtenir la sortie de find sans le début ./:

  1. Utilisez l' -printfoption de recherche et dites-lui d'imprimer uniquement %f. Voir man find:

    % f Nom du fichier avec tous les principaux répertoires supprimés (uniquement le dernier élément).

    Par exemple:

    find . -name "*.h" -printf "%f\n"
    
  2. Analyser la sortie:

    find . -name "*.h" | sed 's#^./##'
    
  3. @ Le tour d'Anthon

    find * -name "*.h"
    

Quant à votre autre question, la chaîne vide ne désigne jamais le répertoire courant. C'est juste que divers programmes prennent le répertoire courant par défaut, donc quand vous les exécutez sans arguments, ils sont exécutés sur le répertoire courant.

terdon
la source
Cela suppose GNU find. Ou utilisez %Puniquement pour dépouiller le./
Stéphane Chazelas
1
find * -name '*.h'trouvera foo/.bar.h, mais pas .bar.hdans le répertoire courant.
Stéphane Chazelas
0

Si les fichiers sont dans le répertoire courant, vous pouvez simplement utiliser ls:

$ ls *.sh

Si vous voulez également les fichiers dans les sous-répertoires et que vous avez juste besoin du nom de fichier, vous pouvez faire quelque chose comme:

$ find . -name '*.h' -exec basename {} \;

Il existe des commandes qui obtiendront l'absence de chaîne comme répertoire courant, mais essentiellement parce qu'elles obtiendront le répertoire courant par défaut si rien n'est entré. Le .dénote toujours ..le répertoire courant (et le répertoire parent)

Karl
la source
l'OP ne voulait que ./ supprimé, tandis que votre findsolution supprime tous les composants du répertoire
artm