Bash scripting

Pour les personnes motivées à faire des scripts bash / sh, voici mes liens qui m’ont déjà bien aidé à plusieurs reprises:

et un exemple créé de mes propres mains que j’utilise tous les jours (fier fier :blush: )

[code]#!/bin/sh

TITLE="=== DEBIAN UPDATE MANAGER ===“
AUTHOR=”@2005-07 ghostintheshell from http://forum.debian-fr.org/"
VERSION=“v.2005.10.01”

To do:

- the to do list o_0’

DECLARATIONS

------------

OPTIONS (system)

#the sources file
SOURCES="/etc/apt/sources.list"

#editor to use
EDITOR="gedit"
EDITORPATH=$(which $EDITOR) #‘which’ returns the full path of the selected editor

#apt-get
APTGET="apt-get"
APTGETPATH=$(which $APTGET)

OPTIONS (script)

#verbose mode?
VERBOSE=1 #0 for silent, 1 for verbose

#debug mode?
DEBUG=0 #0 for normal, 1 for debug

CONSTANTS

#break options
WAIT=1 #wait for a user action
NOWAIT=0 #just display a break

PUBLIC DECLARATIONS

gsPossibilities="“
gsPossibilitiesDefault=”“
gsAnswer=”"

FUNCTIONS

---------

function displayDEBUG() {
if [ $DEBUG -eq 1 ]; then echo “[DEBUG] $1”; fi
}

function displayMESSAGE() {
if [ $VERBOSE -eq 1 ]; then echo “==> $1 …” && echo; fi
}

function displayERROR() {
echo “[ERROR] $1”
}

function displayBREAK() {
echo
if [ $1 -eq 1 ]; then echo -n “— Press to continue —”; read foo; echo; fi
}

function editSources() {
iError=0
displayMESSAGE “Opening the sources list with $EDITOR”
$EDITORPATH $SOURCES > /dev/null 2>&1

if [ $? -ne 0 ]; then
	iError=1
	displayERROR "Please check your parameters!"
	displayERROR "Editor = \"$EDITOR\""
	sMoreHelp=""; if [ -z $EDITORPATH ]; then sMoreHelp=" !!! not found !!!"; fi
	displayERROR "Editor path = \"$EDITORPATH\"$sMoreHelp"
	sMoreHelp=""; if [ ! -e $SOURCES ]; then sMoreHelp=" !!! not found !!!"; fi
	displayERROR "Sources file = \"$SOURCES\"$sMoreHelp"
	displayBREAK $WAIT
fi
return $iError

}

function updatePackageslist() {
displayMESSAGE "Updating"
apt-get update
}

function simulatePackages() {
iNewPackage=1
displayMESSAGE "Doing simulation"
if [ $VERBOSE -eq 1 ]; then
apt-get upgrade --simulate --show-upgraded
else
apt-get upgrade --simulate
fi

sNewPackage="$(apt-get dist-upgrade -s |  grep "newly installed" | grep "[1-9]")"
if [ -z "$sNewPackage" ]; then iNewPackage=0; fi
return $iNewPackage

}

function upgradePackages() {
displayMESSAGE "Upgrading"
if [ $VERBOSE -eq 1 ]; then
apt-get upgrade --show-upgraded
else
apt-get upgrade
fi
}

function upgradeSmart() {
displayMESSAGE "Smart upgrading"
apt-get dist-upgrade --show-upgraded
}

function checkDependencies() {
displayMESSAGE "Checking dependencies"
apt-get check
}

function cleanOldarchives() {
displayMESSAGE "Cleaning old archives"
apt-get autoclean
}

function writeQuestionOrInfo {
echo
case $1 in
"A")
echo -n "[1/5] Edit the sources list ($SOURCES) [y(es)/N(o)/e(x)it]?"
gsPossibilities=“ynx”; gsPossibilitiesDefault=“n”
;;
“B”)
echo -n “[2/5] Update the list of available packages [Y(es)/n(o)/®estart/e(x)it]?”
gsPossibilities=“ynrx”; gsPossibilitiesDefault=“y”
;;
“C”)
echo -n "[3/5] Proceed to an upgrade simulation [C(ontinue)/®estart/e(x)it]?"
gsPossibilities=“cbrx”; gsPossibilitiesDefault=“c”
;;
“D1”)
echo -n "[4/5] Proceed to (a smart) upgrade [(Y)es/(n)o/(s)tandard/®estart/e(x)it]?"
gsPossibilities=“ynsrx”; gsPossibilitiesDefault=“y”
;;
“D2”)
echo “[4/5] No need to upgrade [C(ontinue)]!”
;;
“E”)
echo -n "[5/5] Quit [e(X)it/®estart/(m)ore]?"
gsPossibilities=“xrm”; gsPossibilitiesDefault=“x”
;;
“M1”)
echo -n "[+] Check dependencies [Y(es)/n(o)/®estart/e(x)it]?"
gsPossibilities=“ynrx”; gsPossibilitiesDefault=“y”
;;
“M2”)
echo -n "[+] Clean old archives [Y(es)/n(o)/®estart/e(x)it]?"
gsPossibilities=“ynrx”; gsPossibilitiesDefault=“y”
;;
*)
;;
esac
}

function readAnswer {
while :
do
echo -n " "; read sInput

	if [ "$sInput" = "" ]; then sInput=$gsPossibilitiesDefault; fi  #default answer if empty answer detected
	if [ "${#sInput}" -ne 1 ]; then sInput="dummy"; fi				#answer length must be only 1 character!

	if echo $1 | grep -q "$sInput"; then
		gsAnswer=$sInput; break
	else
		echo -n "WTF!?"
	fi
	
done

}

function executeAction {

let iReturn=0

case $1 in
"A")
	writeQuestionOrInfo $1;	readAnswer $gsPossibilities
	case $gsAnswer in
	"y"|"Y") 
		editSources; iReturn=$?
		;;
	esac
	;;
"B")
	writeQuestionOrInfo $1; readAnswer $gsPossibilities
	case $gsAnswer in
	"y"|"Y")
		updatePackageslist
		;;
	esac
	;;
"C")
	writeQuestionOrInfo $1; readAnswer $gsPossibilities
	case $gsAnswer in
	"c"|"C")
		simulatePackages; iReturn=$?
		;;
	esac
	;;
"D1")
	writeQuestionOrInfo $1; readAnswer $gsPossibilities
	case $gsAnswer in
	"y"|"Y")
		upgradeSmart
		;;
	"s"|"S")
		upgradePackages
		;;
	esac
	;;
"D2")
	writeQuestionOrInfo $1;
	;;
"E")
	writeQuestionOrInfo $1; readAnswer $gsPossibilities
	case $gsAnswer in
	"m"|"M")
		iReturn=253
		;;
	esac
	;;
"M1")
	writeQuestionOrInfo $1; readAnswer $gsPossibilities
	case $gsAnswer in
	"y"|"Y")
		checkDependencies
		;;
	esac
	;;
"M2")
	writeQuestionOrInfo $1; readAnswer $gsPossibilities
	case $gsAnswer in
	"y"|"Y")
		cleanOldarchives
		;;
	esac
	;;
esac

case $gsAnswer in
"x"|"X"|"q"|"Q")
	iReturn=255	#exit
	;;
"r"|"R") 
	iReturn=254	#restart
	;;
esac

return $iReturn

}

MAIN

----

#welcome buddy
clear; echo -e "$TITLE ($VERSION)\n"
let iContinue=0

#some verifications
if [ -z $APTGETPATH ]; then
let iContinue=99
displayERROR "Sorry $APTGET is not found on this system!"
displayBREAK $WAIT
fi

#go for it
if [ $iContinue -eq 0 ]; then
while :
do

	if [ $iContinue -eq 0 ]; then
		clear; echo -e "$TITLE ($VERSION)\n\nUsing $($APTGET -v | grep "apt")"
		let iContinue=1
	fi
	
	#[A] Edit the sources list?
	if [ $iContinue -eq 1 ]; then
		executeAction "A"
		case $? in
			255) break;;
			254) iContinue=0;;
			1)  iContinue=0;;
		esac
	fi

	#[B] Update
	if [ $iContinue -eq 1 ]; then
		executeAction "B"
		case $? in
			255) break;;
			254) iContinue=0;;
		esac
	fi

	#[C] Proceed to upgrade simulation?
	if [ $iContinue -eq 1 ]; then
		let iNewPackage=0
		executeAction "C"
		case $? in
			255) break;;
			254) iContinue=0;;
			1) iNewPackage=1;;
		esac
	fi

	#[D] Proceed to (a smart) upgrade?
	if [ $iContinue -eq 1 ]; then
		if [ $iNewPackage -eq 1 ]; then
			executeAction "D1"
			case $? in
				255) break;;
				254) iContinue=0;;
			esac
		else
			executeAction "D2"
		fi
	fi
	
	#[+] More options #1 (Check dependencies)?
	if [ $iContinue -eq 2 ]; then
		executeAction "M1"
		case $? in
			255) break;;
			254) iContinue=0;;
		esac
	fi

	#[+] More options #2 (Clean old archives)?
	if [ $iContinue -eq 2 ]; then
		executeAction "M2"
		case $? in
			255) break;;
			254) iContinue=0;;
		esac
	fi

	#[E] Quit?
	if [ $iContinue -eq 1 ] || [ $iContinue -eq 2 ]; then
		executeAction "E"
		case $? in
			255) break;;
			254) iContinue=0;;
			253) iContinue=2;;
		esac
	fi	
	
done

fi

#see you buddy
echo; echo “Bye …”; echo
[/code]

un autre petit truc pour debugger ses scripts sh, au lieu de simplement executer le script de cette manière:

l’executer comme ceci

enjoy :wink:

un petit truc de flemmard pour economiser un caractère de temps en temps pendant la frappe:
=$(instruction)
peut aussi s’écrire
=instruction
Juste: l’avantage du $(), c’est qu’on peut l’imbriquer.

Petite remarque: les boucles while “true” avec des break partout, c’est pas propre du tout (pourquoi ne pas simplement faire un test genre [$iContinue -lt 0], tu pourrais en plus passer un code d’erreur abs($iContinue) - 1 en retour). De plus, si je peux me pemettre un jugement subjectif, ton style est affreux… Tu abuses des signaux à transmettre entre tes subs (les “actions”) et du coup le code est difficilement lisible.
Sur un tout autre aspect de ton script qui ne concerne pas le shell: que fais tu du fichier “preferences” ?

quote="MattOTop"
Petite remarque: les boucles while “true” avec des break partout, c’est pas propre du tout (pourquoi ne pas simplement faire un test genre [$iContinue -lt 0], tu pourrais en plus passer un code d’erreur abs($iContinue) - 1 en retour). De plus, si je peux me pemettre un jugement subjectif, ton style est affreux… Tu abuses des signaux à transmettre entre tes subs (les “actions”) et du coup le code est difficilement lisible.
Sur un tout autre aspect de ton script qui ne concerne pas le shell: que fais tu du fichier “preferences” ?[/quote]

peut-etre parce que je ne suis pas informaticien et encore moins programmeur?
ou peut-être encore parce que je fais ça durant mon temps libre et pour le fun?
je ne sais, à toi de me le dire :unamused:

sinon, et vu que tu le proposes si gentiment, ça serait sympa que tu passes en revue le script et que le réécrive à ta manière avec ton style :smiley:

je ne demande qu’à apprendre 8)

[quote]sinon, et vu que tu le proposes si gentiment, ça serait sympa que tu passes en revue le script et que le réécrive à ta manière avec ton style Very Happy

je ne demande qu’à apprendre[/quote]
Je le dis: Sage est le jeune Padawan …

En réponse au jeune padawan: pfffffffffff.
Fatigue…

[quote=“MattOTop”]En réponse au jeune padawan: pfffffffffff.
Fatigue…[/quote]

ça, c’est du style 8)

hop, je dois être dans la bonne rubrique pour poser mon problème :
comment faire un script bash, ou sh, ou python, ou perl, ou haskel, ou ruby, ou … bref… un script qui active plusieurs programmes et se retire en laissant les programmes activés.
Ce serait trés utile pour faire une commande “start” par exemple.
Le problème, c’est que le fait de lancer un programme par un script bloque celui-ci tant que le programme n’est pas terminé. Mais il doit bien y avoir une solution. Si on fait un while, le script ne se retire pas, il reste en processus et utilise de la ressource pour rien.

slt,

Plusieurs possibilités s’offre a toi, tu as le caractetre “&” qui permet de lançer en background mais pas tres patique parce que quand tu te delogue ca kill tout…sinon tu as aussi “exec” qui a mon avis devrait te satisfaire…

slt,
Ouaip, mais la commande exec execute le programme “à l’interieur” du shell, et le fait de killer le shell kill le programme. En plus, exec ne permet pas d’executer plusieurs programmes en même temps, à première vue.

slt,

En couplant “exec” avec “|” ou “;” ou tout autre redirection bash ca pourrait le faire sinon la solution serait de faire diviser ton script en plusieurs scripts.

une solution, c’est
nohup &

une autre : start-stop-daemon

[quote=“MattOTop”]une solution, c’est
nohup &[/quote]
Oui!! Ca marche!
Merci MattOTop.
Ca lance bien les programmes en dehors du script, et avec un “exit” à la fin, les laissent ouvert. C’est parfait pour une commande start perso. :slightly_smiling:

ah bhen merci pour la question et la reponse, je ne connaissais pas non plus :smiley:

[quote]NAME
nohup - run a command immune to hangups, with output to a non-tty

SYNOPSIS
nohup COMMAND [ARG]…
nohup OPTION

DESCRIPTION
Run COMMAND, ignoring hangup signals.
[/quote]

L’autre avantage, c’est que le processus se retrouvant détaché du shell qui l’a lancé, et donc fils d’init, on peut même fermer sa session et le processus continue à tourner. C’est comme ca que je fais mes téléchargements longs sur mes serveurs: une connection ssh, un nohup wget machin & , et hop je me déconnecte en laissant tourner le truc.
L’inconvenient, c’est la sortie nohup.out dans le répertoire courant.
Si nohup ne peut pas écrire, il refuse de se lancer.

[quote=“MattOTop”]L’autre avantage, c’est que le processus se retrouvant détaché du shell qui l’a lancé, et donc fils d’init, on peut même fermer sa session et le processus continue à tourner. C’est comme ca que je fais mes téléchargements longs sur mes serveurs: une connection ssh, un nohup wget machin & , et hop je me déconnecte en laissant tourner le truc.
L’inconvenient, c’est la sortie nohup.out dans le répertoire courant.
Si nohup ne peut pas écrire, il refuse de se lancer.[/quote]
Bonjour,
Autre solution à ce problème : la commande screen qui permet de lancer une tâche dans un terminal, de fermer ce terminal (ou de se Détacher proprement en tappant Control-A puis D) sans que ça coupe la tâche et même de reprendre exactement où ça en est avec l’option -r de screen (pour Rattache).
On peut même lancer plusieurs session shell dans un seul script avec screen.

Idéal pour travailler sur une machine distante si la connexion coupe de temps en temps (malgrés la coupure, on peut reprendre exactement là où on était).

Résumé :

  • pour lancer un screen, taper : screen
  • pour se détacher d’un screen, taper : Ctrl+a puis d
  • pour revenir dans un screen détacher, taper : screen -r []
  • pour avoir la liste des screen lancés, taper : screen -ls
  • pour terminer un screen, taper : Ctrl+d (comme dans un shell normal)
  • pour plus d’info sur screen, taper : man screen :wink:

screen: testé -> adopté