Orientarsi prima di digitare: pwd e uname

La prima domanda davanti a un prompt è “dove sono?”. pwd (print working directory) risponde stampando il percorso assoluto della directory corrente, cioè il posto in cui finirà un file creato senza indicare un percorso.

$ pwd
/home/frank
$ touch nuovofile
$ ls
nuovofile

La seconda domanda è “su che macchina sono?”. uname stampa le informazioni di sistema, ma da solo mostra soltanto il nome del kernel: nella pratica si usa uname -a.

$ uname -a
Linux base 4.18.0-18-generic #19~18.04.1-Ubuntu SMP Fri Apr 5 10:22:13 UTC 2019 x86_64 GNU/Linux

Le singole opzioni conviene saperle a memoria, non riconoscerle: -s nome del kernel, -r release del kernel (la più richiesta), -v versione, -n hostname, -m architettura hardware, -o sistema operativo.

Una premessa che vale per tutto il resto: la shell dell’esame è bash, quella predefinita per gli utenti su tutte le famiglie di distribuzioni. Non confonderla però con /bin/sh: su Debian e Ubuntu /bin/sh è dash, su Red Hat e derivate punta a bash. In interattivo non te ne accorgi, ma i due nomi non sono sinonimi.

Variabili di shell e variabili d’ambiente

Una variabile si crea con il segno di uguale e senza spazi intorno — è l’errore numero uno di chi arriva da altri linguaggi. Per leggerne il valore si antepone il dollaro al nome.

$ myvar=ciao
$ echo $myvar
ciao

Così però la variabile resta locale alla shell corrente. Apri una shell figlia digitando bash e scoprirai che non esiste. Serve export, che la promuove a variabile d’ambiente e la fa ereditare da tutti i processi figli.

$ export myvar
$ bash
$ echo $myvar
ciao

Da qui la differenza fra i due comandi che l’esame confronta sempre: env elenca solo le variabili d’ambiente, cioè quelle esportate, mentre set elenca tutte le variabili di shell più le funzioni. Se una variabile compare in set ma non in env, semplicemente non è stata esportata.

$ mynewvar=addio
$ env | grep mynewvar
$ set | grep mynewvar
mynewvar=addio

Per rimuoverla c’è unset, che vuole il nome senza dollaro: unset myvar. Anche questa è una forma da ricordare esatta, non da riconoscere.

PATH: invocare comandi dentro e fuori dal percorso

PATH contiene le directory, separate da due punti, in cui la shell cerca gli eseguibili.

$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Se il programma sta in una di quelle directory lo lanci con il solo nome. Se sta altrove devi dire tu alla shell dove guardare, con un percorso assoluto come /opt/tool/bin/mytool oppure relativo. Ed ecco la trappola classica: la directory corrente non fa parte del PATH, per una precisa scelta di sicurezza, quindi uno script che si trova dove sei tu si lancia con ./script.sh e mai con script.sh.

Per aggiungere una directory al percorso della sessione, e per impostare una variabile valida solo per un singolo comando:

$ export PATH="/home/frank/bin:$PATH"
$ LANG=C sort elenco.txt
$ env LANG=C sort elenco.txt

Prova mentale utile: se cancelli il percorso con unset PATH, quasi tutto smette di rispondere e perfino sudo diventa irraggiungibile, finché non lo chiami come /usr/bin/sudo.

La history: riusare quello che hai già digitato

history stampa i comandi precedenti numerati, il più recente in fondo; history 20 mostra solo gli ultimi venti. Da lì si pesca.

$ history | grep tar
 1605  tar czf backup.tar.gz /srv/dati
$ !1605
$ !!

!1605 riesegue il comando numero 1605, !! riesegue l’ultimo (tipico sudo !!), le frecce su e giù scorrono la lista e Ctrl+R cerca all’indietro mentre digiti.

I comandi finiscono nel file nascosto .bash_history nella home dell’utente, visibile con ls -a. Dettaglio che l’esame chiede volentieri: la lista in memoria e il file non coincidono, perché bash scrive il file alla chiusura della sessione, quindi gli ultimi comandi lì non ci sono ancora. history -c svuota la lista in memoria; HISTSIZE limita quanti comandi restano in memoria e HISTFILESIZE quanti nel file.

Quoting: apici singoli, virgolette e backslash

Bash tratta in modo speciale gli spazi e caratteri come dollaro, backtick, asterisco e punto interrogativo. Se non li proteggi, li interpreta.

$ touch my big file
$ ls
big  file  my

Tre file invece di uno. Le tre difese, in ordine di forza:

  • Apici singoli: rendono letterale tutto il contenuto, senza eccezioni.
  • Virgolette doppie: rendono letterale tutto tranne il dollaro, il backtick, il backslash e, in sessione interattiva, il punto esclamativo. Sono la scelta giusta quando vuoi che una variabile venga comunque espansa.
  • Backslash: protegge il singolo carattere che lo segue.
$ touch "my big file"
$ touch my\ big\ file
$ echo "$mynewvar"
addio
$ echo '$mynewvar'
$mynewvar

La differenza fra queste ultime due righe è, da sola, una domanda d’esame.