giovedì 19 settembre 2013

BASH - Guida all'automazione: Lanciare o mostrare il terminale.

Probabilmente capita soltanto a me, ma quando posso evito di lanciare all'avvio dei programmi che invece posso lanciare solo "alla bisogna".

Guake, un utile emulatore di terminale fatto per restare "nascosto" ma attivo, e venir richiamato con uno shortcut, ha però dei problemi con alcuni window manager per gestire tale shortcut. Ad esempio, io sono abituato ad usare ALT+\ (backslash) per aprire il terminale dall'alto, e in XFCE ho alcuni problemi a far funzionare quel preciso shortcut: apparentemente viene intercettato prima che possa arrivare a Guake.

Così, mi sono armato del mio fido editor di testo (si può usare quello che si vuole, consiglio SCite, o GEdit), ed ho iniziato a buttar giù uno script.

Passo 1: Riconoscere l'obiettivo

Anche conosciuto come "Che comportamento voglio?"
Il comportamento che mi interessa ottenere, è il seguente:
Premo ALT+Backslash senza il terminale visibile, compare il terminale.
Premo ALT+Backslash con il terminale visibile, scompare il terminale.

Piuttosto semplice no?

Passo 2: Identificazione del problema

Tentiamo subito con un approccio "diretto", apriamo un terminale (qualsiasi) e digitiamo:
[fanfurlio@magliettabianca]$ guake
Il comando avvia Guake, e tiene il processo che l'ha lanciato "occupato", cioè non rimanda al prompt finché non viene terminato Guake, inoltre, lancia soltanto l'emulatore di terminale, che se ne sta nascosto, facendo solo comparire la notifica che è attivo, non mostrando la finestra. Il problema numero uno si risolve mettendo un & in fondo al comando.
[fanfurlio@magliettabianca]$ guake &
In questo modo, Guake viene avviato e messo in background, compare la notifica e ci viene restituito il prompt, ma non si risolve il secondo problema, compare solo la notifica.
Un rapido sguardo alla guida rapida di Guake ci fornisce uno spunto per tentare un approccio successivo:
[fanfurlio@magliettabianca]$ guake --help 
Usage: guake [options]

Options:
  -h, --help            show this help message and exit
  -f, --fullscreen      Put Guake in fullscreen mode
  -t, --toggle-visibility
                        Toggles the visibility of the terminal window
  -p, --preferences     Shows Guake preference window
  -a, --about           Shows Guake's about info
  -n NEW_TAB, --new-tab=NEW_TAB
                        Add a new tab
  -s SELECT_TAB, --select-tab=SELECT_TAB
                        Select a tab
  -g, --selected-tab    Return the selectd tab index.
  -e COMMAND, --execute-command=COMMAND
                        Execute an arbitrary command in the selected tab.
  -r RENAME_TAB, --rename-tab=RENAME_TAB
                        Rename the selected tab.
  -q, --quit            Says to Guake go away =(
L'opzione -t è quella che ci interessa, vediamo di utilizzarla:
[fanfurlio@magliettabianca]$ guake -t
Apre il nostro terminale, che era già avviato, e se ripeto il comando, lo chiude, in entrambi i casi ritornando immediatamente il prompt.
Siamo a buon punto, il terzo problema è che l'opzione -t se Guake non è avviato, lo avvia, senza aprirlo.
La soluzione è rilevare quando il programma non è avviato, ed avviarlo prima di cercare di aprirlo.

Passo 3: Creazione dell'automazione

Con il nostro editor di testo aperto, provvediamo a creare un file nuovo, chiamandolo come preferite, io ho optato per un banalissimo terminaltoggle, inseriamoci la riga di "intestazione", per chiarire al sistema quale interprete ci interessa che esegua il nostro script:
#!/bin/bash
Questa riga fa si che quando viene eseguito il file, il sistema si preoccupi di farlo eseguire a Bash.
Dovremo fare in modo che il nostro script determini se guake è già avviato, o se deve avviarlo lui, e lo facciamo con un comando che troverete molto utile nei vostri script, dopo un po' di pratica:
#!/bin/bash
gproc=$(ps aux | grep 'guake' | grep -v 'grep')
Analizziamo la nuova riga un po' più da vicino, suddividendola in tre parti:
gproc=  $(  ps aux | grep 'guake' | grep -v 'grep'  )
Vediamo cosa significano questi comandi:
gproc=
Questa porzione di codice fa capire a BASH che vogliamo memorizzare in una variabile chiamata "gproc" cosa stiamo per dirgli.
Le variabili in BASH sono molto utili, ci permettono di compiere multiple operazioni, l'unica cosa da ricordare assolutamente è che una variabile si richiama con un segno di dollaro davanti, ad esempio: $gproc, ma la si assegna (cioè ci si inserisce un valore) senza il segno del dollaro davanti.
$(  )
Questa porzione di codice spiega a BASH che vogliamo eseguire ciò che scriviamo fra le parentesi, e recuperare quello che l'esecuzione ci risponde (si chiama "output del comando").
ps aux | grep 'guake' | grep -v 'grep'
Questo è un comando composto, si potrebbe chiamare un "treno" di comandi, visto che l'operatore |, chiamato "pipe" (pron. /paɪp/, inglese per "tubo") fa quello che è effettivamente il lavoro di un tubo, in quanto prende l'output del comando che viene eseguito, e lo invia come input al comando successivo, dando origine nel caso attuale alla seguente catena di lavoro:
ps aux produce una lista di "processi" in esecuzione sulla macchina.
La lista di processi viene inviata al comando grep 'guake', che sceglie fra le righe della lista solo quelle che contengono "guake", inviandole successivamente al comando grep -v 'grep', che rimuove da queste, le righe che contengono "grep".
Il risultato di questo comando è di ottenere una lista di processi in esecuzione che si chiamano "guake".

L'intera riga, ricapitolando, esegue e filtra la lista di processi, individuando solo quelli che si chiamano "guake", e inserisce questa lista nella variabile $gproc.
Cosa succede se Guake non è in esecuzione (avviato) sulla macchina? PS non avrà nessun "guake" nella lista, il filtro (grep) non troverà alcuna riga da far passare, e la variabile alla fine risulterà vuota. Cosa succede se invece Guake è in esecuzione? PS avrà "guake" nella lista, il filtro farà passare solo quella riga, e la variabile alla fine conterrà la riga con i dettagli del processo di Guake. In realtà a noi non interessano i dettagli del processo, ma soltanto se c'è o no, quindi dovremo agire in un modo a seconda se la variabile $gproc sia vuota oppure no: per far questo useremo il test if:
#!/bin/bash
gproc=$(ps aux | grep 'guake' | grep -v 'grep')
if [ -z "$gproc" ]; then
  # VARIABILE VUOTA
fi
# VARIABILE PIENA
Il test -z controlla se il parametro successivo è "vuoto", se lo è passa l'esecuzione al blocco fra then e fi, andando poi avanti; se invece non è vuoto, salta direttamente a dopo il fi
A questo punto abbiamo la nostra funzionalità, non ci resta che aggiungere i due comandi nei posti giusti, e il gioco è fatto:
#!/bin/bash
gproc=$(ps aux | grep 'guake' | grep -v 'grep')
if [ -z "$gproc" ]; then
  guake &
  sleep 0.5
fi
guake -t
Ed ecco il nostro script pronto per essere eseguito.
Ho aggiunto un ritardo di mezzo secondo (sleep 0.5) dopo aver aperto Guake, per essere sicuri che quando viene eseguito il comando per mostrare il terminale, Guake sia effettivamente stato avviato, altrimenti il computer è troppo veloce e potrebbe eseguire il secondo comando prima che il primo sia stato terminato, fallendo nel far comparire il terminale; potreste aver bisogno di modificare questo valore, per situazioni in cui Guake impieghi più di mezzo secondo per avviarsi.
L'ultimo passo da eseguire è dare i permessi di esecuzione allo script
[fanfurlio@magliettabianca]$ chmod +x terminaltoggle
E copiarlo in una posizione che ci ricordiamo, dopodiché possiamo configurare il nostro ambiente grafico (Desktop Environment o DE) per collegare allo shortcut prefissato (ALT+Backslash nel mio caso) il comando, in modo da far funzionare il tutto al tocco di un tasto.