Pense-bête manipulation de fichiers et de chaînes de caractères

English   |   Source

Une des forces importantes des systèmes Unix est la manipulation de fichiers et les manipulations de chaînes de caractères sans avoir à passer par des logiciels exotiques. Il n'est pas toujours aisé de se souvenir de toutes les commandes utiles, d'où la raison de ce pense-bête.

Jouer avec les chaines de caractères en script bash

On peut facilement manipuler les chaînes de caractères en script bash en faisant la chose suivante :

  • ${var: :n}, si on veut récupérer les n premiers caractères de la chaîne ;
  • ${var: n}, si on veut récupérer les derniers caractères à partir du n ième de la chaîne ;
  • ${var: -n}, si on veut récupérer les n derniers caractères de la chaîne ;
  • ${var: n:m}, si on veut récupérer les caractères entre le n ième et le mième caractère de la chaîne.

Voilà un exemple simple où l'on veut copier des données qui sont dans une série de répertoires dans une autre série de répertoires, dont seule une partie du nom change :

for i in $(ls rep_s1*}; do cp $i/{data1, data2} rep_s2${i: 5}; done

Dans la même idée, on peut facilement récupérer ce qui se situe avant ou après un certain pattern ; par exemple :

  • ${fname##*.} pour récupérer l'extension d'un fichier ;
  • ${fname%.*} pour récupérer ce qu'il y a avant l'extension ;
  • ${fname%%.*} pour une version greedy.

Transposition de lignes et colonnes en script shell

Le script suivant est améliorable, mais ça fonctionne. Il prend un fichier en entrée, fichier dont les colonnes doivent être séparées par des espaces.

#!/bin/bash

file=$1

# get the number of columns
num=$(awk -F" " 'NR==1 { print NF }' $file)
echo $num

# transpose
i=1
while (( $i <= $num ))
do
    newline=''
    for val in $(cut -d" " -f$i $file)
    do
    newline=$newline$val" "
    done
    nline=`echo ${newline%?}`
    echo $nline >> tmpdata
    (( i = i + 1 ))
done
mv tmpdata test.dat

Sinon, dans l'hypothétique cas où le fichier contient une unique colonne, une solution élégante est d'utiliser tr :

~$ tr -s '\n' ' ' < [oldfile] > [newfile]

Récupérer une partie d'un path

trois possibilités (les exemples suivants sont donnés en cherchant à enlever les deux dernières composantes du path d'origine) :

  • la façon « j'apprends à coder en bash » :
function RootDir(){
    listdir=($(echo $1 | tr '/' ' '))
    length=${#listdir[@]}
    length=$(printf $(($length - 3)))
    DIR=
    i=0
    while [ $i -le $length ]; do
        DIR=$DIR/${listdir[$i]}
        i=$(($i + 1))
    done
    echo $DIR
}

newpath=$(RootDir $DIR)
  • la façon « je découvre dirname » :
newpath=$(dirname $(dirname $DIR))
  • et enfin, de manière intelligente :
newpath=${$DIR%/*/*}

Compléter un nombre par des zéros en script bash

Un one-liner pour renommer massivement des fichiers en les numérotant, en utilisant bc bien sûr :

j=1; for i in $(ls *png); do cp $i slice_yz-`printf %02d $j`.png; j=$(echo "$j+1" | bc); done

Renommer des fichiers pour ajouter des 0

Ça peut être utile pour manipuler des fichiers numérotés en 001, 010, 100. C'est possible avec rename (sur les distributions Debian-like), en utilisant des expressions régulières :

rename 's/\-([1-9]\.)/\-0$1/g' *

Il suffit sinon de faire quelque chose comme ça avec printf :

for i in $(ls -d *); do for j in $(more $i/list.dat); do scp -r user@remote:/path/$i/output_`printf %05d $j` $i/.; done; done

attention aux guillemets qui sont des ` et non pas des ' !

Fusion de deux fichiers

Il est possible de fusionner des fichiers avec awk en sélectionnant éventuellement des colonnes :

pr -m -t -s\ file1 file2 | awk '{print($1 $5 $3)}' > fileout

Le pr -m -t -s\ `` (ne pas oublier l'espace après l'option ``-s) peut être remplacé par un paste.

Conversion de fichiers musicaux

C'est toujours préférable d'utiliser des formats sans perte, mais tous les lecteurs ne sont pas capables de les lire, quand la taille de ces fichiers n'est pas trop importante. Il est possible avec lame de faire des conversions faciles de fichiers musicaux.

Conversion wave/MP3

for file in *.wav; do lame -h -b [bitrate] "$file" "${file%.wav}.mp3"; done

Conversion FLAC/MP3

for file in *.flac; do $(flac -cd "$file" | lame -h -b [bitrate] - "${file%.flac}.mp3"); done