Sortie bizarre (peut-être) de la commande wc -l

bonjour ,

en essayant de comprendre comment fonctionne une substitution de processus j’ai obtenu un curieux affichage : alors que selon le manuel wc -l devrait afficher un nombre de lignes il affiche aussi un nom de fichier temporaire

mm@Xfce:~$ ls | wc -l
11
mm@Xfce:~$ wc -l <(ls)
11 /dev/fd/63
m@Xfce:~$ wc -l <(ls) <(lsa)  ( lsa = alias dérivé de ls)
     11 /dev/fd/63
     41 /dev/fd/62
     52 total

je pense avoir compris le rôle des fichiers temporaires /dev/fd/63 et 62 , mais ce que je ne comprends pas c’est : comment une commande destinée à afficher un nombre de lignes , ce qu’elle fait dans les deux cas d’ailleurs , peut aussi afficher le nom d’un fichier ?

Je suppose que si cet affichage existe c’est bien qu’il a été prévu dans le comportement de la commande wc et donc que le manuel proposé par man ne répertorie que les cas courants . Où trouver le manuel de référence expliquant chaque commande en détail car je n’ai rien vu dans le manuel de référence bash ?

ps : note sur une similarité où je ne prévoyais aucune sortie puisque ce n’est pas la fonction de echo de lire un fichier

mm@Xfce:~$ echo <(echo foo)
/dev/fd/63

il doit donc s’agir d’un comportement général de bash . Je vais retourner sur le manuel de référence , au cas où … mais toute suggestion est bienvenue .

Ce ne sont pas des fichiers temporaires, ce sont des descripteurs de fichiers.
Dans le système de fichiers, ce sont des liens symboliques vers autre chose.
Si tu lances la commande ls -l /dev/fd/, tu verras vers quoi ça mène.

Quand un nom de ficher est fourni à wc, les noms des fichiers sont affichés.
Mais tu dois probablement te demander à quel moment tu as bien pu donner un nom de fichier dans la commande que tu as tapé.

Je pense avec compris avec ce retour de commande.
Je n’ai pas trouvé de documentation qui en parle, mais il semble que, la syntaxe que tu utilises, l’interpréteur de commandes remplace le <(echo foo) par un nom de fichier qui contient le retour de la commande.
Dans ton cas, l’interpréteur de commande lance la commande echo foo et redirige sa sortie standard vers le descripteur 63 de la commande echo (enfin, echo /dev/fd/63 après remplacement) et tu as donc le retour de tout ça.
Si tu veux avoir « foo » comme retour, le mieux reste de lancer cat <(echo foo) (que d’aucuns critiqueront vivement pour que tu ne fasses pas un usage inutile de la commande cat).

" je pensais avoir compris que la substitution de processus entraînait l’écriture de la sortie de <(cmd) dans un fichier ( le stockage d’une liste de 11 lignes nécessite bien un fichier , temporaire dans ce cas puisqu’il n’apparaît pas dans mon répertoire ) qui ensuite était lu par la 1ère commande , ici wc -l qui alors s’ exécutait et fournissait stdout affiché à l’écran . Tout ça me semblait simple et utile en cas de commandes multiples comme avec wc -l <(ls) <(lsa) .

Il y aurait donc au moins une étape supplémentaire (1) : sortie(?) de <(ls)= descripteur de fichier ----> fichier temporaire utilisé comme fichier-cible par wc -l qui en compte les lignes —> écran .
(1) : en fait cf message

Quand un nom de ficher est fourni à wc , les noms des fichiers sont affichés.

mm@Xfce:~$ wc .bash_aliases 
 10  54 404 .bash_aliases

je vais devoir relire le manuel ; soigneusement cette fois .

Si tu lances la commande ls -l /dev/fd/ , tu verras vers quoi ça mène.

mm@Xfce:~$ ls -l /dev/fd
lrwxrwxrwx 1 root root 13 16 nov.  19:52 /dev/fd -> /proc/self/fd
mm@Xfce:~$ ls -a /proc/self/fd
0  1  2  3

pour une raison qui m’échappe seule une petite partie du contenu de ce répertoire est affiché . Pas de 62 ou 63 mais c’est peut-être parce qu’ils ne sont que temporaires et disparaissent une fois la commande exécutée ?

Dans ton cas, l’interpréteur de commande lance la commande echo foo et redirige sa sortie standard vers le descripteur 63 de la commande echo (enfin, echo /dev/fd/63 après remplacement) et tu as donc le retour de tout ça.

là il va falloir que je réfléchisse pour essayer de comprendre .

en tout cas merci pour toutes ces précisions et le point n°2 à propos de la commande wc répond à mon interrogation principale .

Non, je n’ai pas dit ls -l /dev/fd, mais ls -l /dev/fd/. /dev/fd est un lien symbolique […] vers un répertoire, si tu ne mets pas le / final, ls va penser que tu peux voir le lien symbolique et non pas le contenu du répertoire ciblé.

Absolument pas, c’est juste une question de point de vue.
Les descripteurs de fichier appartienne à leur processus respectifs, tu ne peux pas accéder aux descripteurs d’un programme en demandant les descripteurs d’un autre programme, d’autant plus quand celui-ci n’est plus dans la table de processus.
Dans la commande ls -a /proc/self/fd, tu ne vois que les descripteurs ouverts par ls, à savoir :

  • 0 : l’entrée standard
  • 1 : la sortie standard
  • 2 : la sortie d’erreur standard
  • 3 : probablement le répertoire ciblé par le lien /proc/self/fd

C’est ce que j’ai si je fais pareil de mon côté (/dev/pts/1 étant mon terminal et 2205806 étant le numéro de processus de la commande lancée).

┌ (almtesh@Thorn + 0) (20/11/22 - 9:19:05) (1.40 - 0%) (~)
└% ls -l /dev/fd/
total 0
lrwx------ 1 almtesh sudo 64 20 nov.  11:29 0 -> /dev/pts/1
lrwx------ 1 almtesh sudo 64 20 nov.  11:29 1 -> /dev/pts/1
lrwx------ 1 almtesh sudo 64 20 nov.  11:29 2 -> /dev/pts/1
lr-x------ 1 almtesh sudo 64 20 nov.  11:29 3 -> /proc/2205806/fd

eh bien voilà un autre point auquel je n’avais jamais prếté attention , sauf lorsque / se trouve au début et qu’il empĉhe l’autocomplétion :

mm@Xfce:~$ cd /Do             = refus d'autocomplétion ( il manque ~ )
> ^C

et donc :

mm@Xfce:~$ ls -l /dev/fd/
total 0
lrwx------ 1 mm mm 64 20 nov.  11:37 0 -> /dev/pts/4
lrwx------ 1 mm mm 64 20 nov.  11:37 1 -> /dev/pts/4
lrwx------ 1 mm mm 64 20 nov.  11:37 2 -> /dev/pts/4
lr-x------ 1 mm mm 64 20 nov.  11:37 3 -> /proc/49784/fd

après décantation je vois mieux ce que tu veux dire et je comprends que mon interprétation uniquement en terme de fichier temporaire ne convient pas :

  • si <(cmd) est remplacé directement par le contenu d’ un fichier alors echo <(echo foo) ne devrait rien donner ( ce que je pensais au début ) et donc que /dev/fd/63 ou 62 ne sont que des étiquettes dont l’intitulé constitute l’argument de la commande echo qui l’affiche .
  • si echo <(echo foo) affiche du texte c’est que ce texte est l’argument de la commande et donc que , comme tu le proposes , la substitution de processus le fait apparaître sous la forme affichée par la commande echo .

Ça me paraît logique mais il est fort possible que je sois à côté de la plaque une fois encore !

En tout cas ça expliquerait , me semble-t-il ceci d’où j’ai tiré mon dernier exemple :

<(...) by itself is a process substitution. It behaves like a file name, except the « contents » of the file it names are the output of the command. For example,

$ echo <(echo foo)
/dev/fd/63
$ cat <(echo foo; echo bar)
foo
bar

Par ailleurs, cat <(echo foo) <(echo bar) donne la même chose.

je vais mettre le texte qui m’a fait penser , à tort , que l’effet de la substitution de processus était d’abord la création d’un fichier temporaire ( cours IUT-Rodez p79) . À noter que ce fichier shell comporte une erreur voulue par l’auteur , à des fins didactiques , et que j’ai laissée telle quelle

Le plus simple reste quand même de faire ça :

test "x$(ls)" = "x" && echo 0 || echo 1

par curiosité j’ai fabriqué un lien symbolique dans /home/mm/ vers un fichier de Documents/ puis j’ai utilisé ( wc -l lien ) et effectivement cette commande a affiché les infos obtenues à partir du fichier mais avec le nom donné au lien symbolique ( renommé pour éviter une confusion ): comme avec le descripteur de fichiers ci-dessus en somme .

tout ça ne me servira jamais mais au moins ça satisfait la curiosité .

le canard m’a indiqué une piste à partir d’un renseignement donné par man wc = Aide en ligne de GNU coreutils , et elle conduit effectivement à l’information que je charchais :

mm@Xfce:~$ info '(coreutils) wc invocation'
........
‘wc’ prints one line of counts for each file, and if the file was
given as an argument, it prints the file name following the counts.  If
more than one FILE is given, ‘wc’ prints a final line containing the
cumulative counts, with the file name ‘total.

ce qui est quand même plus complet que ce que fournit man wc .

ps : en fait je m’aperçois que info wc marche aussi bien .

@zao: suite au sujet Améliorer la présentation de la sortie de “find” qui est parti en troll total avec des interventions qui n’ont rien à voir avec le sujet, je vois que tu piétines depuis 10 jours sur une question de redirection de la fonction suivante:

while read L ;do basename $L;((i++));done < <(find -maxdepth 1 -type f)

Je vois aussi dans tes messages que tu mélanges différents problèmes en t’appuyant sur une documentation qui est en fait une traduction/interprétation de deux documentations de référence qui sont celle de bash concernant les redirections, et celle de coreutils concernant wc.

wc est une commande microscopique, secondaire, livrée par coreutils et qui ne mérite pas une semaine d’interrogation.

On va aller progressivement:

:black_small_square: Comment compter le nombre de lignes d’un fichier sans wc ?

Création d’un fichier de 5 lignes pour test :
seq 1 5 > fichier5

x=0;while read L;do ((x++));done <fichier5;echo $x
awk 'END {print NR}' fichier5
sed -n '$=' fichier5
5

:black_small_square: Comment compter le nombre de lignes d’un fichier avec wc ?

wc -l fichier5
5 fichier5

wc -l fichier5 |awk '{print $1}'
wc -l fichier5 |cut -d\ -f1
5

Comme tu le vois, wc nécessite un petit traitement supplémentaire pour extraire le nombre de lignes, sans le nom du fichier.
Jusque là, est-ce que tout est clair ou pas ?
Si non, pas la peine d’aller plus loin avec des redirections.

je te l’accorde et c’est bien la 1ère fois que je vois ça sur ce forum . Mais bon , j’ai vu bien pire ailleurs .

Quelques précisions :

  • comme tout est nouveau , maintenant un peu moins quand même , au bout d’un moment ça sature et c’est un peu la pagaille dans ce que je peux écrire
  • cette fois , grâce en particulier aux messages de @Almtesh ajoutés à 2 messages pris sur des sites US ça s’est bien éclairci et je pense avoir enfin compris comment fonctionne une substituion de processus avec ou sans redirection , au moins en gros et ça me suffit . Donc affaire classée . Jusqu’à une prochaine fois .
  • quant à la commande wc elle était là juste pour essayer d’y voir plus clair dans le fonctionnement d’une substitution de processus , sans plus . Ça m’a donné l’occasion d’en apprendre un peu plus et c’est tant mieux . Du coup j’ai vu que info peut être très utile comme complément de man
  • mon but n’est pas de tout apprendre sur bash mais simplement d’essayer de savoir m’en servir le plus efficacement possible dans l’utilisation que j’en fais , et donc d’avoir un minimum de compréhension de son fonctionnement et de ce qu’il offre comme possibilités . En particulier cette notion de « fonction » que tu as utilisée est très intéressante et restera au menu .

Ok. Donc si le comptage de lignes d’un fichier ET d’un retour de commande sont tous les deux clairs, tout va bien.
Pour compter le nombre de fichiers de la commande ls, en fait ‹ ls |wc -l › te suffit largement:

ls |wc -l
wc -l <<<$(ls)
wc -l < <(ls)
x=0;while read L;do ((x++));done< <(ls);echo $x
ls >/dev/shm/x;wc -l </dev/shm/x

Ce qui t’a embrouillé, c’est de savoir où bash cherche à stocker le buffer/tampon de la sortie ‹ /dev/fd/6x ›, ce qui n’a en fait strictement aucune importance, et qui est de la cuisine interne bash qui peut varier d’une version à l’autre.

et je suis un âne , avec mention en plus . Le même auteur décrit très bien ce qu’il se passe ; mais en page 57 avec le titre substitution de processus en plus des fois qu’on le manque . Et l’exemple pris est justement la commande

$ wc -l <(ls -l)
4 /dev/fd/63

presque comme la mienne , et d’expliquer :

La substitution de processus généralise la notion de tube. La principale forme de substitution
de processus est la suivante : cmd <(suite_cmds)
Le shell crée un fichier temporaire, connecte la sortie de suite_cmds à ce fichier temporaire puis
lance l’exécution de cette suite de commandes. Cette suite de commandes écrit ses résultats
dans le fichier temporaire. Ensuite, le shell remplace la syntaxe <(suite_cmds) par la référence
à ce fichier temporaire et lance l’exécution de la commande cmd.

Dans l’exemple ci-dessus, la sortie de la commande ls –l a été enregistrée dans un fichier
temporaire /dev/fd/63. Puis, la commande wc –l /dev/fd/63 a été exécutée.

malgré tout , de ne pas l’avoir vu m’a permis d’apprendre que ce /dev/fd/63 n’est pas un fichier mais un descripteur de fichier ( cf @Almtesh ) , mais comme il s’adresse à des étudiants d’IUT ils l’auront compris , eux , qu’il s’agissait d’un identifiant qui renvoie à un fichier : "… par la référence à ce fichier temporaire … " .

L’apprentissage a été long et laborieux alors qu’il aurait dû être bien plus rapide si j’avais pris les choses dans l’ordre .

Ce qui est bien, c’est que parmi les quelques mille commandes, tu vas pouvoir trouver d’autres bizarreries à investiguer.

ls /usr/bin |wc -l
1395

Après il y a des bizarreries qui ont un intérêt à creuser, d’autres absolument pas: tu n’as pas eu de chance, comme le précise la documentation bash de référence qui indique que le choix du buffer de redirection est fonction de l’environnement, et totalement transparent pour l’utilisateur

Bash handles several filenames specially when they are used in redirections, as described in the following table.
If the operating system on which Bash is running provides these special files, bash will use them; otherwise it will emulate them internally with the behavior described below.
/dev/fd/fd
/dev/stdin
/dev/stdout
/dev/stderr
/dev/tcp/host/port
/dev/udp/host/port

Tu trouveras des bizarreries plus intéressantes à creuser, par exemple dans la documentation de sed et ses quelques 5000 lignes de 80 caractères de doc.

info sed |wc -l
5117

Bon courage.

Comme je n’ai pas l’intention de passer en revue toutes les commandes disponibles ça ne devrait pas me surcharger . Si j’ai essayé de comprendre la substitution de commande avec ou sans redirection c’est bien parce que je voulias décrypter la fonction que tu avais utilisée pour résoudre mon problème , pratique celui-là , de comparaison visuelle de listes . Ça n’était pas purement théorique .

À ce propos :

mm@Xfce:~$ wc -l <(info sed) <(man sed) <(info wc) <(man wc)
   5117 /dev/fd/63
    283 /dev/fd/62
     83 /dev/fd/61
     79 /dev/fd/60

je vois que info , au moins pour ces commandes , est plus bavard que man . Le cas de sed est vraiment parlant . Heureusement cette commande n’est pas , encore ? , dans mon champ « d’investigation » comme tu dis . Mais peut-être qu’avec sa fonction filtre