pdftk – a kind command line tool to manage pdf files

pdftk is a useful command line tool to manage pdf files.

It permits operations like:

  • to split a document in pages,
  • to merge several documents,
  • to extract pages,
  • to remove pages,
  • to rotate pages,
  • to apply background watermark,
  • to decrypt or encrypt a document

Some examples:

pdftk doc.pdf burst

will split the document in its pages generating several named as pg_0001.pdf, pg_0002.pdf etc…

pdftk pg_0001.pdf pg_0002.pdf cat output tets.pdf

will concatenate two documents

pdftk pg_0001.pdf cat 1east output tets.pdf

will rotate a page

shell tools – parametric commands with xargs

Sometime is necessary to execute commands several times changing only some parameters.

In most of the cases you can use a for or a while loop:

for FILE in $(ls -1); do echo ${FILE}; done 

This form isn’t particularly robust because of the unmanaged spaces in the file names but anyway it is really common because of its simplicity.

Another option is to use the -exec option of the find command.

find . -type f -exec echo '{}' \;

This form is a really powerful way of looping but can be used only on list of files

In some cases neither of the previous forms can be used. Let see an example: you have to read a file (ei: an apache access log) elaborate the row in some way and execute a command for each row. In a case like this find isn’t an option and the for loop is inconvenient at the best because you have to put much more logic in the variable definition then in the true loop. Moreover if you have a big file to manage you couldn’t have enough memory.

It would be much better to elaborate on line at time: xargs is the tool that permits us to proceed in this way.

Let’s look at an example: suppose I want to loop through a log file and search for the served pages that doesn’t contains a word. I’ll have to read the file, probably select some lines, extract the information required to execute a curl, execute the curl and examine the result.
I can do this in a single line using xargs without involving intermediate files.

cat  20150608_access.log |grep "linuxandcompany" |awk -F"GET " '{print $2}' |awk -F" HTTP/" '{print $1}' | xargs -I {} sh -c 'curl -s "http://www.linuxandcompany.it{}" | grep "pippo" > /dev/null;  [ $? -ne 0 ] && echo "{}"'

The first part is really basic: the cat command is used to read through a log file. Then I use a filter (grep) to select some lines (the ones related to this website).

To extract the searched information (URI) I use the two times the awk command.

At this point I have a pipe with a list of URI. The function of xargs is just to get these values, substitute them in a parametrized command and execute the result.

In this example I have to execute several commands at each iteration. xargs doesn’t permit this but a simply trick will help us: we choose a shell as command and execute a script inside it.

Just to complete the description of the example the curl retrieves the web page from internet, the grep command checks for the searched value, and the result is thrown in the trash. This because I’m interested only in the return value of the grep command.

The return value of the grep is checked and if the word isn’t found the URI is written in the output.

Cambiare sfondo periodicamente con Ubuntu Unity

Stranamente Ubuntu Unity non prevede un meccanismo per cambiare lo sfondo periodicamente selezionando delle immagini da una cartella come invece è possibile fare su praticamente ogni altro desktop environment.

A questo è comunque possibile ovviare con facilità sfruttando il comando
gsettings
che permette di gestire da linea di comando molte delle impostazioni dell’interfaccia grafica.

Il comando per cambaire lo sfondo è molto semplice

gsettings set org.gnome.desktop.background picture-uri "file://<path to file>"

e potete testarlo con un’immagine a vostra scelta.

Il secondo obiettivo che ci eravamo posti è quello scegliere un’immagine casualmente un’immagine da una cartella. Si può fare in modo molto semplice con gli strumenti della shell unix. In primo luogo usiamo find per farci dare l’elenco dei file con i path assoluti, utilizziamo poi sort per mischiare casualmente l’eleco; sort, infatti, con l’opzione
-R restituisce un’ordinamento casuale. Usiamo infine
head
per selezionare il primo elemento della lista.

find <absolute path to image directory> |sort -R |head -1

Per combinare le due cose utiliziamo le variabili della shell

export SFONDO="$(find <absolute path to image directory>|sort -R |head -1)"; gsettings set org.gnome.desktop.background picture-uri \"file://${SFONDO}\"

L’ultimo cosa che ci rimane da fare è rendere periodica questa operazione. Per farlo useremo ovviamente
crontab
ma il comando così comè non funzionerebbe perché gsettings, per funzionare correttamente utilizza le variabili di ambiente e crontab crea un ambiente “anomalo” in cui molte di queste variabili o non sono definite o hanno valori specifici.

Per il nostro script ci serve il valore di
DBUS_SESSION_BUS_ADDRESS
. Possiamo vederne il valore con un semplice echo

echo $DBUS_SESSION_BUS_ADDRESS

Questa variabile permette a gsettings di sapere su quale ambiente grafico andare ad agire e deve essere letta “live” cambiando ad ogni sessione grafica. Per recuperarla bisogna fare un po’ di passi. In primo luogo
pgrep
ci permette di recuperare il PID del processo. Con questa operazione possiamo andare a recuperare il valore della variabile dalle informazioni in proc

export PID=$(pgrep gnome-session)
grep -z DBUS_SESSION_BUS_ADDRESS /proc/${PID}/environ

infine awk ci permette di separare il valore dal nome della variabile

grep -z DBUS_SESSION_BUS_ADDRESS /proc/${PID}/environ|awk -F= '{print $2"="$3}'

Non ci rimane che eseguire
crontab -e
ed inserire una riga con la schedulazione voluta ed i comandi

*/10 * * * *  export PID=$(pgrep gnome-session);export DBUS_SESSION_BUS_ADDRESS=$(grep -z DBUS_SESSION_BUS_ADDRESS /proc/${PID}/environ|awk -F= '{print $2"="$3}') ;export SFONDO="$(find <absolute path to image directory> |sort -R |head -1)"; gsettings set org.gnome.desktop.background picture-uri \"file://${SFONDO}\"

L’*/10 sta ad indicare che il comando deve essere eseguito ogni 10 minuti.

backup e rsync

Avere una politica di backup che ci permetta di limitare i danni in caso di errori o problemi è una necessità che risale all’origine dell’informatica.

UNIX e Linux hanno ovviamente diversi tool che vengono in aiuto per questa necessità, e, almeno nel caso di una macchina singola, si possono implementare delle politiche asolutamente soddisfacenti con soli tool interni come cron ed rsync.

Come molti comandi della shell UNIX rsync è estremamente versatile. Il comando di base è:

rsync /path/to/source/file /path/to/destination

Come suggerisce il nome rsync non è un semplice tool per la copia dei file ma si occupa di sincronizzare due directory effettuando la copia dei soli file che lo richiedono.

Tendenzialmente rsync è utile soprattutto se si ha la necessità di copiare molti file e quindi sarà necessario utilizzare l’opzione -r

rsync -r /path/to/source/ /path/to/destination
rsync -r /path/to/source /path/to/destination

Ho riportato due comandi per sottolineare una particolarità di rsync. Se si copia una directory e si specifica / nel path della sorgente il contenuto della directory verrà copiato nella destinazione; se invece il path viene scritto senza / è la directory che viene copiata nella destinazione.

Rsync generalmente non ha quasi nessun output a video, il che è ottimo per script in crontab, se però si sta eseguendo il backup in real time si vorrà probabilmente un’indicazione di come sta procedendo l’attività. PEr questo si hanno due opzioni -v e –progress.

rsync generalmente controlla le dimensioni e la data di ultima modifica per capire se devono essere copiate. Sono però disponibili specifiche opzioni per fare controlli differenti. Ad esempio –size-only se si vole considerare solo le dimensioni del file o –checksum se si vuole un conformto più affidabile anche se più oneroso.

Le opzioni di questo comando sono svarite ma generalmente se si stanno effettuando dei backup -a farà si che vengano attivate le opzioni che più comunemente vengono utilizzate per questa attività.

Una mensione particolare merita l’opzione -b; in questa modalità rsync effettuerà un backup incrementale spostando i file rimossi dalla sorgente in un appositp path e scrivendo su un file tutte le operazioni effettuate. Si otterrà quindi una directory allineata a quella sorgente e si manterranno i file e le informazioni necessari per ricostruire la situazione al momento del backup precedente.

Un esempio di comando reale potrebbe quindi essere

rsync -avb --size-only --delete --log-file=/path/to/file_log/file.log --backup-dir=/path/to/directory_file_incrementali/ --progress /directory/source/ /directory/destination/

command line perl e analisi dei log

Tempo fa abbiamo visto come utilizzare il perl come tool di linea di comando. Vediamo oggi come utilizzarlo in un caso un poco più complesso: per contare le occorrenze di un dato vhost in un access.log di apache e magari al tempo stesso avere le statistiche di quante volte apache da una risposta corretta (return code 200) o qualche altro errore.

Osserviamo per prima cosa una riga di log del nostro sito preferito

xx.xx.xx.xx linuxandcompany.it - [06/Apr/2014:23:54:13 +0200] "GET /2014/01/squid-come-reverse-proxy/ HTTP/1.1" 200 10177 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.110 Safari/537.36"

Questo tipo di analisi si riesce a fare direttamente in linea con il perl. Iniziamo prima da una cosa cosa semplice: contiamo il numero di occorrenze dei vari return code per un dato sito:

cat linuxandcompany.it-08-04-2014.log|grep "linuxandcompany.it" |perl -ne '{@fields = split(/ /); $stat{$fields[8]}++;}END{while( ($key1, $value1) = each(%stat) ){print $key1. "\t" .$value1. "\n"}}'

che darà un risultato della forma:

304	16
200	397
302	4
301	2
404	2

Il comando inizia con un cat che passa ogni riga del file di log in stdout. Lo stdout viene intercettato da un grep che seleziona le righe del mio sito.
Le righe selezionate vengono poi passate al comando per che, come visto, con le opzioni ne applica il comando che segue ad ogni riga.
Il comando per è costituito da due blocchi: quello ordinario nella prima coppia di parentesi graffe e un secondo blocco identificato da END{…} che viene eseguito solo a conclusione del ciclo.

Il primo blocco inizia con uno split che separa la stringa in stdin in un array con le varie parole per elementi

@fields = split(/ /);

Tra gli slash è riportato il carattere scelto per spezzare la stringa.
Lo split avrebbe potuto anche essere scritto come

@fields = split(/ /, $_);

Utilizzo poi i campi per incrementare un hash di contatori che ha per chiavi i return code.

$stat{$fields[8]}++;

Il blocco finale si occuperà poi di visualizzare il risultato:

while( ($key1, $value1) = each(%stat) )
   {
   print $key1. "\t" .$value1. "\n"
   }

ed è costituito da un ciclo sulle coppie chiave, valore dell’hash.

In una situazione reale questo comando funziona però fino ad un certo punto perché le stringhe possono contenere spazi anche se non è frequentissimo. Un risultato molto migliore si ottiene combinando due split: un primo basato sulle virgolette, che racchiudono le stringe significative, ed un secondo basato sugli spazi da applicare alle sottostringhe:

cat linuxandcompany.it-08-04-2014.log|grep "linuxandcompany.it" |perl -ne '{@fields = split(/"/); @sub_fields = split(/ /, $fields[2]); $stat{$sub_fields[1]}++;}END{while( ($key1, $value1) = each(%stat) ){print $key1. "\t" .$value1. "\n"}}'

Si può poi complicare a piacere andando a fare la stessa statistica per ogni sito presente nel log. Sotto faccio un doppio report (per sito e complessivo).

cat linuxandcompany.it-08-04-2014.log |perl -ne '{@fields = split(/"/); @sub_fields1 = split(/ /, $fields[0]); @sub_fields2 = split(/ /, $fields[2]); $stat1{$sub_fields1[1]}{$sub_fields2[1]}++; $stat2{$sub_fields2[1]}++}END{while(($key1, $value1) = each(%stat1)){ while( ($key2,$value2) = each(%$value1) ){print $key1. "\t" . $key2."\t".$value2. "\n"}}; print "\n\n"; while( ($key1, $value1) = each(%stat2) ){print $key1. "\t" .$value1. "\n"}}'

Rimane evidente che questo è al limite di quello che ha senso fare con una linea di comando e non scrivendo un piccolo script.

Comandi analoghi si possono utilizzare ad esempio per contare le occorrenze delle pagine nel log.

awk e linea di comando

La shell di UNIX/Linux è uno strumento potentissimo che permette di velocizzare ed automatizzare le più svariate operazione. Dato che in UNIX ogni cosa è un file, sono di fondamentale importanza quegli strumenti che permettono di analizzare e gestire i file.

Spessissimo i file con cui si ha a che fare nelle attività sistemistiche sono dei rudimentali database strutturati con i record in righe e i campi in colonne. In questa categoria rientrano ad esempio i file passwd e moltissimi file di log.

Per poter automatizzare operazioni su questo tipo di file è necessario uno strumento che dia accesso a specifici campi nei vari record. Come sempre in ambiente UNIX/Linux è possibile procedere in diversi modi ma uno strumento di sicuoro interesse è awk. Si tratta di uno di quei tool la cui conoscenza non dovrebbe mancare a nessun sistemista.

Nella sua forma più semplice awk accetta in stdin un file, e permette di estrarre dei campi da ogni riga.

Ed esempio se volessi estrarre gli utenti che hanno script schedulati nel seguente file crontab:

17 *	* * *	root    cd / && run-parts --report /etc/cron.hourly
25 6	* * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6	* * 7	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6	1 * *	root	test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )

potrei utilizzare la seguente righa di comando:

cat /etc/crontab | awk '{print $6}'

all’interno di awk ogni campo, infatti, è contenuto in una variabile $n, dove n è il numero del campo ed i campi sono separati de default da spazi. L’intera riga è contenuta nella variabile $0. Ci sono molte altre variabili predefinite in awk e la lista completa è riportata nel suo manuale (man awk). Una menzione la merita $NR che contiene il numero della riga che si sta elaborando.

Non in tutti i file il separatore è lo spazio; l’opzione -F permette di scegliere un separatore differente.

awk può essere utilizzato come filtro sfruttando il costrutto if al suo interno:

Ad esempio se voglio sapere quali utenti non hanno una shell reale posso utilizzare:

cat /etc/passwd|awk -F: '{if ($7=="/bin/false") print $1}'

Di grande interesse anche i costrutti BEGIN ed END che permettono di definire blocchi di operazioni da eseguire prima o dopo l’esame del file. Posso ad esempio riscrivere lo stesso esempio di prima come:

cat /etc/passwd|awk 'BEGIN{FS=":"}{if ($7=="/bin/false") print $1}'

awk è un vero e proprio linguaggio di programmazione anche se abbastanza semplice, e le possibilità che offre sono molto ampie; nondimeno la sua utilità è soprattutto nelle sue forme più semplici dove, con molta semplicità, permette di improvvisare filtri o operazioni anche molto sofisticati.

Concludo con un esempio dove mostro come awk può essere utilizzato per calcolare la durata media delle slow query a partire da un file di log di mysql:

cat mysql-slow.log |grep Query_time|awk '{cont++;totale+=$3}END{media=totale/cont; print "media = " media}'

perl e linea di comando

Il perl con le sue espressioni regolari è anche un fantastico tool per la linea di comando in grado di sostituire e surclassare i pur notevoli awk e sed. Usato con le opzioni del caso, infatti, il perl ne riprende tutte le caratteristiche ma aggiunge la sua flessibilità e ricchezza.

Le opzioni chiave per l’uso da linea di comando sono “-p” e “-n“: in entrambi i casi lo script o il comando in perl viene eseguito per ogni riga del file in ingresso ma nel secondo caso il risultato va in output.

Queste opzioni corrispondono ai loop che riporto sotto:

-n

LINE:
while (<>) {
        ...             # programma
      }

-p

LINE:
while (<>) {
         ...             # programma
      } continue {
         print or die "-p destination: $!\n";
      }

Molto utili sono anche

  • -e che permette di inserire direttamente sulla linea di comando il codice perl da eseguire
  • -i che fa si che venga modificato direttamente il file che viene letto

Ad esempio entrambi i seguenti comandi

cat /etc/passwd |perl -pe 's/(.*):(.*):(.*):(.*):(.*):(.*):(.*)/$1\t\t$6/'
cat /etc/passwd | awk -F: '{print $1"\t\t"$6}'

permettono di estrarre dal file password gli utenti e le loro home.

Perl, utilizzato in questo modo, permette di definire dei blocchi di codice BEGIN e END per eventuali operazioni preliminari a o conclusive del loop implicito: si immagini il caso in cui si voglia contare il numero di occorrenze di un qualche pattern in un file.

La documentazione delle opzioni è su perldoc perlrun.