Archives par mot-clé : Bash

Remplacer des caractères spéciaux avec sed

Sid - © Blue Sky Studios
Sid – © Blue Sky Studios

Mais non pas Sid ! « sed » le petit programme sympa qui permet de faire des traitements sur du texte !

Un problème courant est d’avoir des caractères indésirables dans un fichier texte, qu’il s’agisse de données, d’un code source ou d’une page Web. Il arrive même qu’on ait affaire à des caractères ne s’affichant pas correctement, ou bien qui empêchent carrément le fonctionnement d’un logiciel.

Connaître le code hexadécimal des caractères avec « od »

Aujourd’hui tout le monde devrait utiliser l’encodage UTF-8. Dans les faits, c’est très loin d’être le cas : il faut souvent faire avec l’existant (Windows-1258, ISO-8859-*…), et l’UTF-8 ne couvrant pas tous les caractères possibles et imaginables, on peut aussi tomber sur des trucs exotiques issus de l’UTF-16 ou l’UTF-32.

Pour convertir un caractère spécial, une solution générique consiste à d’abord connaître son code hexadécimal (on devrait peut-être même dire « ses codes hexadécimaux » étant donné que ces caractères spéciaux sont encodés sur plusieurs octets). C’est un peu roots, mais ça aura le mérite de fonctionner dans n’importe quel shell de n’importe quel environnement.

Prenons par exemple le caractère €, et voyons à quoi il ressemble avec l’aide de la commande « od » :

printf '€' | od -vAn -tx1
 e2 82 ac

C’est donc cette suite de codes hexadécimaux que nous allons fournir à sed, moyennant une écriture légèrement différente puisque chaque code sera écrit de la forme \x??.

Cas des caractères qu’on n’arrive pas à taper au clavier ou à afficher

Certains caractères peuvent être particulièrement problématiques quand on souhaite connaître leurs codes hexadécimaux, tout simplement parce qu’on ne parvient pas à les taper au clavier, voire à les copier-coller. Dans ce cas, le mieux est d’isoler ces caractères dans un tout petit fichier texte et d’analyser les caractères avec od :

cat analyse.txt | od -vAn -tx1
 31 32 33 2e 34 35 20 e2 82 ac 20 54 54 43

Conversion des codes hexadécimaux avec sed

Maintenant que nous connaissons les caractères que nous souhaitons supprimer/modifier/remplacer (rayer les mentions inutiles), nous pouvons commencer par tester la commande sed avec les codes hexadécimaux :

echo '€' | sed 's/\xe2\x82\xac/\€/'
€

Dans l’exemple ci-dessus, les apostrophes (« simples quotes ») permettent de ne pas échapper les codes hexadécimaux ; mais cette autre écriture est tout à fait équivalente :

echo '€' | sed s/\\xe2\\x82\\xac/'\€'/
€

Il faut toutefois souligner que dans le cas présent, où l’on va remplacer le symbole € par son entité HTML, il est indispensable que l’entité soit d’une part entre apostrophes, d’autre part que le caractère & soit échappé avec un antislash. Ceci vient du fait que sed interprète par défaut & comme une recopie sur la sortie du motif trouvé… Et à ceci s’ajoute le shell qui interprète lui aussi ce caractère !

Remplacer plusieurs caractères spéciaux

Avec l’argument « -r » passé à sed, il est également possible de remplacer plusieurs motifs par quelque chose, en séparant ces motifs par « | » (la joie des expressions régulières ! :mrgreen:). Par exemple, si l’on souhaite changer tous les E accentués d’un texte (ÉÈÊË) en E, il suffit de faire :

sed -r s/\\xc3\\x89\|\\xc3\\x88\|\\xc3\\x8b\|\\xc3\\x8a/E/g origine.txt > resultat.txt

Voilà tout !

Recycler un vieux jeu « Qui est-ce ? »

« Qui est-ce ? » héros Disney
« Qui est-ce ? » héros Disney

Des cartes personnalisées pour le jeu « Qui est-ce ? »

Le jeu « Qui-est-ce ? » fut inventé en 1979 — une fameuse année ! Bien que les trentenaires d’aujourd’hui puissent être nostalgiques de ce jeu et des personnages qui le composaient initialement, je me suis décidé à produire d’autres cartes que celles par défaut. En voici les raisons :

  • Les personnages, au nombre de 24, sont principalement masculins. On ne compte que 5 personnages féminins ! Et il n’y a pas tellement de mixité ethnique non plus…
  • Les enfants aiment bien renouveler les personnages. Hasbro l’a bien compris en éditant une nouvelle version de ce jeu avec plusieurs sets de personnages (classiques, animaux, monstres, etc…), mais puisque je dispose du vieux jeu, j’aime autant le recycler.
  • Je souhaitais permettre à ma gamine de jouer avec ses princesses préférées. C’était aussi l’occasion de proposer des sets de personnages sur des thèmes geek.
« Qui est-ce ? » héros Disney (humains)
« Qui est-ce ? » héros Disney humains (ou presque humains)
« Qui est-ce ? » Disney avec des duos
« Qui est-ce ? » Disney non humains et duos
« Qui est-ce ? » Disney vilains
« Qui est-ce ? » Disney vilains
« Qui est-ce ? » Star Wars
« Qui est-ce ? » Star Wars

Des planches d’images en PDF et au format A4

J’ai développé un petit programme qui, à partir d’un répertoire de 24 images, consiste à produire 3 planches au format A4 :

  • une planche de personnages pour le plateau rouge,
  • une planche de personnages pour le plateau bleu,
  • une planche de cartes à piocher (la fameuse carte que l’on place devant soi).

Préparer ses 24 personnages

Pour utiliser efficacement ce programme, il y a un petit travail préparatoire à réaliser sur les 24 images :

  • Le nom du fichier sera écrit en-dessous du personnage, donc il faut correctement nommer chaque image.
  • Les images doivent avoir pour proportions (ou « ratio ») 26/17 (hauteur/largeur)

Utilisation du script

Ce script Shell fait appel à deux logiciels : ImageMagick pour les multiples traitements d’images, et pdftk pour l’assemblage des 3 planches d’images en un seul fichier PDF prêt à imprimer. Il est donc nécessaire de les installer.

Maintenant, vous allez pouvoir télécharger ce script ! Chouette !

quiestce.sh

Enfin, voici les quelques planches déjà réalisées avec cet outil :

disney1 ; disney2 ; vilains_disney ; starwars

Amusez-vous bien !

Renommer/trier ses photos à la volée

Très régulièrement, on me demande comment renommer chronologiquement un groupe de photos, et les trier dans de multiples sous-répertoires. Sous GNU-Linux évidemment… 😎 Étant donné que je n’avais jamais publié de solution pour cela (et qu’à chaque fois je repars inutilement de zéro), voici un script Shell/Bash, reposant sur les programmes d’ImageMagick, permettant de réaliser très facilement deux ou trois petites choses :

  • Créer des sous répertoires selon l’ordre « année/mois/jour » à partir des informations Exif de date de prise de vue présentes dans chacune des photos
  • Copier chacune des photo dans le bon sous-répertoire « jour/ »
  • Renommer ces photos à l’aide des informations Exif de date et heure de prise de vue
  • Ajouter une valeur incrémentée en fin du nom du fichier si une autre photo porte déjà le même nom
  • Tourner la photo dans le bon sens si l’appareil avait été tenu verticalement lors du déclenchement

#! /bin/bash

# Au prealable, on s'assure que les programmes necessaires sont deja installes
which identify >> /dev/null 2>> /dev/null
if test $? -eq 1
then
	cat << EOF >&2
	Les programmes d'ImageMagick ne sont pas installes. Merci d'executer cette commande en tant qu'utilisateur root :
	
	urpmi imagemagick
	
EOF
	exit 1
fi

i=0

for fic in *jpg *JPG
do
	if test -f "$fic"
	then
		# Increment permettant de ne pas ecraser les noms en doublon (photos prises au meme instante en rafale)
		i=$((i + 1))
		i2=`printf '%04d' $i`
		
		# De la forme 2013_06_03_10_41_10
		nouveau_nom=`identify -verbose $fic | grep 'exif:DateTime:' | sed -e s/'^.*\([0-9][0-9][0-9][0-9]:[0-9][0-9]:[0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9]\).*$'/'\1'/g | sed s/'[: ]'/'_'/g`
		
		# De la forme 2013/06/03
		repertoire=`echo "${nouveau_nom:0:10}" | tr '_' '/'`
		
		mkdir -p $repertoire 2>> /dev/null
		
		nouveau_fic="${repertoire}/${nouveau_nom}.jpg"
		
		# Si un fichier porte deja le meme nom, on ajoute l'increment
		test -f $nouveau_fic && nouveau_fic="${repertoire}/${nouveau_nom}_${i2}.jpg"
		
		cp $fic $nouveau_fic
		
		# Verifier l'orientation de la photo
		orientation=`identify -verbose $nouveau_fic | grep 'exif:Orientation:' | sed -e s/'^.*\([1368]\).*$'/'\1'/g`
		
		if test "$orientation" != "1"
		then
			convert $nouveau_fic -auto-orient $nouveau_fic~ && mv $nouveau_fic~ $nouveau_fic
		fi
	fi
done

Bien évidemment, ce script est perfectible, et il ne répondra pas non plus à tous les besoins, notamment en terme de classement. Mais libre à chacun de se l’approprier et de le modifier.

Concaténer et convertir des traces GPS

Lorsque je suis amené à photographier sur plusieurs jours et dans plusieurs lieux, j’ai pris l’habitude d’utiliser un module GPS pour pouvoir ajouter les coordonnées géodésiques dans mes clichés. Dans la foulée, j’aime bien jeter un oeil aux traces GPS enregistrées par ce module dans Google Earth : effet « wow! » garanti quand le logiciel zoome sur la Terre puis sur les endroits que j’ai visité quelques jours plus tôt.

Mon module GPS est très bien fait, car il crée un nouveau fichier NMEA à chaque allumage. Si bien qu’on se retrouve vite avec de nombreux fichiers, un à plusieurs par jour. Pour synchroniser les photos efficacement, je trouve plus pratique de disposer d’un seul fichier, au format GPX pour utiliser dans digiKam. Par conséquent, à chaque retour de séjour, je répète les mêmes tâches :

  • Concaténer les fichiers NMEA produits sur la période du séjour
  • Convertir le fichier concaténé au format GPX pour synchroniser mes photos dans digiKam
  • Convertir une nouvelle fois ce fichier au format KML pour faire un peu mumuse dans Google Earth

Alors voici un petit script Bash qui permet de faire tout cela en un pouillème de seconde, avec l’aide de GPSBabel bien-sûr !

#! /bin/bash

# Nom du premier fichier NMEA
start=`ls WG*.log | head -1 | cut -d '.' -f1`

# Nom du dernier fichier NMEA
end=`ls WG*.log | tail -1 | cut -d '.' -f1`

fichier_sortie="${start}_${end}"

# Concatenation des fichiers NMEA
echo '@Sonygps/ver1.0/wgs-84/gps-cs3.0' > ${fichier_sortie}.nmea
cat WG*.log | grep -v '@Sonygps/ver1.0/wgs-84/gps-cs3.0' >> ${fichier_sortie}.nmea

# Conversion en GPX pour Digikam
gpsbabel -i nmea -f ${fichier_sortie}.nmea -o gpx -F ${fichier_sortie}.gpx

# Conversion en KML pour Google Earth et Google Maps
gpsbabel -i nmea -f ${fichier_sortie}.nmea -o kml -F ${fichier_sortie}.kml

exit 0

Encoder et encapsuler un code pour l’injecter

Les petits soucis que j’ai rencontrés dernièrement (cf. mon précédent billet), ont amené un de mes amis à me questionner sur la manière qu’un hacker peut « chiffrer » un code malveillant avant de l’injecter dans un script quelconque.

Tout d’abord, encoder un code source est une chose très répandue, qui est réalisée bien au-delà de la dissimulation d’un simple hack. Une grande partie des sites Internet étant programmés avec des langages interprétés tels que JavaScript, PHP ou ASP, souvent les éditeurs de sites Web utilisent cette méthode pour rendre illisible le code (ou du moins une partie) avant de livrer le produit à leur client. Par « illisible », il faut entendre que l’analyse du code par ce client nécessitera un travail de rétro-ingénierie très important, et aura par conséquent un coût non négligeable : c’est donc une façon pour l’éditeur de « fidéliser » sa clientèle, qui devra bien faire appel à lui pour dépatouiller le bordule… On aime ou on n’aime pas. Moi pas.

Puisque l’encodage d’un code exécutable est monnaie courante sur le Web, et notamment par les vilains petits pirates, pour ma démonstration je vais transposer ce mécanisme en Shell : ça n’a rien à voir, ça ne sert pas à grand chose (quoiqu’il doit bien y avoir un ou deux sites hébergés sur des serveurs en Shell), mais j’aime bien quand-même.

Dans mon exemple, la commande se chargera de récupérer la page « La Grande Question sur la vie, l’univers et le reste » et de l’enregistrer dans un fichier HTML. La commande correspondant à cela, et qui sera donc encodée, est comme ceci :

wget -q -O $HOME/42.html http://fr.wikipedia.org/wiki/
La_grande_question_sur_la_vie,_l%27univers_et_le_reste

Pour l’encoder, on peut par exemple utiliser la commande base64, un classique sur GNU/Linux. Cette commande convertit n’importe quoi en un code ASCII facilitant son inclusion dans un quelconque fichier texte. C’est d’ailleurs par cette méthode que les pièces jointes sont stockées dans les e-mails, ou bien encore que les images bitmap sont encapsulées dans les fichiers SVG. Ici, il suffit de soumettre à base64 la commande qui nous intéresse, en tant que vulgaire chaîne de caractères via un printf. Notez au passage, que le pour-cent (en rouge ci-dessous) a été doublé au préalable. La raison est très simple : la commande printf risque d’interpréter le %27 comme une succession de 27 espaces à écrire à la place.

printf 'wget -q -O $HOME/42.html http://fr.wikipedia.org/wiki/
La_grande_question_sur_la_vie,_l%%27univers_et_le_reste'
| base64 | tr -d 'n'

Ce qui va retourner cette chaîne ASCII totalement imbuvable :

d2dldCAtcSAtTyAkSE9NRS80Mi5odG1sIGh0dHA6Ly9mci53aWtpcGVkaWE
ub3JnL3dpa2kvTGFfZ3JhbmRlX3F1ZXN0aW9uX3N1cl9sYV92aWUsX2wlMj
d1bml2ZXJzX2V0X2xlX3Jlc3Rl

Cette chaîne sera celle à encapsuler dans les scripts Shell hôtes. Mais il n’est toutefois pas possible de l’injecter telle quelle, il est nécessaire de donner au script les moyens de décoder ce texte, puis de l’interpréter comme étant une commande exécutable. Tout d’abord, pour la décoder, il suffit de procéder de manière identique à son encodage, c’est-à-dire en utilisant la commande base64, mais cette fois-ci en y précisant l’argument « -d » (comme l’anglais « decode », CQFD).

printf 'd2dldCAtcSAtTyAkSE9NRS80Mi5odG1sIGh0dHA6Ly9mci53aWtpcGV
kaWEub3JnL3dpa2kvTGFfZ3JhbmRlX3F1ZXN0aW9uX3N1cl9sYV92aWUsX2wlMj
d1bml2ZXJzX2V0X2xlX3Jlc3Rl' | base64 -d

La commande base64 -d nous a permis de recouvrer la chaîne de caractères initiale. Il faut donc maintenant l’interpréter en tant que commande pour pouvoir l’exécuter. C’est le rôle de la commande eval, dans laquelle il suffit d’encapsuler le printf et le base64 -d.

eval `printf 'd2dldCAtcSAtTyAkSE9NRS80Mi5odG1sIGh0dHA6Ly9mci53aWtpcGV
kaWEub3JnL3dpa2kvTGFfZ3JhbmRlX3F1ZXN0aW9uX3N1cl9sYV92aWUsX2wlMj
d1bml2ZXJzX2V0X2xlX3Jlc3Rl' | base64 -d`

Cette fois c’est terminé. Cette ligne de code est directement utilisable dans n’importe quel script Shell. A l’exécution de ce script, le fichier 42.html sera alors enregistré à la racine du répertoire utilisateur courant.

Cette méthode est identique quel que soit le langage de programmation employé. Evidemment, l’écriture du code à injecter dépendra du programme visé, puisque pour un site Web en PHP, il faudra nécessairement injecter du code PHP exécutant du PHP (pour écrire du HTML et du JavaScript dans le cadre d’un piratage), et non du Shell exécutant du Shell.