Translation(s): English - Français - Italiano

Trucchi con gli script di shell

Notare: #debian non è necessariamente il posto migliore per trovare aiuto con gli script shell. Se si ha un problema con uno script che ha a che fare con bash (o la shell Bourne), si potrebbe provare piuttosto #bash. La FAQ di #bash è all'indirizzo http://mywiki.wooledge.org/BashFAQ e contiene molti più trucchi di quanto non faccia questa pagina.

Come vedere quanto spazio su disco è rimasto?

df 
df -h  per un formato intelligibile in mega & giga

Come vedere dove sono tutti i file grandi? Il filesystem è pieno.

cd /qualcheparte
du -sk * | sort -n
# Ripetere quante volte è necessario

Se si vuole vedere quali sono le directory grandi invece dei file, si può fare:

du -x /qualcheparte | sort -n | tail -10
# Questo mostra le 10 più grandi directory in /qualcheparte

Un altro modo:

find /qualcheparte -size +2000k -ls
# Questo mostra tutti i file più grandi di 2000 kilobyte in /qualcheparte

Se si vuole tenere i propri file .dev, si possono filtrarli fuori dall'elenco

find / -size +2000k ! -name "*.deb" -ls
# Questo mostra tutti i file nell'intero filesystem più grandi di 2000 kilobyte che non sono pacchetti Debian

Una directory è piena di MP3... come rinominarli tutti in modo da avere segni di sottolineatura al posto degli spazi?

rename 's/ /_/g' *.mp3

Il comando rename non è un comando generale di Unix, ma è incluso con Perl, che è naturalmente installato in modo predefinito in Debian.

Come farlo in modo ricorsivo?

È un po' complicato. Si può ottenere usando find un elenco di file da passare a rename, ma se vengono rinominate anche alcune delle directory rename non può tenerne traccia. Si deve usare l'opzione -depth con find per assicurarsi che ciascun file sia rinominato prima della directory che lo contiene.

cd /qualcheparte
find . -depth -name '* *' -type f -print0 | xargs -r0 rename 's/ /_/g'

Come rinominare (ricorsivamente) tutti i file sostituendo la maiuscole con minuscole?

Ancora una volta questo è complicato se le directory stesse hanno lettere maiuscole all'interno dei loro nomi. Assumiamo per il momento che non le abbiano. Allora usare:

cd /qualcheparte
find . -name '*[A-Z]*' -type f -print0 | xargs -0 rename 'y/A-Z/a-z/'

Sfortunatamente, ogni volta che questo quesito viene riproposto, ciò non è sufficiente: chi chede aiuto di solito ha anche lettere maiuscole nei nomi di directory. Perciò potrebbe essere richiesto qualcosa di simile a:

cd /qualcheparte
find . -type d -depth -name '*[A-Z]*' -print |
  while read dir; do dname="$(dirname $dir)"; bname="$(basename $dir)";
  newbname="$(echo $bname | tr [:upper:] [:lower:])"; mv "$dir" "$dname/$newbname"; done
# Che rinomina le directory. L'opzione -depth lo fa iniziare prima dalle directory più profonde, in modo che
# A viene rinominata quando A/B è già stata rinominata in A/b.
find . -name '*[A-Z]*' -type f -print0 | xargs -0 rename 'y/A-Z/a-z/'

Si noti che lo script più complesso qui sopra è imperfetto: fallisce se ci sono caratteri "a capo" nel nome delle directory e può anche fallire, in talune circostanze, se i nomi di file o directory contengono spazi. Usarlo a proprio rischio e pericolo. (Suggerimento: se si vuole testarlo prima di affidarvicisi, aggiungere un echo davanti a mv.)

In realtà questa soluzione più semplice potrebbe funzionare meglio:

find /qualcheparte -depth -name '*[A-Z]*' -print0 | xargs -r0 rename 'y/A-Z/a-z/'

Anche qui potrebbe essere una buona idea mettere un echo prima del comando rename per testarlo prima di eseguirlo realmente.

Come rimuovere i file che iniziano con "-"?

unlink -pippo

Ci sono tre modi diversi:

rm -- -foo
rm ./-foo
usando 'mc' e premendo F8 sul file interessato

Questi due trucchi funzionano con quasi qualunque strumento a riga di comando, non solo con rm.

Come monitorare qualche cosa in tempo reale?

Se è un file di log, seguire l'ultima parte del file:

tail -f /var/log/messages

oppure

less +F /var/log/messages

Se è un comando generico, usare watch:

watch -n 1 ls -l ~/qualche/file

Questo funziona bene per monitorare un firewall con iptables / tc o cose simili.

Come ottenere solo il nome file da un percorso completo di file?

Ci sono due modi:

basename /percorso/al/file
pippo=/percorso/al/file ; echo ${pippo##*/}

Come testare se una directory contiene o meno file?

if [ "$(ls -A qualchedir)" ]; then
    echo "Contiene file"
fi

Notare: un singolo argomento all'interno delle parentesi quadre equivale a [ -n argomento ]. Sembra non esserci un modo pulito per fare questo usando solo i comandi interni della shell. (C'è stato un tentativo che ci è andato vicino, ma è fallito in presenza di un file chiamato * all'interno della directory.)

Un altro metodo:

if [ "$(ls -A qualchedir | wc -l)" -gt 0 ]; then
    echo "Trovati file"
fi

Un metodo più corto: [ $(ls -A qualchedir) ] && echo "La directory non e' vuota"

Notare: il doppio carattere & funziona come un AND booleano, perciò non è necessario usare "if".

Come avviare un processo sullo sfondo?

Basta aggiungere in fondo una e commerciale (&):

cp grandefile grandefile2 &

o premere Ctrl+Z mentre il comando è in esecuzione (ciò lo fermerà) e, per esempio, usare:

bg cp

per far sì che continui a funzionare, poi usare

fg cp

per rispostarlo in primo piano (Ctrl+Z lo potrà poi fermare di nuovo ed inviarlo allo sfondo) e usare

kill cp

per terminarlo.

Come lavorare con array in bash?

Assegnare ad un array:

arr=(uno due tre quattro)
mieiogg=(*.ogg)

Iterazioni in uno:

for f in "${mieiogg[@]}"; do ...; done

Selezionare un elemento casuale:

miefirme=($HOME/firme/*)
cat ${miefirme[RANDOM % ${#miefirme[*]}]}

(Nell'esempio precedente notare che le parentesi per l'indice dell'array forzano anche un contesto di valutazione numerica, in cui non è necessario che RANDOM sia preceduto da un carattere $.)

Non si può facilmente cancellare un elemento dal mezzo di un array, ma si può aggirare la cosa reindicizzando l'array saltando tutti gli elementi su cui si è usato unset:

arr=(zero uno due tre quattro)
unset arr[1]
arr=("${arr[@]}")

Questo lascerà al loro posto tutti gli elementi "vuoti", cancellando solo quelli "unset".

Come usare una variabile all'interno di una variabile (interpolazione di variabili) in bash?

Se si vuole fare qualcosa del tipo ${$var}, usare questo formato: ${!var}

Perciò se si ha:

PIPPO=uno
PLUTO=PIPPO

allora ${!PLUTO} restituisce "uno"

Questo funziona solo se si fa riferimento ad una variabile esistente, non per l'assegnazione di una variabile. Se si vuole assegnare un nome di variabile generato dinamicamente, si deve usare eval:

IFACE=eth0
eval IP_${IFACE}=192.168.1.1

o, in molti casi, usare semplicemente un array:

N=17
ROW[N]="Questa e' la diciottesima riga (contando da 0)."

Il trucco di usare eval può essere usato anche per far riferimento ad una variabile esistente:

IFACE=eth0
eval echo \$IP_${IFACE}

l'uso di eval genera un secondo passaggio sul codice. Il primo passaggio sostituisce $ a \$ e poi sostituisce il valore di ${IFACE}, ottenendo "eval echo $IP_eth0". Il secondo passaggio sostituisce quindi il valore della variabile ${IP_eth0}, premesso che esista.

Come contare le righe di file con una determinata estensione in una determinata directory?

find $dir -name *.[$estensioni] -exec cat \{\} \; | wc -l

trova i file in $dir con una qualsiasi delle estensioni $estensioni e legge i file usando cat e poi usando 'wc -l' per contare le righe di testo.

Si può fare anche

wc -l $dir/*.$estensioni