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

[bash] traitement entree standard

8 réponses
Avatar
Christophe PEREZ
Bonjour,

Je sens que je vais avoir du mal à expliquer mon besoin, déjà pour le
titre... :-)

Je veux faire un filtre (en bash) pour mes mails sortants.
Je ne sais pas s'il y a mieux, mais j'ai trouvé que ça marche en
remplaçant le /etc/alternative/mta par un lien vers mon script et en
intégrant dans celui-ci quelque chose du genre :
#!/bin/bash
formail -a "mon_nouveau_header" -s /usr/sbin/sendmail.postfix "$@"

Or, je voudrais traiter des cas préalables dans ce script, et du coup, si
je met un traitement avant, l'entrée standard ayant déjà été "lue", elle
n'est plus disponible pour le formail.

J'ai contourné le problème en fait un :
while IFS= read -r LIGNE ; do
MSG="$MSG$LIGNE
"
done
Puis, c'est la variable $MSG que j'exploite, cela fonctionne, mais je me
demande si je ne passe pas encore à côté de quelque chose.
Aussi, j'aimerais bien votre avis.


Autre chose, juste en passant, y a t'il une différence entre les 2 lignes
:
$ cat fichier | commande
$ commande < fichier
?

Je vois parfois l'une, parfois l'autre, j'utilise les 2 un peu au gré du
vent, mais je ne sais pas s'il y a une nuance.

Merci d'avance.

--
Christophe PEREZ

8 réponses

Avatar
Stephane CHAZELAS
Le Tue, 08 Jul 2003 14:45:16 -0400, Christophe PEREZ écrivait :
[...
while IFS= read -r LIGNE ; do
MSG="$MSG$LIGNE
"
done
Puis, c'est la variable $MSG que j'exploite, cela fonctionne, mais je me
demande si je ne passe pas encore à côté de quelque chose.
Aussi, j'aimerais bien votre avis.


MSG=$(cat)
tout simplement.

Note que les sauts de lignes terminaux sont supprimés. Si ça te
gène, tu peux faire:

MSG=$(cat; echo .); MSG=${MSG%?}

À envoyer par la suite par « printf %s "$MSG" | sendmail... »


Autre chose, juste en passant, y a t'il une différence entre les 2 lignes
:
$ cat fichier | commande
$ commande < fichier
?


D'un point de vue purement fonctionnel,
"cat un-seul-fichier |" est une no-op (au lieu de presenter
directement le fichier à l'application qui est derrière, on le
fait passer sans aucune modification par une boucle
while(read(0,...)){write(1,..)} et un pipe dont on présente la
sortie à l'application derrière).

On appelle ça généralement un UUOC (useless use of cat), cat
étant la commande pour con/cat/éner. Toutefois, ça peut dans
certains cas s'avérer util.

une application peut se comporter différemment si son entrée
standard est un pipe plutot qu'un fichier ordinaire (déjà, elle
ne peut plus faire de lseek ou de fchmod). Ensuite, on a le cas
des gros fichiers. Si l'application (ou le shell) n'est pas
largefile aware mais que cat l'est, ça passera avec "cat |".

Note que dans

cat fichier | {
...
}

Ce qui est entre {...} a de grandes chances d'etre executé dans
un sous-shell, avec

{
...
} < fichier

ça ne sera pas exécuté dans un sous-shell (sauf avec le Bourne
shell).

Pour résumer, il n'est pratiquement jamais nécessaire de faire
un:

cat fichier | ...

sauf dans des cas très particuliers.

Note qu'on peut faire

< fichier cmd args

(les opérateurs de redirections peuvent apparaitre n'importe où
au milieux des arguments)

(pour ceux qui disent qu'ils prefèrent "cat fichier|" parce que
justement le nom du fichier apparait au debut du pipeline, ce
qui est plus satisfaisant puisque c'est la source).

--
Stéphane

Avatar
Christophe PEREZ
Le Tue, 08 Jul 2003 20:05:15 +0000, Stephane CHAZELAS a écrit:

MSG=$(cat)
tout simplement.


Effectivement.

Note que les sauts de lignes terminaux sont supprimés. Si ça te
gène, tu peux faire:


Plutôt oui, c'est un mail ;-)

MSG=$(cat; echo .); MSG=${MSG%?}


Noté, vais étudier ;-)

À envoyer par la suite par « printf %s "$MSG" | sendmail... »


Bien sûr.

[...]
ça ne sera pas exécuté dans un sous-shell (sauf avec le Bourne
shell).


Donc, dans ce cas, avec bash, c'est idem, c'est ça ?

Pour résumer, il n'est pratiquement jamais nécessaire de faire
un:

cat fichier | ...


Noté !

sauf dans des cas très particuliers.

Note qu'on peut faire

< fichier cmd args


Ah ben dis donc ! Là j'en reste sur le c.. !

(les opérateurs de redirections peuvent apparaitre n'importe où
au milieux des arguments)


Ça ne s'invente pas ça, mais ça doit s'apprendre dans les docs, je sais
:-))


(pour ceux qui disent qu'ils prefèrent "cat fichier|" parce que
justement le nom du fichier apparait au debut du pipeline, ce
qui est plus satisfaisant puisque c'est la source).


Ce qui se comprend, je trouve.

Merci pour toutes ces explications, qui, j'en suis convaincu, n'auront pas
servi qu'à moi ;-)

--
Christophe PEREZ

Avatar
Stephane CHAZELAS
Le Wed, 09 Jul 2003 23:37:51 -0400, Christophe PEREZ écrivait :
Le Tue, 08 Jul 2003 16:21:32 -0400, Christophe PEREZ a écrit:

MSG=$(cat; echo .); MSG=${MSG%?}


Noté, vais étudier ;-)


Et s'il y a un cas de figure où il n'y a aucune entrée de passée (comme
pour sendmail), comment puis-je le tester ?


Avec [ -t 0 ], tu testes si l'entrée standard est un terminal. En
général, il y a toujours quelquechose de passé (il est très rare que le
fd 0 soit fermé), la distinction, c'est qu'on n'a pas toujours envie de
la lire (comme quand l'entrée standard est un terminal).

--
Stéphane



Avatar
Jean-Marc Bourguet
Christophe PEREZ writes:

Le Thu, 10 Jul 2003 07:54:48 +0000, Stephane CHAZELAS a écrit:

Avec [ -t 0 ], tu testes si l'entrée standard est un terminal. En
général, il y a toujours quelquechose de passé (il est très rare que le
fd 0 soit fermé), la distinction, c'est qu'on n'a pas toujours envie de
la lire (comme quand l'entrée standard est un terminal).


Je n'ai pas du me faire comprendre (une fois de plus), mais ça ne
fonctionne pas.

11:32:03 chris ~ $ cat script
#!/bin/bash
echo "avant"
MSG=$(cat; echo .)
echo "milieu"
MSG=${MSG%?}
echo "apres"
11:31:56 chris ~ $ echo | ./script
avant
milieu
apres
[ fonctionnement correct, avec une entrée ]

11:31:50 chris ~ $ ./script
avant
[ blocage, interrompu par CTRL-C ]


CTRL-D pour marquer la fin de fichier dans un terminal (ou plus
exactement pour indiquer qu'il faut une lecture maintenant, une
lecture de taille nulle etant prise pour une fin de fichier, un CTRL-D
en debut de ligne ou deux CTRL-D consecutifs).

A+

--
Jean-Marc
Site de usenet-fr: http://www.usenet-fr.news.eu.org


Avatar
Christophe PEREZ
Le Thu, 10 Jul 2003 17:58:55 +0200, Jean-Marc Bourguet a écrit:

CTRL-D pour marquer la fin de fichier dans un terminal (ou plus
exactement pour indiquer qu'il faut une lecture maintenant, une
lecture de taille nulle etant prise pour une fin de fichier, un CTRL-D
en debut de ligne ou deux CTRL-D consecutifs).


Ah ok, mais ce n'est pas l'objectif.
Je veux ça dans le script, donc dans :
MSG=$(cat; echo .)

--
Christophe PEREZ

Avatar
Christophe PEREZ
Le Thu, 10 Jul 2003 16:00:59 +0000, Stephane CHAZELAS a écrit:

Que voudrais-tu que ça fasse à la place ?


Je me réexplique :

Je veux, qu'à la façon sendmail (puisque c'est pour s'y substituer), je
puisse faire :
cat msg | script
ou
script -q

Donc, il n'y a pas nécessairement une entrée.


--
Christophe PEREZ

Avatar
Stephane CHAZELAS
Le Thu, 10 Jul 2003 12:10:41 -0400, Christophe PEREZ écrivait :
CTRL-D pour marquer la fin de fichier dans un terminal (ou plus
exactement pour indiquer qu'il faut une lecture maintenant, une
lecture de taille nulle etant prise pour une fin de fichier, un CTRL-D
en debut de ligne ou deux CTRL-D consecutifs).


Ah ok, mais ce n'est pas l'objectif.
Je veux ça dans le script, donc dans :
MSG=$(cat; echo .)


Tu veux quoi ? Que MSG soit vide quand l'entree standard est un
terminal ?

Alors:

if [ -t 0 ]; then
MSG else
MSG=$(cat; echo .); MSG=${MSG%.}
fi

--
Stéphane


Avatar
Christophe PEREZ
Le Thu, 10 Jul 2003 17:00:19 +0000, Stephane CHAZELAS a écrit:

Tu veux quoi ? Que MSG soit vide quand l'entree standard est un
terminal ?


Voilà !

if [ -t 0 ]; then
MSG > else
MSG=$(cat; echo .); MSG=${MSG%.}
fi


Impec !
Merci beaucoup.

--
Christophe PEREZ