web-dev-qa-db-fra.com

Pourquoi ne puis-je pas utiliser le contrôle du travail dans un script bash?

Dans cette réponse à un autre question , on m'a dit que

dans les scripts, vous n'avez pas le contrôle du travail (et essayer de l'allumer est stupide)

C'est la première fois que j'entends cela et je me suis penché sur la section bash.info sur le contrôle des tâches (chapitre 7), ne trouvant aucune mention de l'une ou l'autre de ces assertions. [Update: La page de manuel est un peu meilleure, mentionnant une utilisation "typique", les paramètres par défaut et les E/S de terminal, mais aucune raison réelle pour laquelle le contrôle des tâches est particulièrement déconseillé pour les scripts.]

Alors, pourquoi le contrôle des tâches basé sur des scripts ne fonctionne-t-il pas et qu'est-ce qui en fait une mauvaise pratique (ou "stupide")?

Edit: Le script en question démarre un processus d’arrière-plan, lance un second processus d’arrière-plan, puis tente de replacer le premier processus au premier plan afin que celui-ci ait une E/S de terminal normale puis être redirigé hors du script. Ne peut pas faire cela à un processus d'arrière-plan.

Comme indiqué par la réponse acceptée à l'autre question, il existe d'autres scripts qui résolvent ce problème particulier sans tenter de contrôler le travail. Bien. Et le script lambasted utilise un numéro de travail codé en dur - évidemment mauvais. Mais j'essaie de comprendre si le contrôle des tâches est une approche fondamentalement vouée à l'échec. Il semble encore que peut-être que cela fonctionne {pourrait} _.

45
system PAUSE

Ce qu'il voulait dire, c’est que le contrôle des tâches est désactivé par défaut en mode non interactif (c’est-à-dire dans un script).

De la page de manuel bash:

JOB CONTROL
       Job  control refers to the ability to selectively stop (suspend)
       the execution of processes and continue (resume) their execution at a
       later point.
       A user typically employs this facility via an interactive interface
       supplied jointly by the system’s terminal driver and bash.

et

   set [--abefhkmnptuvxBCHP] [-o option] [arg ...]
      ...
      -m      Monitor mode.  Job control is enabled.  This option is on by
              default for interactive shells on systems that support it (see
              JOB CONTROL above).  Background processes run in a separate
              process group and a line containing their exit status  is
              printed  upon  their completion.

Quand il a dit "est stupide", il ne pensait pas seulement:

  1. est le contrôle des tâches destiné principalement pour faciliter le contrôle interactif (alors qu’un script peut fonctionner directement avec le pid), mais aussi
  2. Je cite sa réponse initiale, ... s'appuie sur le fait que vous n'avez démarré aucun autre travail précédemment dans le script, ce qui est une mauvaise supposition à faire. Ce qui est tout à fait correct.

METTRE À JOUR

En réponse à votre commentaire: oui, personne ne vous empêchera d’utiliser le contrôle des travaux dans votre script bash - il n’existe aucune difficulté majeure pour désactiver avec forceset -m (c’est-à-dire que le contrôle des travaux depuis le script fonctionnera si vous le souhaitez. Rappelez-vous qu’en fin de compte, en particulier dans les scripts, il existe toujours plus d’un moyen de traiter un chat, mais certains sont plus portables, plus fiables, facilitent le traitement des erreurs, l’analyse de la sortie, etc.

Vos circonstances particulières peuvent justifier ou non une différence par rapport à ce que lhunath (et les autres utilisateurs) considèrent comme des "meilleures pratiques".

41
vladr

Le contrôle de tâche avec bg et fg est utile uniquement dans les shells interactifs. Mais & avec wait est également utile dans les scripts.

Sur des systèmes multiprocesseurs, la création de tâches en arrière-plan peut considérablement améliorer les performances du script, par exemple. dans les scripts de construction où vous voulez démarrer au moins un compilateur par CPU, ou traiter les images en utilisant les outils ImageMagick en parallèle, etc.

L'exemple suivant exécute jusqu'à 8 gcc parallèles pour compiler tous les fichiers source d'un tableau:

#!bash
...
for ((i = 0, end=${#sourcefiles[@]}; i < end;)); do
    for ((cpu_num = 0; cpu_num < 8; cpu_num++, i++)); do
        if ((i < end)); then gcc ${sourcefiles[$i]} & fi
    done
    wait
done

Il n'y a rien de "stupide" à ce sujet. Mais vous aurez besoin de la commande wait , qui attend tous les travaux en arrière-plan avant que le script ne continue. Le PID du dernier travail en arrière-plan est stocké dans la variable $!. Vous pouvez donc aussi wait ${!}. Notez également la commande Nice .

Parfois, ce code est utile dans les makefiles:

buildall:
    for cpp_file in *.cpp; do gcc -c $$cpp_file & done; wait

Cela donne un contrôle beaucoup plus fin que make -j.

Notez que & est un terminateur de ligne comme ; (write command& not command&;).

J'espère que cela t'aides.

29
Andreas Spindler

Le contrôle des tâches n’est utile que lorsque vous exécutez un shell interactif, c’est-à-dire que vous savez que stdin et stdout sont connectés à un périphérique terminal (/ dev/pts/* sous Linux). Ensuite, il est logique d’avoir quelque chose au premier plan, quelque chose d’autre au fond, etc.

Les scripts, par contre, n'ont pas une telle garantie. Les scripts peuvent être rendus exécutables et exécutés sans aucun terminal connecté. Cela n'a pas de sens d'avoir des processus de premier plan ou d'arrière-plan dans ce cas.

Vous pouvez toutefois exécuter d'autres commandes de manière non interactive sur l'arrière-plan (en ajoutant "&" à la ligne de commande) et capturer leurs PID avec $!. Ensuite, vous utilisez kill pour les supprimer ou les suspendre (en simulant Ctrl-C ou Ctrl-Z sur le terminal, si le shell était interactif). Vous pouvez également utiliser wait (au lieu de fg) pour attendre la fin du processus en arrière-plan.

6
Juliano

Il pourrait être utile d’activer le contrôle du travail dans un script pour définir des interruptions sur SIGCHLD. La section CONTRÔLE DES TRAVAUX dans le manuel indique:

Le shell apprend immédiatement chaque fois qu'un travail change d'état. Normalement, bash attend jusqu'à ce qu'il soit sur le point d'imprimer une invite avant de signaler changements dans l'état d'un travail afin de ne pas interrompre une autre sortie. Si l'option -b de la commande intégrée set est activée, indique bash de tels changements immédiatement. Tout piège sur SIGCHLD est exécuté pour chaque enfant qui sort.

(c'est à moi)

Prenons le script suivant, à titre d'exemple:

dualbus@debian:~$ cat children.bash 
#!/bin/bash

set -m
count=0 limit=3
trap 'counter && { job & }' CHLD
job() {
  local amount=$((RANDOM % 8))
  echo "sleeping $amount seconds"
  sleep "$amount"
}
counter() {
  ((count++ < limit))
}
counter && { job & }
wait
dualbus@debian:~$ chmod +x children.bash 
dualbus@debian:~$ ./children.bash 
sleeping 6 seconds
sleeping 0 seconds
sleeping 7 seconds

Note: le piégeage de CHLD semble être interrompu à partir de la bash 4.3

Dans la version 4.3, vous pouvez utiliser 'wait -n' pour obtenir la même chose, bien que

dualbus@debian:~$ cat waitn.bash 
#!/home/dualbus/local/bin/bash

count=0 limit=3
trap 'kill "$pid"; exit' INT
job() {
  local amount=$((RANDOM % 8))
  echo "sleeping $amount seconds"
  sleep "$amount"
}
for ((i=0; i<limit; i++)); do
  ((i>0)) && wait -n; job & pid=$!
done
dualbus@debian:~$ chmod +x waitn.bash 
dualbus@debian:~$ ./waitn.bash 
sleeping 3 seconds
sleeping 0 seconds
sleeping 5 seconds

Vous pourriez faire valoir qu'il existe d'autres moyens de faire cela de manière plus portable, c'est-à-dire sans CHLD ou wait -n:

dualbus@debian:~$ cat portable.sh 
#!/bin/sh

count=0 limit=3
trap 'counter && { brand; job & }; wait' USR1
unset RANDOM; rseed=123459876$$
brand() {
  [ "$rseed" -eq 0 ] && rseed=123459876
  h=$((rseed / 127773))
  l=$((rseed % 127773))
  rseed=$((16807 * l - 2836 * h))
  RANDOM=$((rseed & 32767))
}
job() {
  amount=$((RANDOM % 8))
  echo "sleeping $amount seconds"
  sleep "$amount"
  kill -USR1 "$$"
}
counter() {
  [ "$count" -lt "$limit" ]; ret=$?
  count=$((count+1))
  return "$ret"
}
counter && { brand; job & }
wait
dualbus@debian:~$ chmod +x portable.sh 
dualbus@debian:~$ ./portable.sh 
sleeping 2 seconds
sleeping 5 seconds
sleeping 6 seconds

Donc, en conclusion, set -m est not utile dans les scripts, puisque La seule fonctionnalité intéressante qu’il apporte aux scripts est de pouvoir travailler avec SIGCHLD. Et il existe d'autres moyens de réaliser la même chose Soit plus court (wait -n), soit plus portable (envoyer des signaux vous-même).

5
Eduardo Bustamante

Bash favorise le contrôle des tâches, comme vous le dites. Dans l'écriture de scripts Shell, il est souvent supposé que vous ne pouvez pas compter sur le fait que vous avez bash, mais que vous avez le Vanilla Bourne Shell (sh), qui historiquement n'avait pas de contrôle de travail.

De nos jours, j'ai peine à imaginer un système dans lequel vous êtes honnêtement limité au véritable Bourne Shell. Le /bin/sh de la plupart des systèmes sera lié à bash. Pourtant, c'est possible. Une chose que vous pouvez faire est au lieu de spécifier

#!/bin/sh

Tu peux faire:

#!/bin/bash

Cela, ainsi que votre documentation, indiquerait clairement que votre script a besoin de bash.

1
Peter

Peut-être, mais j'utilise assez souvent Nohup lorsque ssh est installé sur un serveur sur un travail de longue durée. Ainsi, si je suis déconnecté, le travail est toujours terminé.

Je me demande si les gens confondent s’arrêter à partir d’un processus interactif principal de Shell et créer des processus en arrière-plan? La commande wait vous permet de générer beaucoup de choses, puis d'attendre qu'elles soient toutes terminées. Comme je l'ai dit, j'utilise Nohup tout le temps. C'est plus complexe que cela et très sous-utilisé - sh prend également en charge ce mode. Regardez le manuel.

Vous avez aussi

kill -STOP pid

Je le fais assez souvent si je veux suspendre le logiciel Sudo en cours d'exécution, comme dans:

kill -STOP $$

Mais malheur à vous si vous êtes passé d'un éditeur à un autre éditeur Shell.

J'ai tendance à utiliser mnémonique -KILL etc. parce qu'il y a un risque de dactylographie

kill - 9 pid # note the space

et jadis, vous pouviez parfois mettre la machine hors service parce que cela tuerait init!

0
Ghoti

emplois travaillent dans des scripts bash

MAIS, vous ... devez BESOIN de surveiller le personnel engendré.

ls -1 /usr/share/doc/ | while read -r doc ; do ... done

les emplois auront un contexte différent de chaque côté de la |

en contournant ceci peut être utiliser pour au lieu de tant que:

for `ls -1 /usr/share/doc` ; do ... done

cela devrait montrer comment utiliser les tâches dans un script .... avec la mention que ma note commentée est ... REAL (ne sait pas pourquoi ce comportement)

    #!/bin/bash


for i in `seq 7` ; do ( sleep 100 ) &  done

jobs

while [ `jobs | wc -l` -ne 0 ] ; do

    for jobnr in `jobs | awk '{print $1}' | cut -d\[ -f2- |cut -d\] -f1` ; do
        kill %$jobnr
    done
    #this is REALLY ODD ... but while won't exit without this ... dunno why
    jobs >/dev/null 2>/dev/null
done

sleep 1
jobs
0
THESorcerer