Shell Tricks

Da MontelLUG.
Menu
MontelLUG frontpage
Aiuto: Come modificare le pagine
Torna indietro

Avvertenza

Gli script presentati in questa pagina sono assolutamente senza alcuna garanzia. Usarli a proprio rischi e pericolo.

Eliminazione file mp3 di tutti gli utenti :)

1. find /home -name '*.mp3' -print0 | xargs -0r rm -f 
2. find /home -iname '*.mp3' -exec rm {} \; 

Spiegazione

  1. Cerca nella cartella /home (e nelle sue sottodirectory) i file con estensione mp3 e stampali separati da carattere nullo (0x00) (quindi funziona anche con nomi con spazi). Passa il tutto a xargs che controlla che l'elenco, separato da caratteri nulli (-0), non sia vuoto (-r) e rimuove i file senza pieta' (-f), invocando una sola volta il comando rm (piu' veloce di un ciclo for o della seconda versione qui sotto).
  2. Cerca i file *.mp3 in modo case insensitive (-iname) e per ogni file trovato esegui il comando rm NOMEFILE (in quanto le parentesi graffe vengono sostituite con il nome del file trovato). Per rimuovere i file chiedendo prima conferma, basta aggiungere il parametro -i di rm, ovvero rm -i {} \;

Ricerca di testo in file

find /dir -type f -print0 | xargs -0r grep "stringa"

Spiegazione

Elenca tutti i file normali (non le directory, non i link simbolici, non i device...) nelle directory e /dir e nelle sue sottodirectory e li passa nel solito modo a xargs, che crea la stringa per il grep, una potente utility di ricerca. Il grep cercherà allora la stringa "stringa" nei file, restituendo la riga e il nomefile in cui si trovano.
Specificando il parametro -i del grep la ricerca sarà insensibile a maiuscole/minuscole.
Specificando il parametro -s il grep non restituisce messaggi di errore se non riesce a guardare qualche file perché non si hanno i permessi.

Creazione in automatico di directory


echo "for(c=0;c<10;++c){\"prova\";c}" | bc | xargs mkdir

Spiegazione

Il comando bc (e' nello standard posix) e' una potente calcolatrice programmabile. Se gli si butta qualcosa nello stdin lui restituisce gentilmente il risultato in stdout. Il ciclo for che gli passiamo crea semplicemente le stringhe prova1, prova2, ecc. separate da a capo.
Il mkdir crea quindi le directory.
Attenzione: cosi' com'e' non funziona con nomi che contengono spazi.

Selezione di righe da un file


1. sed -n '5,10p;10q' nomefile
2. head -10 nomefile | tail -5

Spiegazione

  1. Il sed e' un potente editor di flussi. Il comando qui sopra dice: -n fa solo quel che ti chiedo esplicitamente (altrimenti copia in output ogni riga che riceva in input); 5,10p butta in output le righe da 5 a 10; 10q alla decima riga esci senza leggere altro dal file
  2. leggi le prime 10 righe da nomefile, crea una pipe e passa tutto a tail, che tiene solo le ultime 5.

NOTE: la prima versione e' piu' efficiente perche' non crea pipe e legge le prime 10 righe solo una volta.

Scartare le prime righe da un file


1. tail +10 nomefile
2. sed -n '10,$p' nomefile

Spiegazione

  1. tail accetta anche +n come argomento, non solo -n! tail +n significa proprio dammi la coda del file dalla riga 10 in poi
  2. l'unica cosa nuova rispetto al comando sopra e' il segno $ che sta ad indicare l'ultima riga

Rinominare immagini con la data

Script per rinonimare file dalla macchina digitale con la data

Scalare immagini per la pubblicazione nel web

Si puo' utilizzare l'ottimo programma convert di ImageMagick per scalare, ruotare, convertire immagini fra diversi formati. Esempio tipico: dopo aver acquisito delle immagini da scanner o fotocamera, queste devono essere ruotate e scalate per la pubblicazione nel web, con larghezza di 600 pixel. Inoltre, si vogliono creare delle anteprime con larghezza di 100 pixel. Supponiamo che le immagini siano tutte dentro la directory corrente, e che le immagini e le anteprime per il web debbano essere scritte nella sottodirectory web. Lo script che segue e' reso molto piu' lungo a causa dei commenti e delle istruzioni di test che aiutano a seguire passo passo il procedimento svolto, che risultano utili solo come esercizio.

mkdir web ;# crea directory, se non esiste
for file in *.jpg; do 
  # file e' una variabile a cui viene associato il nome di ciascun file con estensione .jpg
  # presente nella directory corrente
  echo "Processo l'immagine $file..."  

  # In radice metto il nome del file senza l'estensione .jpg: ci servirà per 
  # attribuire un nome alle anteprime
  # echo stampa il nome del file e sed sostituisce ".jpg" in ""  
  radice=`echo $file |sed s/\.jpg$//g`
  # stampo il nome della radice
  echo "radice=$radice..."

  # Ruotiamo e scaliamo le immagini
  echo "Ruoto e ridimensione $file in web/$file..."
  convert -rotate 90 -sample 600 "$file" "web/$file"; done
  # Creiamo anteprime con larghezza di 100 pixel
  echo "Creo anteprima in web/$radice_small.jpg..."
  convert -sample 100 "web/$file" "web/$radice_small.jpg"
done

Volendo evitare commenti e altre amenità superflue, basta digitare da linea di comando

mkdir web; for file in *.jpg; do convert -rotate 90 -sample 600 "$file" "web/$file"; 
convert -sample 100 "$file" "web/`echo $file |sed s/\.jpg$//g`_small.jpg"; done

Script per il ripping automatico di un CD

Ripping automatico di un CD su un lettore MP3

Contare il numero di righe di codice di un progetto

Può essere utile o curioso sapere quante righe di codice sono state scritte per un determinato progetto software. Una semplice stringa di shell, et voilà:

find . \( -name '*.h' -o -name '*.cc' \) -print0 | xargs -0r wc -l

Il comando così com'è va lanciato dalla cartella princiaple del progetto, opure si sostituisce il suo percorso al posto del punto dopo find. Il comando cerca tutti i file sorgente .h e .cc (se usate linguaggi diversi dal C++, basta cambiare le estensioni), li passa ad xargs che li prende e costrisce al solito modo la riga di comando per wc, che si occupa infine di contare le righe (o, volendo, caratteri o parole).

Ordinare le pagine di un pdf

La cosa può sembrare strana, ma ogni girno se ne vedono di nuove :-) Il problema è questo: coloro che tengono la contabilità della mia ditta ci inviano le paghe in un bel pdf con le buste paga ordinate secondo criteri che possono essere definiti random, con grande felicità della segretaria che impazzisce per trovare ciò che le serve. Questo programmino non fa altro che riordinare le pagine secondo l'ordine alfabetico dei nomi.


#!/bin/bash

#Problema: file pdf con buste paga degli utenti messe in disordine
#(si ringrazia chi ci tiene la contabilità...); una busta paga per pagina.
#Per praticità sarebbe meglio che fossero ordinate alfabeticamente per nome utente.
#Soluzione: qui sotto :-)

#Convertiamo il pdf in un txt
pdftotext -layout origine.pdf
sed -n '/riga_di_riferimento/{n;n;n;n;p}' origine.txt | sed 's/^ *//;s/^\t*//' > nomi_estratti.txt

#Si spezza il pdf in tanti files quante sono le pagine; i nomi saranno: 1.pdf 2.pdf ... n.pdf
pdftk stampa_di_mer\ mag\ 10\ 14_41_18\ CEST\ 2006.pdf burst output ./singole/%1d.pdf

#Rinominiamo i files coi nomi degli utenti per poi procedere all'ordinamento
contatore=1
for nome in $(awk '{print $1}' < "nomi_estratti.txt" )
 do
 mv ./singole/$contatore.pdf ./singole/$nome.pdf
 let "contatore += 1"
done

#Riuniamo il file in un unico con le pagine finalmente ordinate correttamente
pdftk ./singole/*.pdf cat output unione.pdf

Note

Come prerequisiti servono i programmi pdftotext per generare un file txt da cui estrarre i dati che ci servono e pdftk per spezzare e unire le pagine.

Come si può notare, l'estrazione dei nomi avviene tramite "sed", partendo da una riga di riferimento, nel mio caso si tratta dell'intestazione della pagina, che non ho trascritto qui per ovvi motivi :-)

Faccio questa acrobazia in quanto è uno dei pochi punti di riferimento certi: nel mio caso il nome si trova nella quinta riga sotto l'intestazione (vedasi la sequenza {n;n;n;n;p} che dice quali riche stampare e quali no); per il resto ho tutto sfalsato: cognome e nome non cominciano sempre allo stesso punto, ma tra una pagina e l'altra a volte c'è uno scarto anche di 5 posizioni più o meno avanti nella riga.

Altro punto da sistemare: a causa di questa instabilità così com'è il programma gestisce solo i cognomi, e più precisamente solo la prima parte di essi, se sono compositi (in caso di Dai Rossi il file pdf corrispondente avrà nome "dai.pdf"). Per ora sono fortunato e non ho omonimie (e non ho testato l'ipotesi), ma se ce ne fossero potrebbero presentarsi problemi, quindi sarebbe utile arrivare a gestire il nome completo.

Eliminazione righe duplicate da una lista non ordinata

awk -F\\t '!v[$2]++' lista.txt > lista_uniq.txt

Spiegazione

La lista, contenuta in un file di testo, ha delle righe con campi separati da caratteri di tabulazione, ad esempio:

Pos.	Nome e cognome	Cellulare	Indirizzo
1	Adriano Pinco	+39***********	v. Bile, 42
2	Marco Rossi	***********	Via dei Matti, 0
3	Alessio Pallo	+39***********	Piazza Tona, 1
4	Marco Rossi	+39***********	v. de' Matti, 0
5	Marco Rossi S.	***********	Largo Alessandro, 33
6	Stefano Caio	+39***********	vc. Giambattista, s/n
7	Adriano Pinco	***********	Via Bile, 42

Voglio una sola copia delle righe che hanno la stessa stringa nella seconda colonna, "Nome e cognome".

Il comando awk scorre il file "lista.txt" riga per riga.

Tramite "-F\\t" ciascuna riga viene spezzata in campi in corrispondenza dei caratteri di tabulazione in essa contenuti; il carattere di escape "\" è necessario perché la shell non interpreti "\t".

Con "v[$2]++" dico ad awk di incrementare di uno ("++") un elemento del vettore "v", avente per indice la stringa contenuta nel campo appena isolato:

  • se tale elemento non esisteva, il comando impartito lo creerà, restituendo 0 ("falso" in termini informatici), e grazie alla negazione "!" anteposta otterrò "vero", ossia "stampa la riga";
  • se viceversa l'elemento esiste perché il campo "$2" è stato incontrato in precedenza, il comando restituirà un valore diverso da 0 (maggiore o uguale ad 1, "vero"), che negato dà "falso" - la riga viene saltata.

Per finire, redirigo l'output di awk nel file "lista_uniq.txt".

La stessa riga di comando può essere riadattata per liste con campi separati da altri tipi di delimitatore, come virgole, punti e virgola, due punti, ecc.

Sostituzione di una parola in tutti i file di testo con una data estensione

Sostituisce tutte le occorrenze di pippo con pluto per ogni file con estensione .txt nella cartella corrente.

for NOMEFILE in *.txt; do sed -i 's/pippo/pluto/g' "${NOMEFILE}"; done

Link