2 votes

Regarder la sortie d'un script bash

Je suis en train d'essayer d'utiliser watch pour regarder la sortie d'un script shell, en exécutant /bin/bash et en gardant le script lui-même dans un here-document.

Cependant, le truc ne s'exécute qu'une seule fois. Il donne la bonne sortie, puis watch se rafraîchit et l'écran devient blanc. En sortant de watch, aucune erreur n'est répertoriée.

Je n'arrive pas à comprendre où est le problème car il devient difficile de déboguer avec watch > bash > heredoc > code laid.

La bonne nouvelle est que le code laid dans le here-document fonctionne bien.

function show_users { 

    [[ -z $1 ]] && watchtime=1 || watchtime=$1
    [[ -z $2 ]] && export userToShow="monutilisateurpardefaut" || export userToShow=$2

    echo "Mise en place de watch pour l'utilisateur ${userToShow}"

    watch -n $watchtime --no-title /bin/bash <<-'EOF'
        #Afficher les résultats de finger de l'utilisateur demandé
        finger ${userToShow}

        #afficher la liste des utilisateurs su dans l'utilisateur demandé
        echo "************************************************************************************"
        echo "utilisateurs connectés en tant que ${userToShow}"

        #obtenir les PID parent de tout processus appartenant à l'utilisateur demandé
        #dans une liste qui peut être lue par grep
        parentPIDs=$(ps -ef | grep "su - ${userToShow}" | grep -v 'grep\|finger' | awk 'NR>1{printf " %s \\|",parentpid}{parentpid=$3}END{printf " %s\n", parentpid}')

        #obtenir les noms d'utilisateur associés à ces PID parent
        parentUsers=$(ps -ef | grep "${parentPIDs}" | grep -v "grep\|${userToShow}" | awk '{print $1}' | sort | uniq)

        #finger chacun de ces utilisateurs et obtenir leur nom complet
        while IFS= read -r line ; do
            printf "%s: " $line
            parentName=$(finger $line | awk -F":" 'NR==1{print $3}')
            echo $parentName
        done <<< "${parentUsers}"

        #afficher l'arborescence de tous les processus exécutés par l'utilisateur demandé jusqu'à la racine.
        echo "************************************************************************************"
        ps -ef --forest | egrep -e "sshd:|-ksh|$userToShow" | grep -v grep | awk 'root==1{print ""} NR>1{print line} {line=$0;root=($1=="root") ? 1 : 0}'
    EOF
}

appelé comme :

show_users 2 "nomdutilisateur"

2voto

tripleee Points 28746

Vous pouvez mettre le code dans une fonction. Avec export -f func vous rendez la définition de la fonction disponible pour les sous-processus du script en cours, de sorte que vous puissiez dire ensuite

watch bash -c func

Dans l'exemple de l'OP :

# ne pas utiliser la syntaxe des fonctions bash uniquement (selon moi, moche)
get_user_info () {
    local userToShow=$1

    # Pas besoin de mettre des accolades autour des noms de variable sauf si une désambiguïsation est requise
    finger "$userToShow"

    echo "beaucoup de vilains astérisques"
    echo "utilisateurs connectés en tant que ${userToShow}"

    # éviter grep | grep | awk
    # le spécificateur de champ peut probablement être rendu plus strict
    # (correspondre uniquement à $2 au lieu de $0)?
    parentPIDs=$(ps -ef |
        awk -v user="$userToShow" 'NR>1 && ($0 ~ "su - " user) {
                printf " %s \\|",parentpid}
            {parentpid=$3}
            END{printf " %s\n", parentpid}')

    # Ce serait mieux si parentPIDs était une regex correcte
    # Suppose que vous cherchez le PPID dans la colonne 3
    parentUsers=$(ps -ef |
        awk -v pids="$parentPIDs" 'parentPIDs ~$3 {print $1}' |
        # préférer sort -u à sort | uniq
        sort -u)

    while IFS= read -r line ; do
        printf "%s: " "$line"
        # Pas besoin de capturer la sortie juste pour l'echo
        finger "$line" | awk -F":" 'NR==1{print $3}'
    done <<< "${parentUsers}"

    echo "Encore beaucoup d'astérisques laids"
    # Encore une fois, la regex peut probablement être appliquée à un seul champ
    ps -ef --forest |
        awk -v re="sshd:|-ksh|$userToShow"  '$0 !~ re { next }
            root==1{print ""}
            NR>1{print line}
            {line=$0;root=($1=="root" || $3==1) ? 1 : 0}'
}

export -f get_user_info

show_users () {
     # Éviter les complexes [[ -z ... ]]; utiliser des valeurs par défaut avec ${var-"valeur si non définie"}
     # Marquer ces variables comme locales pour éviter de polluer l'espace de noms global
     local watchtime={$1-1}
     local userToShow=${2-mydefaultuser}
     # pas besoin d'exporter ces variables

     echo "$mycommand"
     echo "Mise en place de la surveillance pour l'utilisateur ${userToShow}"

     watch -n $watchtime --no-title bash -c get_user_info "$userToShow"
}

1voto

janos Points 22603

watch exécute de manière répétée une commande spécifiée avec ses arguments. Le heredoc, et plus généralement l'effet des opérateurs de redirection ne font pas partie de la commande. Ainsi, watch ne peut pas régénérer le heredoc. Et une fois que le heredoc est consommé par le premier lancement de bash, eh bien, il ne restera rien pour le second.

Il existe une astuce grossière que vous pourriez essayer en bas de cette réponse. Mais ma solution recommandée est de sauvegarder le contenu du heredoc dans un fichier temporaire. C'est raisonnablement simple à faire et robuste.

Enregistrez le fichier dans un fichier temporaire, créé par mktemp. Configurez un trap pour capturer l'interruption et peut-être d'autres signaux pour vous assurer que le fichier temporaire est nettoyé. Exécutez watch bash "$tmpfile". C'est simple et ça fonctionnera.

"Solution" sale avec (graves) avertissements, ne faites pas ça !

Vous pourriez mettre un script dans une variable puis exécuter avec watch de cette manière :

watch "bash -c '$var'"

Ou comme ceci :

watch "bash -c \"$var\"" 

Mais l'avertissement grave est que la première version sera cassée si var contient ', et la deuxième version sera cassée si var contient ". Ainsi, ils fonctionneraient uniquement avec le type de scripts le plus basique, et certainement pas celui de votre exemple.

Ce n'est clairement pas une option, je l'ai juste ajoutée ici pour des raisons de complétude.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X