Forum Programmation.shell Redirection de scripts

Posté par (page perso) .
Tags : aucun
0
3
août
2004
Bonjour,

petite question sur la redirection de script :
j'aimerai savoir (la cible c'est en bash, mais si en vous connaissez qui marche sous d'autres shells, pourquoi pas ?) comment faire une commande en début de mes scripts qui :
- envoie la sortie standard vers std_out.log
- envoie la sortie erreur (et toutes les autres, au cas ou) vers std_err.log
- si le terminal est interactif, affiche aussi à l'écran les deux sorties, sinon, n'affiche rien. (dans le cas des taches cron, par exemple)

les noms des fichiers sont juste pour l'exemple, bien sur. En fait, ils seraient remis à zero au debut de chaque script (avec un avertissement) ou nommés avec une date, bref, peu importe.

bon, pour l'instant je me bats avec exec, qui donne des résultats mitigés. Par exemple,

if ["${PS1}" ] ; then
exec 2> err.log > out.log
else
???
fi


fait ce qu'il faut. Mais reste le problème de la redirection en même temps vers le terminal et deux fichiers. J'ai essayé des douzaines de combinaisons, d'abord en lisant le manuel, puis un peu au hasard, (sait-on jamais :) mais je n'ai pas trouvé la solution...

Si quelqu'un a une idée...

Par contre, je signale un bon guide, Advanced Bash-Scripting Guide, sur TLDP,
http://www.tldp.org/LDP/abs/html/(...)

PS: j'ai beau visualiser, le caractère de redirection ne s'affiche pas très bien. pour info le " & gt ; " signifie superieur, pour ceusses qu'auraient du mal :)
  • # Take a cup of tee

    Posté par (page perso) . Évalué à 2.

    Bonjour,

    Le programme que tu recherches s'appelle tee. Pour rediriger la sortie d'un programme tout en l'affichant :

    echo toto | tee /tmp/log

    Par contre pour rediriger la sortie d'erreur vers autre chose ... j'ai pas trop d'idée.

    Bonne chance !
    • [^] # Re: Take a cup of tee

      Posté par . Évalué à 1.

      Ci dessous un exemple en ksh sous aix (mais qui doit pouvoir s'adapter sous linux)
      Il faut juste verifier que la commande who -m te retourne ceci (le deuxieme champ est interessant):

      totof2000 pts/4 02 ao{ 15:24

      #!/bin/ksh

      # Identification du pseudo-terminal de sortie
      SORTIE=/dev/$( who -m |awk '{ print $2}')
      echo $SORTIE
      # Commande:
      (ls - toto | tee titi.log>$SORTIE)2>&1 | tee error.log


      Tu mets tout ca dans un script test1.ksh (ou test1.sh, peu importe).

      A l'exécution:

      $ksh test1.ksh
      /dev/pts/4
      ls : 0653-341 Le fichier - n'existe pas.
      ls : 0653-341 Le fichier toto n'existe pas.
      $more titi.log
      $more error.log
      ls : 0653-341 Le fichier - n'existe pas.
      ls : 0653-341 Le fichier toto n'existe pas.
      $
      $touch toto
      $ksh test1.ksh
      /dev/pts/4
      toto
      ls : 0653-341 Le fichier - n'existe pas.
      $more titi.log
      toto
      $more error.log
      ls : 0653-341 Le fichier - n'existe pas.

      Normalement, en cron, le pseudo-terminal devrait être /dev/null (a verifier).
    • [^] # Re: Take a cup of tee

      Posté par (page perso) . Évalué à 1.

      Je connaissais le tee (bien pratique) mais par contre, je ne suis pas arrivé à le faire marcher avec exec.
      Justement, je veux éviter d'avoir à lancer les commandes shell comme ça :
      ./script.sh 2>&1 | tee script.log
      Mais ça me donne une idée ...
      En fait il faudrait dupliquer les file descriptors out (1) et err(2) comme le ferait un tee, afficher le 1 et le 2 à l'écran, et diriger les duplicats vers deux fichiers...

      je vais finir par trouver :) je sens que je suis pas loin !
      • [^] # Re: Take a cup of tee

        Posté par . Évalué à 1.

        Ma solution ne marche pas en arriere plan :(
        • [^] # Re: Take a cup of tee

          Posté par . Évalué à 1.

          Pour que ma solution marche en arriere plan, il faut tester le code retour de la commande who -m.
          Si elle échoue, SORTIE=/dev/null
          • [^] # Re: Take a cup of tee

            Posté par . Évalué à 1.

            Voici une variante qui pourrait marcher comme tu le demande.

            J'ai pas de linux sous la main, peux-tu me dire si who -m fonctionne comme sous aix ?



            #!/bin/ksh

            # Identification du pseudo-terminal de sortie

            SORTIE=/dev/$((who -m 2>/dev/null || echo "toto null") |awk '{ print $2}')


            echo $SORTIE

            myexec() {

            # Commande:
            ($* | tee titi.log>$SORTIE)2>&1 | tee error.log >$SORTIE

            }

            myexec ls - toto
            • [^] # Re: Take a cup of tee

              Posté par . Évalué à 1.

              Le "echo $SORTIE" peut être supprimé, il me servait uniquement à tester que la variable $SORTIE valait bien /dev/null lors de l'exécution en arriere plan par cron.
  • # est-ce que perl est un shell ?

    Posté par (page perso) . Évalué à 2.

    je laisse la question fameuse "est-ce que perl est un shell ?" de côté et je t'indique comment faire en perl :

    open STDOUT, "|tee stdout";
    open STDERR, "|tee stderr >&2";

    et puis même que si tu es vraiment masochiste et que tu veux quand même utiliser bash ça te donnera une idée poir réussir à le faire en bash.
  • # Une solution en Shell

    Posté par . Évalué à 1.

    A mon avis, la seule solution pour le faire en shell est d'utiliser mkfifo :
    $> mkfifo mon_pipe
    $> ma_commande 2>mon_pipe | tee out.log &
    $> cat mon_pipe | tee err.log
    Sans oublier de faire :
    $> rm mon_pipe
    à la fin de la manipulation.
  • # pas évident

    Posté par . Évalué à 2.

    Je me suis déjà +/- battu avec ce problème, et je n'ai pas de solution complètement satisfaisante. Je te mets ici une version verbosement commentée de ma solution la plus simple :
    http://tdegreni.free.fr/files/foobar.sh(...)

    Le problème qui persiste est que stdout et stderr ne sont pas synchronisés, et que donc des messages rapidement enchainés sur chacune de ses sorties peuvent se retrouver entrelacés sur le terminal. En fait, je ne vois pas vraiment de solution à ce problème : la duplication des deux flux passe forcement par des processus concurrents (les deux 'tee'), qui sont bufferisés, et on n'a pas le contrôle de leur ordonnancement. Peux être qu'en C on pourrait faire un seul processus pour consommer et dupliquer les deux flux alternativement (et vu que côté producteur l'écriture sur des pipes est bloquante, on ne perdrait pas la synchro), bref on pourrait faire un genre de double-tee, mais en shell par contre je ne vois vraiment pas.

    À part ça, tu verras que dans mon script je n'ai pas distingué shell interactif de shell non-interactif. Il me semble d'ailleurs que tout ce qui est mis dans un script est non-interactif, sauf à forcer ça avec un "#!/bin/bash -i". Je ne crois pas que ton test sur $PS1 fonctionne comme tu l'attends, parceque $PS1 n'est pas censé être exporter. Moi en général, quand je veux appeler qqch depuis un cron sans pourrir mon log, je ne compte pas sur le script lui même, mais je fais mon appel avec "&>/dev/null", et puis voilà.
    Bon ceci dit, si tu tiens à tester ça au niveau du script, alors y'a peut être moyen plutôt avec $TERM, ou encore avec la commande 'tty', je sais pas trop... Je sais qu'en python par exemple, je ferais ça avec le prédicat "sys.stdout.isatty()", mais en bash, j'ai jamais essayer de trouver un équivalent. Si tu trouves, alors dans mon script ce sera facile de rajouter le test pour faire un appel récursif avec une redirection plus simple dans le cas non-interactif (genre '$0 "${@}" 1>${STDOUT_LOG} 2>${STDERR_LOG}').
    • [^] # Re: pas évident

      Posté par . Évalué à 2.

      Walah, j'avais pas vu qu'il y avait autant de nouveau commentaires pertinents depuis que j'avais lu ton message. Bon, mon truc semble tout de suite un peu redondant...
    • [^] # Re: pas évident

      Posté par . Évalué à 2.

      Ou encore +/- le même en explicitant la création des fifo et des processus d'arrière plan au lieu d'utiliser des pipes, comme proposé plus haut (plutôt plus lourd amha) :
      http://tdegreni.free.fr/files/foobar2.sh(...)
  • # QQ chose très crade comme ça ?

    Posté par (page perso) . Évalué à 1.

    echo -n > out.log
    echo -n > err.log
    tail -f out.log &
    tail -f err.log &
    ls > out.log >& err.log && sleep 1
    killall tail

    C'est très crade...
    Remplacer killall tail pas qq chose de moins... destructif serait une bonne idée, mais j'ai pas trop le temps, je vous livre juste ça comme ça pour vous donner des idée...

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.