Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

lignes de A qui ne sont pas dans B

14 réponses
Avatar
mpg
Bonjour,

Soit deux fichiers A et B contenant chacun une liste de mots, un par ligne.
Je souhaite connaître tous les mots de A qui ne sont pas dans B. Pour le
moment, je fais :

sort -u A > A.trie
sort -u B > B.trie
diff A.trie B.trie | grep '^<'

Y a-t'il moyen de faire ça sans passer par les fichiers intermédiaires
A.trie et B.trie ? Pour l'instant, je ne vois pas trop comment utiliser
diff au milieu d'un pipe, ni comment faire ça sans passer par diff.

Ce n'est franchement pas très important, passer par les fichiers
intermédiaires ne me dérange pas en pratique, c'est plutôt pour apprendre.

Merci d'avance.

Manuel.

4 réponses

1 2
Avatar
mpg
Le (on) samedi 24 novembre 2007 14:25, Stephane Chazelas a écrit (wrote) :

comm -23 <(sort -u A) <(sort -u B)

Sympa, je ne connaissais pas comm. Mais du coup je n'ai plus besoin de faire

un sort -u avant, non ? (Je peux toujours en faire un après si je veux la
sortie triée :
comm -23 A B | sort -u
mais ce n'est plus un préliminaire indispensable à la comparaison, on
dirait.)

<(...) appelé "process substitution" est une fonctionalité
non-standard de zsh, bash et certains ksh.

Vu qu'en interactif, j'utilise uniquement zsh et bash, ça me va. Pour ls

scripts, je peux faire un effort.

sort -u A | { exec 3<&0; sort -u B | comm -23 /dev/fd/3 -; }

Hum, ça atteint mes limites là. Alors si je comprends un peu, /dev/fd/N sont

les fameux "file descriptor" dont les plus connus sont la sortie standard
(1) et la sortie d'erreur (2). Visiblement 3<&0 redirige le flux 0 (sortie
standard ?) sur le fd3. Donc sur /dev/fd3, comm va lire la sortie du sort
B, sur -, à savoir l'entrée standard.

Là où je rame complètement, c'est sur le rôle du exec, le groupement des
différents processus et son influence sur les entrées et sorties standard
de chacun. Je peux demander une petit explication ?

Sinon, j'imagine bien sûr que l'avantage de cette syntaxe, c'est qu'elle est
standard et protable ?

Manuel.

Avatar
Stephane Chazelas
On Sat, 24 Nov 2007 19:50:47 +0100, mpg wrote:
Le (on) samedi 24 novembre 2007 14:25, Stephane Chazelas a écrit (wrote) :

comm -23 <(sort -u A) <(sort -u B)

Sympa, je ne connaissais pas comm. Mais du coup je n'ai plus besoin de faire

un sort -u avant, non ? (Je peux toujours en faire un après si je veux la
sortie triée :
comm -23 A B | sort -u
mais ce n'est plus un préliminaire indispensable à la comparaison, on
dirait.)


Si, comm fonctionne lineairement, il attend des entrees triees,
c'est pour ca qu'il peut lire des pipes ou on ne peut pas
revenir en arriere.

Par comparaison, grep -xvFf B A va stocker le contenu de B en
memoire, et pour chaque ligne de A va comparer avec chaque ligne
de B, ce qui tres non-optimal nA x nB comparaisons.

comm fait autant de comparaisons que le nombre de ligne de A ou
B (suivant lequel a le plus de lignes) et sort fait en moyenne
dans l'O(n x log(n)) comparaisons.

[...]
Vu qu'en interactif, j'utilise uniquement zsh et bash, ça me va. Pour ls
scripts, je peux faire un effort.

sort -u A | { exec 3<&0; sort -u B | comm -23 /dev/fd/3 -; }

Hum, ça atteint mes limites là. Alors si je comprends un peu, /dev/fd/N sont

les fameux "file descriptor" dont les plus connus sont la sortie standard
(1) et la sortie d'erreur (2). Visiblement 3<&0 redirige le flux 0 (sortie
standard ?) sur le fd3. Donc sur /dev/fd3, comm va lire la sortie du sort
B, sur -, à savoir l'entrée standard.

Là où je rame complètement, c'est sur le rôle du exec, le groupement des
différents processus et son influence sur les entrées et sorties standard
de chacun. Je peux demander une petit explication ?

Sinon, j'imagine bien sûr que l'avantage de cette syntaxe, c'est qu'elle est
standard et protable ?
[...]


Well, /dev/fd/3 est une feature non-standard de certains
systemes mais c'est independant du shell (encore que certains
comme bash ou certains ksh peuvent faire des trucs nasty dans le
dos quand on fait > /dev/fd/<n>).

/dev/fd/<n> sont des fichiers speciaux dont la specialité est
que faire un open("/dev/fd/3") revient en gros a faire un
dup(3). C'est assez vrai sous Solaris, mais sous Linux,
/dev/fd/3 est un fait un lien symbolique vers /proc/self/fd/3
(enfin, /dev/fd est un lien symbolique vers /proc/self/fd), et
/proc/self/fd/3 est un lien symbolique vers la resource ouverte
par le fd 3, donc pour les fichiers reguliers, faire un
open("/dev/fd/3") ouvre le fichier une deuxieme fois qui n'a
rien a voir avec faire un dup(3).

Mais bon, dans notre exemple, vu qu'on parle de pipes, ca
marchera pareil.

Note que les <(...) utilisent /dev/fd/<n> par en dessous (sur
les systemes qui le supportent, sur les autres, certains shells
peuvent utiliser des named pipes a la place).

Pour t'en rendre compte, tu peux faire:

echo <(ls)

Tu verras quelque chose comme /dev/fd/12, ce qui veut dire que
le shell a cree un pipe et lancé ls dans un nouveau processus
avec sa sortie standard redirigée vers ce pipe. L'autre bout du
pipe est accessible via le fd 12. "/dev/fd/12" est donc passé en
argument a echo. echo se content d'afficher ca, mais s'il
l'avait ouvert, le fd qu'il aurait obtenu (13, ou 14, whatever)
aurait ete un duplicate du fd 12 et lui aurait donc permis de
lire l'output de ls.

Cette fonctionnalité est utile pour les commandes qui ne
prennent leur input que sous forme de nom de fichier, ou/et qui
doivent prendre plus d'un seul input (vu qu'il n'y a qu'un
stdin).

Dans

sort -u A | { exec 3<&0; sort -u B | comm -23 /dev/fd/3 -; }

On lance deux commandes C1 et C2 en meme temps:

sort -u A et un sous-shell (ou ca peut etre le shell courant) qui execute le
"exec 3<&0; sort -u B | comm -23 /dev/fd/3 -"

exec 3<&0 fait un dupplicate du fd 0 en fd 3. Vu que fd 0 est la
sortie du pipe, ca veut dire qu'une commande de C2 qui lit
depuis le fd 3 lira l'output de C1.

Dans C1 on lance alors deux commandes en meme temps, C21 et C22.
Pour "comm" (C22), "-" veut dire lire stdin (le fd 0) et
"/dev/fd/3" est un nom de fichier ordinaire. Mais quand comm va
ouvrir "/dev/fd/3", il va en fait obtenir un dupplicate du fd 3,
d'ou il pourra donc lire l'output de C1.

--
Stephane


Avatar
Nicolas George
Stephane Chazelas wrote in message
<4749480b$0$5102$:
mais sous Linux,
/dev/fd/3 est un fait un lien symbolique vers /proc/self/fd/3
(enfin, /dev/fd est un lien symbolique vers /proc/self/fd), et
/proc/self/fd/3 est un lien symbolique vers la resource ouverte
par le fd 3, donc pour les fichiers reguliers, faire un
open("/dev/fd/3") ouvre le fichier une deuxieme fois qui n'a
rien a voir avec faire un dup(3).


Ce n'est pas tout à fait vrai. Par exemple :

ssecem /tmp $ mkdir jail
ssecem /tmp $ cd jail
ssecem /tmp/jail $ exec 3> foo
ssecem /tmp/jail $ echo first line >&3
ssecem /tmp/jail $ chmod 000 /tmp/jail
ssecem /tmp/jail $ exec 4>> `readlink /proc/$$/fd/3`
zsh: permission denied: /tmp/jail/foo
ssecem /tmp/jail $ exec 4> /dev/fd/3
zsh: file exists: /dev/fd/3
ssecem /tmp/jail $ exec 4>> /dev/fd/3
ssecem /tmp/jail $ echo second line >&4
ssecem /tmp/jail $ chmod 755 /tmp/jail
ssecem /tmp/jail $ cat foo
first line
second line

Le fichier dans /proc/$$/fd apparaît comme un lien symbolique pour un lstat,
et un readlink montre le nom réel du fichier. Mais pour toutes les
opérations qui travaillent sur le fichier, l'entrée dans /proc/$$/fd se
comporte comme le fichier lui-même, et pas comme un lien symbolique. En
particulier, les permissions d'accès sur les répertoires parents ne sont pas
testées.

De même :

ssecem /tmp/jail $ echo first line > foo
ssecem /tmp/jail $ exec 3< foo
ssecem /tmp/jail $ exec 4>> foo
ssecem /tmp/jail $ rm foo
ssecem /tmp/jail $ exec 5>> "`readlink /proc/$$/fd/4`"
ssecem /tmp/jail $ exec 6>> /proc/$$/fd/4
ssecem /tmp/jail $ print second line readlink >&5
ssecem /tmp/jail $ print second line direct >&6
ssecem /tmp/jail $ ls
foo (deleted)
ssecem /tmp/jail $ cat 'foo (deleted)'
second line readlink
ssecem /tmp/jail $ cat <&3
first line
second line direct

L'entrée dans /proc/$$/fd permet de ré-ouvrir un fichier qui a été supprimé.

En revanche, tu as raison de dire que ce n'est pas un dup, en particulier
pour ceci :

ssecem /tmp/jail $ echo first line > foo
ssecem /tmp/jail $ echo second line >> foo
ssecem /tmp/jail $ exec 3< foo
ssecem /tmp/jail $ exec 4< /proc/$$/fd/3
ssecem /tmp/jail $ cat <&3
first line
second line
ssecem /tmp/jail $ cat <&3
ssecem /tmp/jail $ cat <&4
first line
second line

Les fd ne partagent pas le même pointeur de fichier. Une autre manifestation
de se fait est qu'il est impossible de ré-ouvrir une socket par ce moyen, ce
qui est parfois pénible.

Avatar
Stephane Chazelas
On 25 Nov 2007 11:21:47 GMT, Nicolas George wrote:
Stephane Chazelas wrote in message
<4749480b$0$5102$:
mais sous Linux,
/dev/fd/3 est un fait un lien symbolique vers /proc/self/fd/3
(enfin, /dev/fd est un lien symbolique vers /proc/self/fd), et
/proc/self/fd/3 est un lien symbolique vers la resource ouverte
par le fd 3, donc pour les fichiers reguliers, faire un
open("/dev/fd/3") ouvre le fichier une deuxieme fois qui n'a
rien a voir avec faire un dup(3).


Ce n'est pas tout à fait vrai. Par exemple :

ssecem /tmp $ mkdir jail
ssecem /tmp $ cd jail
ssecem /tmp/jail $ exec 3> foo
ssecem /tmp/jail $ echo first line >&3
ssecem /tmp/jail $ chmod 000 /tmp/jail
ssecem /tmp/jail $ exec 4>> `readlink /proc/$$/fd/3`
zsh: permission denied: /tmp/jail/foo
ssecem /tmp/jail $ exec 4> /dev/fd/3
zsh: file exists: /dev/fd/3
ssecem /tmp/jail $ exec 4>> /dev/fd/3


Ah oui, tiens, merci, j'ignorais. Ca me semble assez douteux
comme truc comme dans:

~/tmp/jail$ exec 3> a
~/tmp/jail$ echo a > a
~/tmp/jail$ chmod 0 .
~/tmp/jail$ cat /dev/fd/3
a

De quel droit le processus qui execute "cat" peut il lire le
contenu de "a"?

Le fichier a ete ouvert *en ecriture* avant que "jail" n'ait ete
rendu inaccessible. A mon avis, ca ne devrait pas permettre
quelqu'un de *lire* le fichier.

--
Stephane


1 2