9.2. Manipolazione di stringhe

Bash supporta un numero sorprendentemente elevato di operazioni per la manipolazione delle stringhe. Purtroppo, questi strumenti mancano di organizzazione e razionalizzazione. Alcuni sono un sotto insieme della sostituzione di parametro, altri appartengono alle funzionalità del comando UNIX expr. Tutto questo si traduce in una sintassi dei comandi incoerente ed in una sovrapposizione di funzionalità, per non parlare della confusione.

Lunghezza della stringa

${#stringa}

expr length $stringa

expr "$stringa" : '.*'

stringaZ=abcABC123ABCabc

echo ${#stringaZ}                 # 15
echo `expr length $stringaZ`      # 15
echo `expr "$stringaZ" : '.*'`    # 15

Esempio 9-10. Inserire una riga vuota tra i paragrafi di un file di testo

#!/bin/bash
# paragraph-space.sh

#  Inserisce una riga vuota tra i paragrafi di un file di testo con 
#+ spaziatura semplice.
#  Utilizzo: $0 <NOMEFILE

LUNMIN=45        # Potrebbe rendersi necessario modificare questo valore.
#  Si assume che le righe di lunghezza inferiore a $LUNMIN caratteri
#+ siano le ultime dei paragrafi.

while read riga  # Per tutte le righe del file di input...
do
  echo "$riga"   # Visualizza la riga.

  lun=${#riga}
  if [ "$lun" -lt "$LUNMIN" ]
    then echo    # Aggiunge la riga bianca.
  fi  
done

exit 0

Lunghezza della sottostringa verificata nella parte iniziale della stringa

expr match "$stringa" '$sottostringa'

$sottostringa è una espressione regolare.

expr "$stringa" : '$sottostringa'

$sottostringa è un'espressione regolare.

stringaZ=abcABC123ABCabc
#        |------|

echo `expr match "$stringaZ" 'abc[A-Z]*.2'`   # 8
echo `expr "$stringaZ" : 'abc[A-Z]*.2'`       # 8

Indice

expr index $stringa $sottostringa

Numero di posizione in $stringa del primo carattere presente in $sottostringa che è stato verificato.

stringaZ=abcABC123ABCabc
echo `expr index "$stringaZ" C12`             # 6
                                              # Posizione di C.

echo `expr index "$stringaZ" 1c`              # 3
# 'c' (in terza posizione) viene verificato prima di '1'.

È quasi uguale alla funzione strchr() del C.

Estrazione di sottostringa

${stringa:posizione}

Estrae la sottostringa da $stringa iniziando da $posizione.

Se il parametro $stringa è "*" o "@", allora vengono estratti i parametri posizionali, [1] iniziando da $posizione.

${stringa:posizione:lunghezza}

Estrae una sottostringa di $lunghezza caratteri da $stringa iniziando da $posizione.

stringaZ=abcABC123ABCabc
#        0123456789.....
#        L'indicizzazione inizia da 0.

echo ${stringaZ:0}                            # abcABC123ABCabc
echo ${stringaZ:1}                            # bcABC123ABCabc
echo ${stringaZ:7}                            # 23ABCabc

echo ${stringaZ:7:3}                          # 23A
                                              # Sottostringa di tre caratteri.



# È possibile indicizzare partendo dalla fine della stringa?

echo ${stringaZ:-4}                           # abcABC123ABCabc
# Restituisce l'intera stringa, come con ${parametro:-default}.
# Tuttavia . . .

echo ${stringaZ:(-4)}                         # Cabc
echo ${stringaZ: -4}                          # Cabc
# Ora funziona.
# Le parentesi, o l'aggiunta di uno spazio, "preservano" il parametro negativo.

# Grazie a Dan Jacobson per averlo evidenziato.

Se il parametro $stringa è "*" o "@", vengono estratti un massimo di $lunghezza parametri posizionali, iniziando da $posizione.

echo ${*:2}          #  Visualizza tutti i parametri iniziando dal secondo.
echo ${@:2}          #  Come prima.

echo ${*:2:3}        #  Visualizza tre parametri posizionali 
                     #+ iniziando dal secondo.

expr substr $stringa $posizione $lunghezza

Estrae $lunghezza caratteri da $stringa iniziando da $posizione.

stringaZ=abcABC123ABCabc
#        123456789......
#        L'indicizzazione inizia da 1.

echo `expr substr $stringaZ 1 2`              # ab
echo `expr substr $stringaZ 4 3`              # ABC

expr match "$stringa" '\($sottostringa\)'

Estrae $sottostringa dalla parte iniziale di $stringa, dove $sottostringa è una espressione regolare.

expr "$stringa" : '\($sottostringa\)'

Estrae $sottostringa dalla parte iniziale di $stringa, dove $sottostringa è un'espressione regolare.

stringaZ=abcABC123ABCabc
#        =======

echo `expr match "$stringaZ" '\(.[b-c]*[A-Z]..[0-9]\)'`   # abcABC1
echo `expr "$stringaZ" : '\(.[b-c]*[A-Z]..[0-9]\)'`       # abcABC1
echo `expr "$stringaZ" : '\(.......\)'`                   # abcABC1
# Tutte le forme precedenti danno lo stesso risultato.

expr match "$stringa" '.*\($sottostringa\)'

Estrae $sottostringa dalla parte finale di $stringa, dove $sottostringa è un'espressione regolare.

expr "$stringa" : '.*\($sottostringa\)'

Estrae $sottostringa dalla parte finale di $stringa, dove $sottostringa è un'espressione regolare.

stringaZ=abcABC123ABCabc
#                 ======

echo `expr match "$stringaZ" '.*\([A-C][A-C][A-C][a-c]*\)'`    # ABCabc
echo `expr "$stringaZ" : '.*\(......\)'`                       # ABCabc

Rimozione di sottostringa

${stringa#sottostringa}

Toglie l'occorrenza più breve di $sottostringa dalla parte iniziale di $stringa.

${stringa##sottostringa}

Toglie l'occorrenza più lunga di $sottostringa dalla parte iniziale di $stringa.

stringaZ=abcABC123ABCabc
#        |----|
#        |----------|

echo ${stringaZ#a*C}      # 123ABCabc
# È stata tolta l'occorrenza più breve compresa tra 'a' e 'C'.

echo ${stringaZ##a*C}     # abc
# È stata tolta l'occorrenza più lunga compresa tra 'a' e 'C'.

${stringa%sottostringa}

Toglie l'occorrenza più breve di $sottostringa dalla parte finale di $stringa.

Per esempio:

# Rinomina tutti i file in $PWD con suffisso "TXT" al suffisso "txt".
# Esempio, "file1.TXT" diventa "file1.txt" . . .
			
SUFF=TXT
suff=txt
			
for i in $(ls *.$SUFF)
do
mv -f $i ${i%.$SUFF}.$suff
#  Lascia inalterato tutto *tranne* la corrispondenza più breve
#+ ad iniziare dalla parte destra della variabile $i . . .
done ### Volendo, si poteva condensare in una "unica riga".
			
# Grazie a Rory Winston.

${stringa%%sottostringa}

Toglie l'occorrenza più lunga di $sottostringa dalla parte finale di $stringa.

stringaZ=abcABC123ABCabc
#                     ||
#         |------------|

echo ${stringaZ%b*c}      # abcABC123ABCa
#  È stata tolta l'occorrenza più breve compresa 
#+ tra 'b' e 'c', dalla fine di $stringaZ.

echo ${stringaZ%%b*c}     # a
#  È stata tolta l'occorrenza più lunga compresa
#+ tra 'b' e 'c', dalla fine di $stringaZ.

È un operatore utile per generare nomi di file.

Esempio 9-11. Conversione di formato di file grafici e modifica del nome dei file

#!/bin/bash
#  cvt.sh:
#  Converte tutti i file immagine MacPaint, in una directory data, 
#+ nel formato "pbm".

#  Viene utilizzato l'eseguibile "macptopbm" del pacchetto "netpbm",
#+ mantenuto da Brian Henderson (bryanh@giraffe-data.com). 
#  Netpbm di solito è compreso nell'installazione standard della 
#+ maggior parte delle distribuzioni Linux.

OPERAZIONE=macptopbm
ESTENSIONE=pbm          # Nuova estensione dei nomi dei file.

if [ -n "$1" ]
then
  directory=$1      #  Se viene fornito il nome di una directory come 
                    #+ argomento dello script...
else
  directory=$PWD    # Altrimenti viene utilizzata la directory corrente.
fi  
  
#  Si assume che tutti i file immagine nella directory siano dei MacPaint,
#+ con nomi aventi estensione ".mac" 

for file in $directory/*    # Globbing dei nomi dei file.
do
  nomefile=${file%.*c}      #  Toglie l'estensione ".mac" dal nome del file
                            #+ ('.*c' verifica tutto tra '.' e 'c', compresi).
  $OPERAZIONE $file > "$nomefile.$ESTENSIONE"
                            #  Converte e redirige il file con una nuova 
                            #+ estensione.
  rm -f $file               #  Cancella i file originali dopo la conversione.
  echo "$nomefile.$ESTENSIONE"  # Visualizza quello che avviene allo stdout.
done

exit 0

# Esercizio:
# ----------
#  Così com'è, lo script converte "tutti" i file presenti nella
#+ directory di lavoro corrente.
#  Modificatelo in modo che agisca "solo" sui file con estensione ".mac".

Esempio 9-12. Conversione di file in audio streaming nel formato ogg

#!/bin/bash
# ra2ogg.sh: Converte i file in streaming audio (*.ra) al formato ogg.

# Usa il programma "mplayer":
#      http://www.mplayerhq.hu/homepage
#      Affinché lo script funzioni potrebbe rendersi necessaria 
#+     l'installazione di codec appropriati.
# Usa le librerie "ogg" e "oggenc":
#      http://www.xiph.org/


OFILEPREF=${1%%ra}      # Toglie il suffisso "ra".
OFILESUFF=wav           # Suffisso dei file wav.
OUTFILE="$OFILEPREF""$OFILESUFF"
E_ERR_ARG=65

if [ -z "$1" ]          # Deve essere fornito il nome del file da convertire.
then
  echo "Utilizzo: `basename $0` [nomefile]"
  exit $E_ERR_ARG
fi


##########################################################################
mplayer "$1" -ao pcm:file=$OUTFILE
oggenc "$OUTFILE"  # oggenc aggiunge automaticamente la corretta estensione.
##########################################################################

rm "$OUTFILE"      # Cancella il file *.wav intermedio.
                   # Se volete conservarlo, commentate la riga precedente.

exit $?

#  Nota:
#  ----
#  In un sito web cliccando sul file di streaming audio *.ram
#+ solitamente viene scaricato solo l'URL del vero file audio, il file *.ra.
#  Si può allora usare "wget", o un programma analogo,
#+ per scaricare direttamente il file *.ra.


#  Esercizi:
#  --------
#  Così com'è lo script converte solamente i file con estensione *.ra.
#   Rendetelo più flessibile in modo che possa accettare anche file con 
#+ estensione *.ram o altra.
#
#  Se siete veramente ambiziosi espandete lo  script
#+ in modo che esegua in automatico sia il download che la conversione dei file 
#+ di streaming audio.
#  Dato un URL, scarica in modalitàbatch gli streaming audio (usando "wget")
#+ e li converte.

Una semplice emulazione di getopt utilizzando i costrutti di estrazione di sottostringa.

Esempio 9-13. Emulare getopt

#!/bin/bash
# getopt-simple.sh
# Autore: Chris Morgan
# Usato in Guida ASB con il suo consenso.


semplice_getopt()
{
    echo "semplice_getopt()"
    echo "I parametri sono '$*'"
    until [ -z "$1" ]
    do
      echo "Elaborazione parametro di: '$1'"
      if [ ${1:0:1} = '/' ]
      then
          tmp=${1:1}               # Elinina le '/' iniziali . . .
          parametro=${tmp%%=*}     # Estrae il nome.
          valore=${tmp##*=}        # Estrae il valore.
          echo "Parametro: '$parametro', valore: '$valore'"
          eval $parametro=$valore
      fi
      shift
    done
}

# Passiamo tutte le opzioni a semplice_getopt().
semplice_getopt $*

echo "verifica '$verifica'"
echo "verifica2 '$verifica2'"

exit 0

---

sh getopt_example.sh /verifica=valore1 /verifica2=valore2

I parametri sono '/verifica=valore1 /verifica2=valore2'
Elaborazione parametro di: '/verifica=valore1'
Parametro: 'verifica', valore: 'valore1'
Elaborazione parametro di: '/verifica2=valore2'
Parametro: 'verifica2', valore: 'valore2'
verifica 'valore1'
verifica2 'valore2'

Sostituzione di sottostringa

${stringa/sottostringa/sostituto}

Sostituisce la prima occorrenza di $sottostringa con $sostituto.

${stringa//sottostringa/sostituto}

Sostituisce tutte le occorrenze di $sottostringa con $sostituto.

stringaZ=abcABC123ABCabc

echo ${stringaZ/abc/xyz}   # xyzABC123ABCabc
                           # Sostituisce la prima occorrenza di 'abc' con 'xyz'.

echo ${stringaZ//abc/xyz}  # xyzABC123ABCxyz
                           # Sostituisce tutte le occorrenze di 'abc' con 'xyz'.

${stringa/#sottostringa/sostituto}

Se $sottostringa viene verificata all'inizio di $stringa, allora $sostituto rimpiazza $sottostringa.

${stringa/%sottostringa/sostituto}

Se $sottostringa viene verificata alla fine di $stringa, allora $sostituto rimpiazza $sottostringa.

stringaZ=abcABC123ABCabc

echo ${stringaZ/#abc/XYZ}     # XYZABC123ABCabc
                              # Sostituisce l'occorrenza iniziale 'abc'con'XYZ'.

echo ${stringaZ/%abc/XYZ}     # abcABC123ABCXYZ
                              # Sostituisce l'occorrenza finale 'abc' con 'XYZ'.

9.2.1. Manipolare stringhe con awk

Uno script Bash può ricorrere alle capacità di manipolazione delle stringhe di awk, come alternativa all'utilizzo dei propri operatori builtin.

Esempio 9-14. Modi alternativi di estrarre sottostringhe

#!/bin/bash
# substring-extraction.sh

Stringa=23skidoo1
#       012345678    Bash
#       123456789    awk
# Fate attenzione al diverso sistema di indicizzazione della stringa:
# Bash numera il primo carattere della stringa con '0'.
# Awk  numera il primo carattere della stringa con '1'.

echo ${Stringa:2:4} # posizione 3 (0-1-2), 4 caratteri di lunghezza
                                           # skid

#  L'equivalente awk di ${stringa:pos:lunghezza} è 
#+ substr(stringa,pos,lunghezza).
echo | awk '
{ print substr("'"${Stringa}"'",3,4)       # skid
}
'
#  Collegando ad awk un semplice comando "echo" gli viene dato un 
#+ input posticcio, in questo modo non diventa più necessario 
#+ fornirgli il nome di un file.

exit 0

9.2.2. Ulteriori approfondimenti

Per altro materiale sulla manipolazione delle stringhe negli script, si faccia riferimento alla Sezione 9.3 e all' importante sezione relativa all'elenco dei comandi expr. Per gli script d'esempio, si veda:

  1. Esempio 15-9

  2. Esempio 9-17

  3. Esempio 9-18

  4. Esempio 9-19

  5. Esempio 9-21

Note

[1]

Questo vale sia per gli argomenti da riga di comando che per i parametri passati ad una funzione.