ECHO et guillemets, avec ou sans?
Problème:
Lorsqu'il faut enchainer les pipes (pas de jeux de mots siouplé), le formattage de sortie d'une commande doit correspondre à la commande suivante. La commande echo prend beaucoup de libertés avec les retours-chariot. Par exemple: echo $(ls) donne une suite de fichiers séparés par un espace. Il y a bien un '\n' entre chaque nom dans la variable a, mais il est transformé en ' ' par echo! En effet, chaque nom est une entrée séparée et echo ajoute son séparateur qui est l'espace.
Solution:
Les guillemets! La commande echo "$(ls)" laisse la chaîne de caractères telle quelle puisqu'il n'y a plus qu'une seule entrée.
Trier sur les champs (SORT)
Problème:
Comme j'en ai déjà parlé sur un autre fil, certains fichiers à trier sont constitués de plusieurs champs, le fichier /etc/passwd par exemple. La simple commande sort /etc/passwd donne parfois des surprises. Essayez, vous verrez.
Solution:
Demandez (gentiment) à la commande sort de trier suivant le(s) champ(s) que vous désirez (les champs doivent être consécutifs): sort -t ':' -k 1,1 /etc/passwd. Cela veut dire: trier le fichier comportant plusieurs champs séparés par ':' depuis le champ 1 jusqu'au champ 1. Cette fois-ci, le séparateur n'est pas testé comme simple caractère et le tri devient correct...
Dans la pénombre du tunnel (SSH)
Problème:
Il y a plein de services au boulot que j'aimerais utiliser à la maison, comme le mail, mais sans avoir à passer par un client web (que je déteste). Malheureusement Thunderbird n'arrive pas à s'y connecter. Normal, m'a-t-on répondu, de l'extérieur ça marche que sous Windows! :furieux:.
Solution:
Pas de panique, ssh est là. Prenons le cas du serveur SMTP: je dis à Thunderbird qu'il est assis directement dessus (localhost), à un port plus grand que 1024, pour ne pas être root chaque fois que je veux envoyer un mail.
Code:
1 2
| Server Name: localhost
Port: 1025 |
Cela est à faire une fois pour toute. Ensuite j'ouvre une connexion ssh avec l'unique ordinateur visible de la boîte, ordi_boite, avec les options magiques à chaque session où je veux envoyer un e-mail:
Code:
ssh -L 1025:smtp_server:25 jmelyn@ordi_boite
Explications:
Le client ssh à la maison écoute sur le port 1025 (vers lequel parle Thunderbird lorsque j'envoie un e-mail), transfert les données vers ordi_boite puis les renvoie vers le serveur smtp_serveur (celui qui est caché dans la boîte), port 25, celui de smtp. Maintenant j'envoie tous les mails que je veux depuis Thunderbird à la maison, et j'ai pas Windows! :pingoin2:
Single-quote or double-quote? That is the question!
Texte repris d'un fil à propos de sed: Quelles différences y a-t-il entre les chaînes délimitées par ' (single-quote) et celles délimitées par " (double-quote)?- ": à l'intérieur de double-quotes, Bash considère que c'est une chaîne de caractères à ne pas interpréter, sauf pour 4 caractères spéciaux:
- $: évaluation de 3 types d'expression:
- contenu de variable: echo "$SHELL" donne /bin/bash
- retour de commande: echo "heure: $(date +%H:%M)" donne heure: 13:00
- résultat de calcul arithmétique: echo "1 + 1 = $((1 + 1))" donne 1 + 1 = 2
- `: back-quote, echo "heure: `date +%H:%M`" est synonyme de echo "heure: $(date +%H:%M)"
- \: utilisé pour échapper (c'est-à-dire supprimer le sens spécial) les 5 caractères spéciaux suivants:
- $: echo "caractère dollar: \$" donne caractère dollar: $
- `: echo "caractère back-quote: \`" donne caractère back-quote: `
- ": echo "caractère double-quote: \"" donne caractère double-quote: "
- \: echo "caractère back-slash: \\" donne caractère back-slash: \
- retour-chariot: si un \ est en fin de ligne dans une chaîne entre " et ", alors la chaîne continue sur la ligne suivante sans ce retour-chariot. echo "ligne1, \
encore ligne 1 :-)" donne ligne 1, encore ligne 1 :-)
- !: utilisé pour l'historique des commandes. Entre double-quotes, il est impossible d'échapper ce caractère, ce sera toujours l'historique. Par exemple chez moi, echo "!ssh" donne ssh -L 1025:smtp:25 -L 1636:ldap:636 serveur.du.boulot: c'est ma dernière commande commençant par ssh.
- ': à l'intérieur de single-quotes, Bash considère que c'est une chaîne de caractères à ne pas interpréter du tout. L'interpréteur recherche simplement le second single-quote qui termine la chaîne, sans plus. Impossible donc de mettre un single-quote à l'intérieur d'une chaîne délimitée par single-quotes!
Boucle for comme en C, c'est possible?
Problème:
En langage de script en général, et en Bash en particulier, il est possible de passer en revue la liste des fichiers d'un répertoire par exemple:
Code:
1 2 3 4
| for fichier in $(echo *.c)
do
head -20 $fichier
done |
Mais lorsqu'on veut faire une action 10 fois par exemple, ça ne marche pas!
Solutions:
Une première possibilité est d'utiliser la commande seq:
Code:
1 2 3 4
| for i in $(seq 10)
do
echo "i: $i"
done |
Et la seconde possibilité est d'utiliser la syntaxe arithmétique de Bash:
Code:
1 2 3 4
| for ((i = 1; i <= 10; i++))
do
echo "i: $i"
done |
Bon, la première solution est certainement plus portable vers les autres shells...
Vérifier / ajouter / forcer une ligne de texte
Problème:
Je voudrais être sûr que le fichier de configuration toto.conf du programme toto ait bien la ligne "ligne hyper importante = moi personnellement je", que la ligne n'apparaisse qu'une seule fois et que ce soit exactement celle-là si elle y est déjà.
Solution:
Je tombe régulièrement sur ce genre de problème et suite à un fil, je place la solution que j'utilise maintenant régulièrement.
Code:
1 2 3 4 5 6 7 8 9 10
| line="ligne hyper importante = moi personnellement je"
selection="^ligne hyper importante = "
confFile="toto.conf"
awk -v l="$line" -v s="$selection" '
{
if ($0 !~ /^#/ && $0 ~ s) {if (!done) {print l; done=1}}
else {print}
}
END {if (!done) {print l}}' $confFile > $confFile.new
mv -f $confFile.new $confFile.new |
Explications:
- Les trois premières lignes initialisent des variables. Pas important.
- La première partie de awk permet de ne pas tenir compte des commentaires et de forcer la ligne si elle est trouvée. Dans ce dernier cas, les éventuelles lignes suivantes identiques sont supprimées.
- La dernière partie de awk ajoute la ligne si elle n'a pas été détectée.
Correction pour truc et astuces
Citation:
Envoyé par
jmelyn
Exemple 1: L'effondrement du mur de Berlin, c'était quel jour?
D'abord, petit tour sur Wikipépia me donne le 09 novembre 1989.
Puis, le
man de
date -d m'indique comment passer la date en nombre de secondes.
Ensuite le
man de
awk au châpitre
Time functions me propose
strftime() qui me va bien.
Enfin, le
man de
strftime, fonction C, me fournit le bon argument:
%A.
Code:
1 2
| date -d 1989-11-09 +%s | awk '{print strftime("%A", $0)}'
Thursday |
Pourquoi pas simplement:
Code:
1 2
| $ date -d 1989-11-09 +%A
jeudi |
Ou, en anglais:
Code:
1 2
| $ LANG=C date -d 1989-11-09 +%A
Thursday |
Créer un lockfile - verrou
Si on veut qu'un script ne tourne pas s'il est déjà lancé par un autre utilisateur. Ou bien si on souhaite qu'un script ne se lance pas si un autre est toujours occupé, classiquement, on peut créer un "lock file" qui est détruit quand le script se termine et tester son existence. Il y a plus simple: la commande lockfile.
Un petit exemple que vous pourrez développer:
Exemple, premier processus:
Code:
lockfile /tmp/lock; sleep 10; rm -f /tmp/lock
Le second processus attendra tant que le premier est occupé:
Code:
lockfile /tmp/lock && echo "Enfin! C'est pas trop tôt..."; rm -f /tmp/lock
Le second processus s'interrompt si le premier est occupé:
Code:
lockfile -r0 /tmp/lock && echo "Enfin! C'est pas trop tôt..."; rm -f /tmp/lock
Voir aussi les autres options telles que sleeptime.
Source:
http://www.unix.com/shell-programmin...#post302364124
getopts : Mutualisation des options dites "communes"
En créant de multiples scripts, j'ai eu envie des créer de l'aide en ligne, d'afficher leur version, ou de les lancer en version debug, plus certaines particularités pour certains scripts. La commande getops fût la bienvenue. Plusieurs étapes ont vu le jour dans mon idée, dont voici la première :
- mutualisation des options dites « communes » (ex : -h, -x) en les incluant dans un script de style bibliothèque.
Mutualisation des options :
Ci-dessous, la commande getops lançant la bibliothèque d'options communes :
Code:
1 2 3 4 5 6 7 8 9
| while getopts abcdefghijklmnopqrstivwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ OPTION
do
case $OPTION in
a) f_a_all # traitements (ici lancement de la function f_a_all)
;;
*) . /usr/expl/proc/BIBLIOTHEQUES/getopts.lib
;;
esac
done |
La bibliothèque des options communes (getopts.lib) :
Code:
1 2 3 4 5 6 7 8 9 10
| case $OPTION in
h) help # traitements (ici lancement de la function help)
;;
v) version # traitements (ici lancement de la function version)
;;
x) set x # traitements (ici set x)
;;
*) echo "Erreur : l'option courte \"$OPTION\" est inexistante"
exit
esac |
PS : dans cette exemple, les options a, h, v et x sont reconnues. Les autres options afficheront :
l'option courte \"$OPTION\" est inexistante"
Attention : la bibliothèque doit être lancée avec le point devant « . »
Dans cet exemple, nous pouvons utiliser l'option -x au lieu d'ajouter la commande "set -x" dans le script et évite son modification.
Cela permet une plus grande souplesse dans les ajouts, modifications ou suppressions d'options communes (ex : ajout d'options longues dans ma seconde étape).
getopts en ksh : les options longues "sans arguments"
Nous voilà dans ma seconde étape, afin de pallier au problème des options longues, j'ai recours à l'utilisation du tiret comme option. Je ne suis pas le premier à trouver une solution, mais voici ma solution (en 2 points):
- ajout d'une option acceptant les arguments "-:"
- utilisation de la commande case pour les options longues
getopts dans le script :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| while getopts abcdefghijklmnopqrstivwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-: OPTION
do
case $OPTION in
a) f_a_all # Lancement de function f_a_all
;;
d) f_b_description # Lancement de function f_d_description
;;
-) case $OPTARG in
all) f_a_all # Lancement de la function f_a_all
;;
description) f_b_description # Lancement de function f_d_description
;;
*) . /usr/expl/proc/BIBLIOTHEQUES/getopts.lib
;;
esac
*) . /usr/expl/proc/BIBLIOTHEQUES/getopts.lib
;;
esac
done |
et pour les options communes :
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| case $OPTION in
h) help # traitements (ici lancement de la function help)
;;
x) set x # traitements (ici set x)
;;
-) case $OPTARG in
debug) set -x
;;
help) help
;;
*) echo "Erreur : l'option longue \"$OPTARG\" est inexistante"
exit
;;
esac
*) echo "Erreur : l'option longue \"$OPTARG\" est inexistante"
exit
;;
esac |
La troisième et dernière étape sera "getops en ksh - les options longues avec arguments".
getopts en ksh : les options longues "avec arguments"
Nous voici donc dans la troisième étape (complément des options longues sans arguments) :
Afin de pouvoir récupérer les arguments des options longues, il faut prétraiter les paramètres afin de les avoir sur plusieurs lignes (au lieu d’une seule). L’action d’aller à la ligne doit se faire uniquement lorsque nous rencontrons un tirer « - »
Prétraitement avant getopts :
Code:
1 2 3 4 5 6 7 8 9
| for i in $*
do
if [ "$(echo $i | cut -c1)" = "-" ]
then
PARAMETRES="$PARAMETRES\n$i"
else
PARAMETRES="$PARAMETRES $i"
fi
done |
Cela nous permet, dans l’exemple ci-dessous :
Code:
1 2 3 4 5 6
| courriel --objet "TEST" --message "Ceci est un test" --destinataire "toto@huit.fr"
echo $PARAMETRES
--objet TEST
--message Ceci est un test
--destinataire toto@huit.fr |
Il suffit ensuite de lui passer un grep en prenant à partir du second champ, comme ceci :
Code:
1 2 3 4 5 6 7 8 9 10 11 12
|
-) case $OPTARG in
destinataire*) CIBLE=$(echo $PARAMETRES | grep "\-\-destinataire" | cut -d" " -f2-)
shift
;;
message) MESSAGE="$(echo $PARAMETRES | grep "\-\-message" | cut -d" " -f2-)"
shift
;;
objet) OBJET="$(echo $PARAMETRES | grep "\-\-objet" | cut -d" " -f2-)"
shift
;;
esac |
et le tour est joué !!!!
Code:
1 2 3 4 5 6 7 8
| echo $CIBLE
toto@huit.fr
echo $MESSAGE
Ceci est un test
echo $OBJET
TEST |