Bash scripting - espansioni non quotabili

Da Guide@Debianizzati.Org.
Versione del 22 lug 2014 alle 12:32 di S3v (discussione | contributi) (- template "Versioni compatibili")
Vai alla navigazione Vai alla ricerca
Bash scripting

Sommario

  1. Introduzione
  2. Comandi essenziali
  3. Variabili (stringhe)
  4. Caratteri di escape, apici e virgolette
  5. Espansioni in stringhe quotate
  6. Espansioni non quotabili
  7. Istruzioni composte
  8. Funzioni
  9. File descriptor e redirezioni
  10. Segnali

Espansioni non quotabili

Le espansioni trattate nelle sezioni successive avvengono unicamente in stringhe non quotate, almeno limitatamente ai caratteri speciali che le attivano, e sono attivabili soltanto da caratteri diversi dal $.

Le loro priorità sono tutte diverse: l'espansione di parentesi è quella maggiore, seguita dalla tilda, da tutte le espansioni attivabili con $ (quelle quotabili) e infine dall'espansione di percorso.

Inoltre l'espansione di tilda, che è sempre espansa in una singola stringa, è l'unica possibile in un'assegnazione tra le espansioni non quotabili.

Espansione di tilda

Sintassi:

  • ~ si espande alla home, se non è quotata (equivalente all'uso di $HOME, che può essere quotata);
  • ~utente si espande alla home di un dato utente, se esiste, ma la stringa non può essere quotata né essere una variabile.

Si distingue dall'espansione di percorso perché:

  • si espande sempre a una singola stringa;
  • può essere espansa in un'assegnazione, se non è quotata;
  • ha priorità maggiore delle espansioni quotabili; per cui, se assegnata quotata a una variabile, non sarà espansa quando si accede alla variabile.

Esempi:

var=~                # assegno la home dell'utente a $var
var=$HOME            # equivalente (ma più chiaro)
var="~"              # assegno ~ a $var
printf %s\\n "$var"  # stampo ~
printf %s\\n $var    # equivalente (nessuna espansione)
var=~root            # assegno a var la home di root
printf %s\\n ~       # stampo la home dell'utente
printf %s\\n "$HOME" # equivalente
printf %s\\n "~"     # stampo ~
printf %s\\n ~root   # stampo la home di root
printf %s\\n ~fdsfd  # stampo ~fdsfd (l'utente fdsfd non esiste)

Espansione di percorso

Info.png File
Su Unix e Unix-like per file si può intendere sia un file regolare, ma anche una directory, un link simbolico, una pipe, un socket, un device, ecc...

L'espansione di percorso avviene unicamente in base al nome del file, che dev'essere univoco in una cartella, e non al tipo di file. L'unica eccezione sono le directory, perché possono essere identificate anche con lo slash (/) finale.


Le espansioni di percorso sono possibili solo se i caratteri speciali che la consentono (* ? [ ]) non sono racchiusi tra virgolette, apici o preceduti da /. È sempre consigliabile racchiudere tutto il resto tra virgolette, per non permettere espansioni accidentali.

L'espansione non è possibile, direttamente, in un'assegnazione. Avendo la priorità più bassa, contrariamente all'espansione di tilda può avvenire anche in seguito all'espansione di una variabile (e con ogni altra espansione), se non è quotata:

var="./*"           # assegno ./* a $var
var=./*             # come sopra (nessuna espansione in un'assegnazione)
printf %s\\n "$var" # stampa letteralmente ./*
printf %s\\n $var   # stampa la lista di tutti i file non nascosti
                    # nella directory corrente, oppure ./* se è vuota

Sintassi (prefisso e suffisso possono essere omessi, o essere variabili da espandere):

  • prefisso?suffisso sostituisce un singolo carattere di un nome di file, con tutti quelli possibili che combaciano con le due stringhe date, ma di default tranne il . iniziale se manca il prefisso;
  • prefisso*suffisso può sostituire tutti i caratteri nei nomi di file, ma di default tranne quelli nascosti se manca il prefisso (ossia quelli inizianti con .);
  • prefisso[classe]suffisso sostituisce un singolo carattere di un nome di file, con tutti quelli possibili appartenenti alla classe data e che combaciano con le due stringhe. La classe può contenere:
    • una lista di caratteri, tutti attaccati, per sostituirne uno qualsiasi della lista;
    • un intervallo composto da due caratteri separati da un trattino -, per sostituirne uno qualsiasi dell'intervallo in base al loro valore ASCII;
    • un ! iniziale per farne il complemento, ossia sostituirne uno qualsiasi non presente nella classe;
    • il carattere ! può essere contenuto per il suo valore letterale in una classe purché non in prima posizione, mentre - è considerato letteralmente soltanto in prima posizione (o seconda se dopo !) e in ultima posizione.

Se un nome di file non include il percorso assoluto (iniziante con la directory radice /) o relativo (iniziante con ./ oppure ../, dalla directory corrente e da quella superiore rispettivamente), di default si assume che sia nella directory corrente. Tuttavia per evitare ambiguità con i nomi delle opzioni di alcuni comandi, in presenza di possibili nomi di file inizianti con il trattino -, in particolare se la parte iniziale del file è generata dall'espansione di percorso, è sempre bene rendere esplicito il percorso relativo premettendo ./ al nome del file.

Esempi (nella directory corrente):

  • ./file.??? si espande a tutti i file con nome "file" e con una qualsiasi estensione di tre caratteri;
  • ./???.ext si espande a tutti i file con nomi di tre caratteri (salvo i file nascosti, ossia con . iniziale) ed estensione ext;
  • ./* si espande a tutti i file non nascosti nella directory corrente;
  • ./*.txt espande a tutti i file con estensione .txt (NOTA: anche directory e qualsiasi file non regolare avente tale estensione);
  • ./*."${estensione}" espande dopo aver espanso la variabile (contrariamente a ~), che può anche essere quotata;
  • "./${nome}"* espande a tutti i file inizianti con ${nome};
  • ./*/ espande a tutte le directory non nascoste;
  • ./.* espande a tutti i file nascosti (ATTENZIONE: comprese "." e "..", ossia directory corrente e superiore);
  • [a-zA-Z]* espande a tutti i file inizianti con una lettera qualsiasi (nessun file nascosto perché il punto non è nella classe);
  • ./*.[tT][xX][tT] espande a tutti i file con estensione txt (ignorando maiuscole e minuscole);
  • ./.[!.]* espande a tutti i file nascosti di almeno due caratteri in cui il secondo non è un punto (non espande a . e .., ma nemmeno a possibili file nascosti inizianti con ..);
  • ./..?* espande a tutti i file nascosti di almeno tre caratteri in cui il secondo non è un punto (tutti i file nascosti saltati dal precedente, ma sempre escludendo . e ..);
  • ./.[!.]* ./..?* espande a tutti i file nascosti, esclusi . e .. (POSIX).

È importante sapere che, se nessun file combacia con un dato pattern, allora l'espansione non viene effettuata e i caratteri mantengono il loro valore letterale. E inoltre *, ?, [ e ] sono caratteri validi per un nome di file.

L'esistenza di file ottenuti da tali espansioni va pertanto sempre controllata, impiegando l'espansione per generare una lista di stringhe all'interno di un ciclo for, ed effettuando poi il controllo di esistenza su ognuna:

for file in ./*; do
   if [ -e "$file" ]; then
      ...
   fi
done

Si faccia attenzione che tutti i pattern delle espansioni di percorso, e non solo quelli composti da *, possono generare più stringhe, anche se sostituiscono un singolo carattere in presenza di fili multipli con lo stesso prefisso e/o suffisso (per esempio il pattern a?c può espandersi alla lista abc aBC acc, se esistono questi tre file).

In alternativa il comportamento di default dell'espansione può essere cambiato in bash (non POSIX), tramite shopt -s:

  • nullglob espande a "niente" se non trova nessun file con un dato pattern, rendendo superfluo il controllo sull'esistenza;
  • dotglob espande ai file nascosti (ma non a . e ..);
  • nocaseglob espande il percorso a tutte le corrispondenze trovate, ignorando maiuscole e minuscole (case-insensitive).

Per esempio per espandere a tutti i file, compresi quelli nascosti:

shopt -s dotglob nullglob
for file in ./*; do
   ...
done

Esempio: cambiare l'estensione ai file regolari

Rinomina tutti i file regolari *.txt della directory corrente in *.log, tramite il comando esterno mv:

for f in ./*.txt; do
   if [ -f "$f" ]; then
      mv -- "$f" "${f%txt}log"
   fi
done

Si noti che utilizzando [ -f ... ] in luogo di [ -e ... ], si saltano anche tutti i file che non sono regolari, e che potrebbero essere restituiti dall'espansione di percorso.

Espansione di parentesi (graffa)

In bash (non POSIX) se i caratteri { e } non sono quotati, e non sono preceduti dal carattere di escape \, possono essere espansi con due diverse sintassi per generare una lista di stringhe. E più espansioni di parentesi possono essere annidate.

Questa espansione avviene prima di tutte le altre, e il risultato può passare per tutte le altre espansioni. Non può avvenire in un'assegnazione, se non all'interno di altre espansioni.

Con indici di intervallo

Sintassi: prefisso{x..y[..z]}suffisso

L'espansione avviene per tutte le stringhe nell'intervallo compreso da "prefissoxsuffisso" fino a "prefissoysuffisso", con incrementi di 1 (o z se specificato). Le stringhe prefisso e suffisso possono essere omesse, o essere variabili (anche quotate, purché le graffe non lo siano), mentre x e y (e z, se presente) devono essere determinati valori e non possono essere variabili:

  • {x..y} dove x e y sono due interi;
  • {x..y..z} dove x, y e z sono tre interi;
  • {a..b} dove a e b sono due caratteri;
  • {a..b..z} dove a e b sono due caratteri, e z è un intero.

Per esempio:

# crea un file temporaneo, associa il percorso a $tmp_file
tmp_file=$(tempfile)
# crea altri dieci file temporanei (.0, .1, .., .9) con lo stesso nome
touch -- "$tmp_file".{0..9}

Con lista di stringhe

Sintassi: prefisso{stringa1,stringa2,...}suffisso

L'espansione avviene per tutte le stringhe nella lista, racchiudendole tra il prefisso e il suffisso dati, se presenti. Il prefisso, il suffisso e tutte le stringhe possono essere variabili, anche quotate, purché non siano quotate le graffe e le virgole interne.

Questa espansione è effettuata prima di tutte le altre, e il risultato dell'espansione se non quotato può quindi subire ulteriori espansioni. Per esempio per effettuare un'operazione sui file nella cartella corrente che hanno una data estensione, si può scrivere:

for file in ./*.{odt,abw,txt,rtf,doc}; do
   if [ -e "$file" ]; then
      ...
   fi
done

e la prima riga è equivalente a:

for file in ./*.odt ./*.abw ./*.txt ./*.rtf ./*.doc; do


Guida scritta da: HAL 9000 13:00, 22 lug 2014 (CEST) Swirl-auth20.png Debianized 20%
Estesa da:
Verificata da:

Verificare ed estendere la guida | Cos'è una guida Debianized