Content from Introduzione alla shell


Ultimo aggiornamento il 2025-11-07 | Modifica questa pagina

Tempo stimato: 5 minuti

Panoramica

Domande

  • Cos’è una shell di comando e perché dovrei usarla?

Obiettivi

  • Spiegare come la shell si relaziona con la tastiera, lo schermo, il sistema operativo e i programmi degli utenti.
  • Spiegare quando e perché si dovrebbero usare le interfacce a riga di comando invece delle interfacce grafiche.

Sfondo

L’uomo e il computer interagiscono comunemente in molti modi diversi, ad esempio tramite tastiera e mouse, interfacce touch screen o sistemi di riconoscimento vocale. Il modo più diffuso di interagire con i computer è chiamato interfaccia grafica (graphical user interface GUI). Con una GUI, le istruzioni vengono impartite facendo clicK con il mouse e utilizzando interazioni guidate da menu.

Sebbene l’aiuto visivo di un’interfaccia grafica ne renda intuitivo l’apprendimento, questo modo di fornire istruzioni a un computer è poco scalabile. Immaginate il seguente compito: per una ricerca bibliografica, dovete copiare la terza riga di mille file di testo in mille directory diverse e incollarla in un unico file. Utilizzando un’interfaccia grafica, non solo dovreste cliccare alla vostra scrivania per diverse ore, ma potreste anche commettere un errore nel completare questo compito ripetitivo. È qui che possiamo tratte vantaggio della shell Unix. La shell Unix è sia un’interfaccia a riga di comando (CLI) che un linguaggio di scripting, che consente di svolgere tali compiti ripetitivi in modo automatico e veloce. Con i comandi appropriati, la shell può ripetere le operazioni con o senza modifiche tutte le volte che si vuole. Utilizzando la shell, l’operazione descritta nell’esempio di letteratura può essere eseguita in pochi secondi.

La shell

La shell è un programma in cui gli utenti possono digitare comandi. Con la shell è possibile richiamare programmi complicati come il software di modellazione climatica o semplici comandi che creano una directory vuota con una sola riga di codice. La shell Unix più diffusa è Bash (Bourne Again SHell — così chiamata perché deriva da una shell scritta da Stephen Bourne). Bash è la shell predefinita nella maggior parte delle moderne implementazioni Unix e nella maggior parte dei pacchetti che forniscono strumenti simili a Unix per Windows. Si noti che “Git Bash” è un software che consente agli utenti di Windows di utilizzare un’interfaccia simile a Bash quando interagiscono con Git.

L’uso della shell richiede un certo sforzo e un po’ di tempo per essere appreso. Mentre l’interfaccia grafica presenta delle scelte da selezionare, le scelte della CLI non vengono presentate automaticamente, quindi è necessario imparare alcuni comandi come un nuovo vocabolario in una lingua che si sta studiando. Tuttavia, a differenza di una lingua parlata, un piccolo numero di “parole” (cioè di comandi) permette di fare molta strada e oggi ci occuperemo di questi pochi elementi essenziali.

La grammatica di una shell consente di combinare gli strumenti esistenti in potenti pipeline e di gestire automaticamente grandi volumi di dati. Le sequenze di comandi possono essere scritte in un script, migliorando la riproducibilità dei flussi di lavoro.

Inoltre, la riga di comando è spesso il modo più semplice per interagire con macchine e supercomputer remoti. La familiarità con la shell è quasi essenziale per eseguire una serie di strumenti e risorse specializzate, compresi i sistemi di calcolo ad alte prestazioni. Con l’aumento della popolarità dei cluster e dei sistemi di cloud computing per l’elaborazione di dati scientifici, la capacità di interagire con la shell sta diventando un’abilità necessaria. Possiamo basarci sulle competenze della riga di comando qui trattate per affrontare un’ampia gamma di domande scientifiche e sfide computazionali.

Iniziamo.

Quando la shell viene aperta per la prima volta, viene visualizzato un prompt, che indica che la shell è in attesa di input.

BASH

$

La shell usa tipicamente $ il segno del dollaro come prompt, ma può usare un simbolo diverso. Negli esempi di questa lezione, mostreremo il prompt come $. La cosa più importante è non digitare il prompt quando si digitano i comandi. Digitate solo il comando che segue il prompt. Questa regola si applica sia in queste lezioni che in quelle di altre fonti. Si noti inoltre che dopo aver digitato un comando, è necessario premere il tasto Invio per eseguirlo.

Il prompt è seguito da un cursore di testo, un carattere che indica la posizione in cui verrà visualizzata la digitazione. Il cursore è solitamente un blocco lampeggiante o fisso, ma può anche essere un trattino basso o una pipe. Lo si può vedere in un programma di editor di testo, ad esempio.

Si noti che il prompt potrebbe avere un aspetto leggermente diverso. In particolare, gli ambienti di shell più diffusi mettono per default il nome dell’utente e il nome dell’host prima di $. Un prompt di questo tipo potrebbe apparire come, ad esempio:

BASH

nelle@localhost $

Il prompt potrebbe includere anche più di questo. Non preoccupatevi se il vostro prompt non è solo un breve $. Questa lezione non dipende da queste informazioni aggiuntive e non dovrebbe nemmeno ostacolarvi. L’unico elemento importante su cui concentrarsi è il carattere $ stesso e vedremo più avanti perché.

Proviamo il nostro primo comando, ls, abbreviazione di listing. Questo comando elencherà il contenuto della directory corrente:

BASH

$ ls

OUTPUT

Desktop     Downloads   Movies      Pictures
Documents   Library     Music       Public
Richiamo

Comando non trovato

Se la shell non riesce a trovare un programma il cui nome corrisponde al comando digitato, stamperà un messaggio di errore come:

BASH

$ ks

OUTPUT

ks: command not found

Questo può accadere se il comando è stato digitato in modo errato o se il programma corrispondente a quel comando non è installato.

La pipeline di Nelle: Un problema tipico


Nelle Nemo, una biologa marina, è appena tornata da un’indagine di sei mesi nel North Pacific Gyre, dove ha campionato la vita marina gelatinosa nel Great Pacific Garbage Patch. Ha 1520 campioni che ha analizzato per misurare l’abbondanza relativa di 300 proteine. Deve far passare questi 1520 file attraverso un programma immaginario chiamato goostats.sh. Oltre a questo enorme compito, deve scrivere i risultati entro la fine del mese, in modo che il suo articolo possa apparire in un numero speciale di Aquatic Goo Letters.

Se Nelle dovesse scegliere di eseguire goostats.sh a mano utilizzando un’interfaccia grafica, dovrà selezionare e aprire un file 1520 volte. Se goostats.sh impiega 30 secondi per eseguire ogni file, l’intero processo richiederà più di 12 ore dell’attenzione di Nelle. Con la shell, Nelle può invece assegnare al computer questo compito banale mentre si concentra sulla stesura del suo articolo.

Le prossime lezioni esploreranno i modi in cui Nelle può raggiungere questo obiettivo. In particolare, le lezioni spiegano come Nelle possa utilizzare una shell di comando per eseguire il programma goostats.sh, utilizzando dei loop per automatizzare i passaggi ripetitivi di inserimento dei nomi dei file, in modo che il suo computer possa lavorare mentre lei scrive il suo articolo.

Una volta messa insieme una pipeline di elaborazione, sarà in grado di riutilizzarla ogni volta che raccoglierà altri dati.

Per svolgere il suo compito, Nelle deve sapere come fare:

  • navigare in un file o in una cartella
  • creare un file o una cartella
  • verifica la lunghezza di un file
  • concatenare i comandi tra loro
  • recuperare un insieme di file
  • iterazione di file
  • esegue uno script di shell contenente la sua pipeline
Punti Chiave
  • Una shell è un programma il cui scopo principale è leggere i comandi ed eseguire altri programmi.
  • Questa lezione utilizza Bash, la shell predefinita in molte implementazioni di Unix.
  • I programmi possono essere eseguiti in Bash inserendo i comandi nel prompt della riga di comando.
  • I principali vantaggi della shell sono l’elevato rapporto tra azioni e battute, il supporto per l’automazione di attività ripetitive e la capacità di accedere a macchine in rete.
  • Una sfida significativa nell’uso della shell può essere quella di sapere quali comandi devono essere eseguiti e come eseguirli.

Content from Navigazione tra file e directory


Ultimo aggiornamento il 2025-11-07 | Modifica questa pagina

Tempo stimato: 40 minuti

Panoramica

Domande

  • Come posso muovermi nel mio computer?
  • Come posso vedere quali file e cartelle ci sono?
  • Come posso specificare la posizione di un file o di una cartella sul mio computer?

Obiettivi

  • Spiegare le somiglianze e le differenze tra un file e una directory.
  • Tradurre un percorso assoluto in un percorso relativo e viceversa.
  • Costruire percorsi assoluti e relativi che identificano file e cartelle specifici.
  • Usare opzioni e argomenti per modificare il comportamento di un comando di shell.
  • Dimostrare l’uso del completamento a schede e spiegarne i vantaggi.

L’introduzione e la navigazione del filesystem nella shell (trattata nella sezione Navigare tra file e directory) può creare confusione. Si possono tenere aperti fianco a fianco sia il terminale che il file explorer della GUI, in modo che gli studenti possano vedere il contenuto e la struttura dei file mentre usano il terminale per navigare nel sistema.

La parte del sistema operativo responsabile della gestione dei file e delle directory si chiama file system. Organizza i dati in file, che contengono informazioni, e cartelle, che contengono file o altre cartelle.

Diversi comandi sono usati frequentemente per creare, ispezionare, rinominare e cancellare file e cartelle. Per iniziare a esplorarli, andiamo alla nostra finestra di shell aperta.

Per prima cosa, scopriamo dove ci troviamo eseguendo un comando chiamato pwd (che sta per ‘print working directory’). Le directory sono come luoghi: in qualsiasi momento, mentre si utilizza la shell, ci si trova esattamente in un luogo chiamato current working directory. I comandi leggono e scrivono i file nella directory di lavoro corrente, cioè “qui”, quindi è importante sapere dove ci si trova prima di eseguire un comando. pwd mostra la posizione in cui ci si trova:

BASH

$ pwd

OUTPUT

/Users/nelle

Qui, la risposta del computer è /Users/nelle, che è la home directory di Nelle:

Richiamo

Variazione della directory principale

Il percorso della home directory ha un aspetto diverso nei diversi sistemi operativi. Su Linux, può apparire come /home/nelle, mentre su Windows sarà simile a C:\Documents and Settings\nelle o C:\Users\nelle. (Negli esempi futuri, abbiamo utilizzato l’output del Mac come predefinito; l’output di Linux e Windows può differire leggermente, ma dovrebbe essere generalmente simile.

Si suppone inoltre che il comando pwd restituisca la home directory dell’utente. Se pwd restituisce qualcosa di diverso, potrebbe essere necessario navigare lì usando cd o alcuni comandi di questa lezione non funzioneranno come scritto. Vedere Esplorare altre directory per maggiori dettagli sul comando cd.

Per capire che cos’è una “cartella home”, diamo un’occhiata a come è organizzato il file system nel suo complesso. Per il bene di questo esempio, illustreremo il filesystem del computer della nostra scienziata Nelle. Dopo questa illustrazione, imparerete i comandi per esplorare il vostro filesystem, che sarà costruito in modo simile, ma non esattamente identico.

Sul computer di Nelle, il filesystem ha questo aspetto:

Il file system è costituito da una cartella principale che contiene sottocartelle intitolate bin, data, users e tmp

Il filesystem ha l’aspetto di un albero capovolto. La cartella più in alto è la cartella di root che contiene tutto il resto. Ci si riferisce ad essa usando un carattere slash, /, da solo; questo carattere è lo slash iniziale di /Users/nelle.

All’interno di questa cartella ci sono diverse altre cartelle: bin (dove sono memorizzati alcuni programmi integrati), data (per i file di dati vari), Users (dove si trovano le cartelle personali degli utenti), tmp (per i file temporanei che non devono essere memorizzati a lungo) e così via.

Sappiamo che la nostra cartella di lavoro corrente /Users/nelle è memorizzata all’interno di /Users perché /Users è la prima parte del suo nome. Allo stesso modo, sappiamo che /Users è memorizzata nella cartella principale / perché il suo nome inizia con /.

Richiamo

Slash

Si noti che il carattere / ha due significati. Quando compare davanti al nome di un file o di una cartella, si riferisce alla cartella principale. Quando appare all’interno di un percorso, è solo un separatore.

Sotto /Users, troviamo una directory per ogni utente con un account sulla macchina di Nelle, i suoi colleghi imhotep e larry.

Come altre directory, le cartelle home sono sottocartelle di "/Users", come "/Users/imhotep", "/Users/larry" o "/Users/nelle"

I file dell’utente imhotep sono memorizzati in /Users/imhotep, quelli dell’utente larry in /Users/larry e quelli di Nelle in /Users/nelle. Nelle è l’utente degli esempi; pertanto, la nostra cartella home è /Users/nelle. In genere, quando si apre un nuovo prompt dei comandi, all’inizio ci si trova nella propria cartella home.

Ora impariamo il comando che ci permetterà di vedere il contenuto del nostro filesystem. Possiamo vedere cosa c’è nella nostra cartella home eseguendo ls:

BASH

$ ls

OUTPUT

Applications Documents    Library      Music        Public
Desktop      Downloads    Movies       Pictures

(Anche in questo caso, i risultati possono essere leggermente diversi a seconda del sistema operativo e di come è stato personalizzato il filesystem)

ls stampa i nomi dei file e delle cartelle nella cartella corrente. È possibile rendere il suo output più comprensibile utilizzando l’opzione -F **che indica a ls di classificare l’output aggiungendo un marcatore ai nomi dei file e delle cartelle per indicare di cosa si tratta:

  • il trattino / indica che si tratta di una directory
  • @ indica un link
  • * indica un eseguibile

A seconda delle impostazioni predefinite della shell, questa potrebbe anche utilizzare dei colori per indicare se ogni voce è un file o una cartella.

BASH

$ ls -F

OUTPUT

Applications/ Documents/    Library/      Music/        Public/
Desktop/      Downloads/    Movies/       Pictures/

Qui possiamo vedere che la cartella home contiene solo sottocartelle. Tutti i nomi nell’output che non hanno un simbolo di classificazione sono file nella cartella di lavoro corrente.

Richiamo

Cancellazione del terminale

Se lo schermo è troppo ingombro, si può cancellare il terminale con il comando clear. È comunque possibile accedere ai comandi precedenti usando e per spostarsi riga per riga, oppure scorrendo il terminale.

Ottenere aiuto

ls ha molte altre opzioni. Ci sono due modi comuni per scoprire come usare un comando e quali opzioni accetta — a seconda del vostro ambiente, potreste scoprire che solo uno di questi modi funziona:

  1. possiamo passare un’opzione --help a qualsiasi comando (disponibile su Linux e Git Bash), ad esempio:

BASH

$ ls --help
  1. possiamo leggere il suo manuale con man (disponibile su Linux e macOS):

BASH

$ man ls

descriveremo entrambi i modi in seguito.

Richiamo

Guida per i comandi incorporati

Alcuni comandi sono integrati nella shell Bash, anziché esistere come programmi separati nel filesystem. Un esempio è il comando cd (cambio di directory). Se si riceve un messaggio come No manual entry for cd, provare invece help cd. Il comando help è il modo per ottenere informazioni sull’uso di Bash built-ins.

L’opzione --help è stata utilizzata per elencare i contenuti della cartella di lavoro corrente

La maggior parte dei comandi di bash e dei programmi scritti per essere eseguiti all’interno di bash supportano un’opzione --help che mostra ulteriori informazioni su come utilizzare il comando o il programma.

BASH

$ ls --help

OUTPUT

Usage: ls [OPTION]... [FILE]...
List information about the FILEs (the current directory by default).
Sort entries alphabetically if neither -cftuvSUX nor --sort is specified.

Mandatory arguments to long options are mandatory for short options, too.
  -a, --all                  do not ignore entries starting with .
  -A, --almost-all           do not list implied . and ..
      --author               with -l, print the author of each file
  -b, --escape               print C-style escapes for nongraphic characters
      --block-size=SIZE      scale sizes by SIZE before printing them; e.g.,
                               '--block-size=M' prints sizes in units of
                               1,048,576 bytes; see SIZE format below
  -B, --ignore-backups       do not list implied entries ending with ~
  -c                         with -lt: sort by, and show, ctime (time of last
                               modification of file status information);
                               with -l: show ctime and sort by name;
                               otherwise: sort by ctime, newest first
  -C                         list entries by columns
      --color[=WHEN]         colorize the output; WHEN can be 'always' (default
                               if omitted), 'auto', or 'never'; more info below
  -d, --directory            list directories themselves, not their contents
  -D, --dired                generate output designed for Emacs' dired mode
  -f                         do not sort, enable -aU, disable -ls --color
  -F, --classify             append indicator (one of */=>@|) to entries
...        ...        ...
Richiamo

Quando usare le opzioni brevi o lunghe

Quando le opzioni esistono sia come opzioni brevi che lunghe:

  • Usare l’opzione breve quando si digitano comandi direttamente nella shell per ridurre al minimo la pressione dei tasti e svolgere più rapidamente il proprio compito.
  • Usate l’opzione long negli script per fornire chiarezza. Verrà letto molte volte e digitato una sola volta.
Richiamo

Opzioni della riga di comando non supportate

Se si cerca di usare un’opzione non supportata, ls e altri comandi di solito stampano un messaggio di errore simile a:

BASH

$ ls -j

ERRORE

ls: invalid option -- 'j'
Try 'ls --help' for more information.

Il comando man

L’altro modo per conoscere ls è digitare

BASH

$ man ls

Questo comando trasformerà il vostro terminale in una pagina con una descrizione del comando ls e delle sue opzioni.

Per navigare tra le pagine di man, si possono usare e per spostarsi riga per riga, oppure provare b e Spacebar per saltare su e giù di una pagina intera. Per cercare un carattere o una parola nelle pagine di man, usare / seguito dal carattere o dalla parola che si sta cercando. A volte una ricerca produce più risultati. In tal caso, è possibile spostarsi tra i risultati utilizzando N (per andare avanti) e Shift+N (per andare indietro).

Per uscire dalle pagine man, premere q.

Richiamo

Pagine di manuale sul web

Naturalmente, esiste un terzo modo per accedere alla guida dei comandi: la ricerca su Internet tramite il browser. Quando si utilizza la ricerca su Internet, includere la frase unix man page nella query di ricerca aiuterà a trovare risultati pertinenti.

GNU fornisce collegamenti ai suoi manuali, compreso il core GNU utilities, che copre molti comandi introdotti in questa lezione.

Sfida

Esplorazione di altre opzioni ls

È anche possibile utilizzare due opzioni contemporaneamente. Cosa fa il comando ls quando viene usato con l’opzione -l? E se si utilizzano sia l’opzione -l che l’opzione -h?

Alcuni dei suoi risultati riguardano proprietà che non vengono trattate in questa lezione (come i permessi e la proprietà dei file), ma il resto dovrebbe essere comunque utile.

L’opzione -l fa sì che ls utilizzi un formato di elenco llungo, mostrando non solo i nomi dei file/directory ma anche informazioni aggiuntive, come la dimensione del file e l’ora dell’ultima modifica. Se si usano sia l’opzione -h che l’opzione -l, si rende la dimensione del file “leggibile dall’uomo”, cioè si visualizza qualcosa come 5.3K invece di 5369.

Sfida

Elenco in ordine cronologico inverso

Per impostazione predefinita, ls elenca il contenuto di una cartella in ordine alfabetico per nome. Il comando ls -t elenca gli elementi in base all’ora dell’ultima modifica invece che in ordine alfabetico. Il comando ls -r elenca il contenuto di una cartella in ordine inverso. Quale file viene visualizzato per ultimo quando si combinano le opzioni -t e -r? Suggerimento: Potrebbe essere necessario usare l’opzione -l per vedere le ultime date di modifica.

Il file modificato più di recente è elencato per ultimo quando si usa -rt. Questo può essere molto utile per trovare le modifiche più recenti o per verificare se è stato scritto un nuovo file di output.

Esplorazione di altre cartelle

Non solo si può usare ls sulla cartella di lavoro corrente, ma anche per elencare il contenuto di una cartella diversa. Diamo un’occhiata alla nostra cartella Desktop eseguendo ls -F Desktop, cioè il comando ls con la opzione -F e l’[argomento][Argomenti] Desktop. L’argomento Desktop indica a ls che si vuole un elenco di qualcosa di diverso dalla cartella di lavoro corrente:

BASH

$ ls -F Desktop

OUTPUT

shell-lesson-data/

Si noti che se una cartella denominata Desktop non esiste nella cartella di lavoro corrente, questo comando restituirà un errore. In genere, una cartella Desktop esiste nella propria home directory, che si presume sia la cartella di lavoro corrente della shell bash.

L’output dovrebbe essere un elenco di tutti i file e le sottocartelle della vostra cartella Desktop, compresa la cartella shell-lesson-data che avete scaricato nel setup per questa lezione. (Nella maggior parte dei sistemi, il contenuto della cartella Desktop nella shell viene visualizzato come icona in un’interfaccia grafica dietro tutte le finestre aperte. Verificate se questo è il vostro caso)

Organizzare le cose in modo gerarchico ci aiuta a tenere traccia del nostro lavoro. Sebbene sia possibile mettere centinaia di file nella nostra cartella home, così come è possibile ammassare centinaia di fogli stampati sulla nostra scrivania, è molto più facile trovare le cose quando sono organizzate in sottocartelle con nomi ragionevoli.

Ora che sappiamo che la cartella shell-lesson-data si trova nella nostra cartella Desktop, possiamo fare due cose.

Per prima cosa, usando la stessa strategia di prima, possiamo esaminare il suo contenuto passando il nome di una cartella a ls:

BASH

$ ls -F Desktop/shell-lesson-data

OUTPUT

exercise-data/  north-pacific-gyre/

In secondo luogo, possiamo cambiare la nostra posizione in una cartella diversa, in modo da non trovarci più nella nostra cartella home.

Il comando per cambiare posizione è cd seguito da un nome di cartella per cambiare la nostra cartella di lavoro. cd sta per ‘change directory’, il che è un po’ fuorviante. Il comando non cambia la cartella, ma la cartella di lavoro corrente della shell. In altre parole, cambia le impostazioni della shell per quanto riguarda la cartella in cui ci troviamo. Il comando cd è simile a un doppio clic su una cartella in un’interfaccia grafica per entrare in quella cartella.

Supponiamo di volerci spostare nella cartella exercise-data che abbiamo visto sopra. Per arrivarci possiamo usare la seguente serie di comandi:

BASH

$ cd Desktop
$ cd shell-lesson-data
$ cd exercise-data

Questi comandi ci spostano dalla nostra cartella home alla nostra cartella Desktop, poi alla cartella shell-lesson-data, quindi alla directory exercise-data. Si noterà che cd non stampa nulla. Questo è normale. Molti comandi di shell non producono nulla sullo schermo quando vengono eseguiti con successo. Ma se si esegue pwd dopo di esso, si può vedere che ora ci troviamo in /Users/nelle/Desktop/shell-lesson-data/exercise-data.

Se ora si esegue ls -F senza argomenti, viene elencato il contenuto di /Users/nelle/Desktop/shell-lesson-data/exercise-data, perché è lì che ci troviamo:

BASH

$ pwd

OUTPUT

/Users/nelle/Desktop/shell-lesson-data/exercise-data

BASH

$ ls -F

OUTPUT

alkanes/  animal-counts/  creatures/  numbers.txt  writing/

Ora sappiamo come scendere nell’albero delle cartelle (cioè come entrare in una sottocartella), ma come salire (cioè come lasciare una cartella e andare nella sua cartella home)? Potremmo provare a fare come segue:

BASH

$ cd shell-lesson-data

ERRORE

-bash: cd: shell-lesson-data: No such file or directory

ma si ottiene un errore! Perché?

Con i metodi utilizzati finora, cd può vedere solo le sottocartelle all’interno della cartella corrente. Esistono diversi modi per vedere le cartella al di sopra della posizione corrente; inizieremo con il più semplice.

Esiste una scorciatoia nella shell per spostarsi su un livello di cartella. Funziona come segue:

BASH

$ cd ..

.. è un nome speciale di cartella che significa “la cartella che contiene questa”, o più brevemente, il genitore della cartella corrente. Sicuramente, se si esegue pwd dopo aver eseguito cd .., ci si ritrova in /Users/nelle/Desktop/shell-lesson-data:

BASH

$ pwd

OUTPUT

/Users/nelle/Desktop/shell-lesson-data

La cartella speciale .. di solito non viene visualizzata quando si esegue ls. Se si desidera visualizzarla, si può aggiungere l’opzione -a a ls -F:

BASH

$ ls -F -a

OUTPUT

./  ../  exercise-data/  north-pacific-gyre/

-a sta per “mostra tutto” (compresi i file nascosti); obbliga ls a mostrare i nomi dei file e delle cartella che iniziano con ., come ad esempio .. (che, se ci troviamo in /Users/nelle, si riferisce alla cartella /Users). Come si può vedere, viene visualizzata anche un’altra cartella speciale, chiamata ., che significa “la cartella di lavoro corrente”. Può sembrare superfluo avere un nome per questa cartella, ma presto ne vedremo l’uso.

Si noti che nella maggior parte degli strumenti a riga di comando, più opzioni possono essere combinate con un singolo - e senza spazi tra le opzioni; ls -F -a è equivalente a ls -Fa.

Richiamo

Altri file nascosti

Oltre alle cartella nascoste .. e ., è possibile che venga visualizzato anche un file chiamato .bash_profile. Questo file contiene solitamente le impostazioni di configurazione della shell. Si possono vedere anche altri file e cartella che iniziano con .. Si tratta in genere di file e cartella utilizzati per configurare diversi programmi sul computer. Il prefisso . è usato per evitare che questi file di configurazione ingombrino il terminale quando si usa il comando standard ls.

Questi tre comandi sono i comandi di base per navigare nel filesystem del computer: pwd, ls e cd. Esploriamo alcune variazioni di questi comandi. Cosa succede se si digita cd da solo, senza indicare una directory?

BASH

$ cd

Come si può controllare cosa è successo? pwd ci dà la risposta!

BASH

$ pwd

OUTPUT

/Users/nelle

Si scopre che cd senza un argomento vi riporterà alla vostra cartella home, il che è ottimo se vi siete persi nel vostro filesystem.

Proviamo a tornare alla cartella exercise-data di prima. L’ultima volta abbiamo usato tre comandi, ma possiamo mettere insieme l’elenco delle cartella per spostarci a exercise-data in un solo passaggio:

BASH

$ cd Desktop/shell-lesson-data/exercise-data

Verificare che ci si sia spostati nel posto giusto eseguendo pwd e ls -F.

Se si vuole salire di un livello dalla cartella dei dati, si può usare cd ... Ma c’è un altro modo per spostarsi in qualsiasi cartella, indipendentemente dalla posizione attuale.

Finora, quando si sono specificati i nomi delle cartella, o anche un percorso di cartella (come sopra), si sono usati percorsi relativi. Quando si usa un percorso relativo con un comando come ls o cd, si cerca di trovare quella posizione dal punto in cui ci si trova, piuttosto che dalla radice del file system.

Tuttavia, è possibile specificare il percorso assoluto di una cartella includendo il suo intero percorso a partire dalla cartella principale, indicata da una barra iniziale. La barra iniziale / indica al computer di seguire il percorso dalla radice del file system, in modo da riferirsi sempre esattamente a una cartella, indipendentemente dalla posizione in cui ci si trova quando si esegue il comando.

Questo ci permette di spostarci nella nostra cartella shell-lesson-data da qualsiasi punto del filesystem (anche da dentro exercise-data). Per trovare il percorso assoluto che stiamo cercando, possiamo usare pwd e poi estrarre il pezzo che dobbiamo spostare in shell-lesson-data.

BASH

$ pwd

OUTPUT

/Users/nelle/Desktop/shell-lesson-data/exercise-data

BASH

$ cd /Users/nelle/Desktop/shell-lesson-data

Eseguire pwd e ls -F per assicurarsi di essere nella directory prevista.

Richiamo

Altre due scorciatoie

La shell interpreta il carattere tilde (~) all’inizio di un percorso come “la cartella home dell’utente corrente”. Ad esempio, se la cartella home di Nelle è /Users/nelle, allora ~/data è equivalente a /Users/nelle/data. Questo funziona solo se è il primo carattere del percorso; here/there/~/elsewhere è non here/there/Users/nelle/elsewhere.

Un’altra scorciatoia è il carattere - (trattino).cd tradurrà - in la cartella precedente in cui mi trovavo, il che è più veloce che dover ricordare, e poi digitare, il percorso completo. Questo è un modo molto efficiente di spostarsi avanti e indietro tra due cartella – cioè se si esegue cd - due volte, si torna alla cartella di partenza.

La differenza tra cd .. e cd - è che il primo porta su, mentre il secondo porta indietro.


Provate! Per prima cosa spostatevi in ~/Desktop/shell-lesson-data (dovreste essere già lì).

BASH

$ cd ~/Desktop/shell-lesson-data

Poi cd nella cartella exercise-data/creatures

BASH

$ cd exercise-data/creatures

ora se si esegue

BASH

$ cd -

vedrete che siete tornati in ~/Desktop/shell-lesson-data. Eseguite di nuovo cd - e sarete di nuovo in ~/Desktop/shell-lesson-data/exercise-data/creatures

Sfida

Percorsi assoluti e relativi

Partendo da /Users/nelle/data, quale dei seguenti comandi potrebbe essere usato da Nelle per navigare verso la sua cartella home, che è /Users/nelle?

  1. cd .
  2. cd /
  3. cd /home/nelle
  4. cd ../..
  5. cd ~
  6. cd home
  7. cd ~/data/..
  8. cd
  9. cd ..
  1. No: . sta per la cartella corrente.
  2. No: / sta per la cartella principale.
  3. No: la cartella home di Nelle è /Users/nelle.
  4. No: questo comando sale di due livelli, cioè termina con /Users.
  5. Sì: ~ sta per la cartella home dell’utente, in questo caso /Users/nelle.
  6. No: questo comando naviga in una cartella home nella cartella corrente, se esiste.
  7. Sì: inutilmente complicato, ma corretto.
  8. Sì: scorciatoia per tornare alla cartella principale dell’utente.
  9. Sì: sale di un livello.
Sfida

Risoluzione del percorso relativo

Utilizzando il diagramma del filesystem qui sotto, se pwd visualizza /Users/thing, cosa visualizzerà ls -F ../backup?

  1. ../backup: No such file or directory
  2. 2012-12-01 2013-01-08 2013-01-27
  3. 2012-12-01/ 2013-01-08/ 2013-01-27/
  4. original/ pnas_final/ pnas_sub/
Un albero di cartella sotto la cartella Utenti dove "/Utenti" contiene le cartella "backup" e "thing"; "/Utenti/backup" contiene "original", "pnas_final" e "pnas_sub"; "/Utenti/thing" contiene "backup"; e "/Utenti/thing/backup" contiene "2012-12-01", "2013-01-08" e "2013-01-27"
  1. No: c’è *una cartella backup in /Users.
  2. No: questo è il contenuto di Users/thing/backup, ma con .., abbiamo chiesto un livello più in alto.
  3. No: vedi spiegazione precedente.
  4. Sì: ../backup/ si riferisce a /Users/backup/.
Sfida

ls Comprensione della lettura

Utilizzando il diagramma del filesystem qui sotto, se pwd visualizza /Users/backup e -r dice a ls di visualizzare le cose in ordine inverso, quale/i comando/i produrrà il seguente output:

OUTPUT

pnas_sub/ pnas_final/ original/
Un albero di directory sotto la directory Utenti dove "/Utenti" contiene le cartella "backup" e "thing"; "/Utenti/backup" contiene "original", "pnas_final" e "pnas_sub"; "/Utenti/thing" contiene "backup"; e "/Utenti/thing/backup" contiene "2012-12-01", "2013-01-08" e "2013-01-27"
  1. ls pwd
  2. ls -r -F
  3. ls -r -F /Users/backup
  1. No: pwd non è il nome di una cartella.
  2. Sì: ls senza l’argomento cartella elenca i file e le directory nella directory corrente.
  3. Sì: utilizza esplicitamente il percorso assoluto.

Sintassi generale di un comando di Shell


Abbiamo già incontrato comandi, opzioni e argomenti, ma forse è utile formalizzare un po’ di terminologia.

Considerate il comando qui sotto come un esempio generale di comando, che verrà sezionato nelle sue parti componenti:

BASH

$ ls -F /
Sintassi generale di un comando di shell

ls è il comando, con una opzione -F e un argomento /. Abbiamo già incontrato opzioni che iniziano con un solo trattino (-), note come opzioni corte, o con due trattini (--), note come opzioni lunghe. le [Opzioni] modificano il comportamento di un comando e gli [Argomenti] indicano al comando su cosa operare (ad esempio, file e cartella). A volte le opzioni e gli argomenti sono indicati come parametri. Un comando può essere chiamato con più di un’opzione e più di un argomento, ma non sempre un comando richiede un argomento o un’opzione.

A volte si può vedere che le opzioni vengono chiamate scambi o flags, specialmente per le opzioni che non richiedono argomenti. In questa lezione useremo il termine opzione.

Ogni parte è separata da spazi. Se si omette lo spazio tra ls e -F la shell cercherà un comando chiamato ls-F, che non esiste. Inoltre, la capitalizzazione può essere importante. Ad esempio, ls -s visualizzerà la dimensione dei file e delle cartella accanto ai nomi, mentre ls -S ordinerà i file e le cartella per dimensione, come mostrato di seguito:

BASH

$ cd ~/Desktop/shell-lesson-data
$ ls -s exercise-data

OUTPUT

total 28
 4 animal-counts   4 creatures  12 numbers.txt   4 alkanes   4 writing

Si noti che le dimensioni restituite da ls -s sono in blocchi. Poiché queste sono definite in modo diverso per i diversi sistemi operativi, è possibile che non si ottengano le stesse cifre dell’esempio.

BASH

$ ls -S exercise-data

OUTPUT

animal-counts  creatures  alkanes  writing  numbers.txt

Mettendo insieme tutti questi elementi, il comando ls -F / di cui sopra fornisce un elenco di file e cartella nella cartella principale /. Di seguito è riportato un esempio dell’output che si potrebbe ottenere dal comando precedente:

BASH

$ ls -F /

OUTPUT

Applications/         System/
Library/              Users/
Network/              Volumes/

Pipeline di Nelle: Organizzare i file

sapendo queste informazioni su file e cartella, Nelle è pronta a organizzare i file che la macchina per il saggio delle proteine creerà.

Crea una cartella chiamata north-pacific-gyre (per ricordarsi da dove provengono i dati), che conterrà i file di dati della macchina di analisi e i suoi script di elaborazione dei dati.

Ogni campione fisico è etichettato secondo la convenzione del suo laboratorio con un ID unico di dieci caratteri, ad esempio “NENE01729A”. Questo ID viene utilizzato nel registro di raccolta per registrare la posizione, l’ora, la profondità e altre caratteristiche del campione, quindi decide di utilizzarlo nel nome di ogni file di dati. Poiché l’output della macchina di analisi è un testo semplice, chiamerà i suoi file NENE01729A.txt, NENE01812A.txt e così via. Tutti i 1520 file andranno nella stessa directory.

Ora nella sua cartella corrente shell-lesson-data, Nelle può vedere quali file ha usando il comando:

BASH

$ ls north-pacific-gyre/

Questo comando è molto impegnativo da digitare, ma si può lasciare che la shell faccia la maggior parte del lavoro attraverso il cosiddetto completamento delle tabelle. Se si digita:

BASH

$ ls nor

e poi preme Tab (il tasto tab della tastiera), la shell completa automaticamente il nome della cartella:

BASH

$ ls north-pacific-gyre/

Premendo di nuovo Tab non si ottiene nulla, poiché ci sono più possibilità; premendo Tab due volte si ottiene un elenco di tutti i file.

Se Nelle preme G e poi di nuovo Tab, la shell aggiungerà ‘goo’ poiché tutti i file che iniziano con ‘g’ condividono i primi tre caratteri ‘goo’.

BASH

$ ls north-pacific-gyre/goo

per vedere tutti quei file, si può premere Tab altre due volte.

BASH

ls north-pacific-gyre/goo
goodiff.sh   goostats.sh

questo si chiama completamento delle tabelle e lo vedremo in molti altri strumenti man mano che andremo avanti.

Punti Chiave
  • Il file system è responsabile della gestione delle informazioni sul disco.
  • Le informazioni sono memorizzate in file, che sono memorizzati in cartelle.
  • le cartelle possono anche memorizzare altre cartelle, che formano un albero di cartelle.
  • pwd stampa la cartella di lavoro corrente dell’utente.
  • ls [path] stampa un elenco di uno specifico file o cartella; ls da solo elenca la cartella di lavoro corrente.
  • cd [path] cambia la cartella di lavoro corrente.
  • La maggior parte dei comandi accetta opzioni che iniziano con un singolo -.
  • i nomi delle cartelle in un percorso sono separati con / su Unix, ma \ su Windows.
  • / da sola è la cartella principale dell’intero file system.
  • Un percorso assoluto specifica una posizione dalla radice del file system.
  • Un percorso relativo specifica una posizione a partire da quella corrente.
  • . da solo significa “la cartella corrente”; .. significa “la cartella sopra quella corrente”.

Content from Lavorare con i file e le directory


Ultimo aggiornamento il 2025-11-07 | Modifica questa pagina

Tempo stimato: 50 minuti

Panoramica

Domande

  • Come posso creare, copiare ed eliminare file e cartelle?
  • Come posso modificare i file?

Obiettivi

  • Eliminare, copiare e spostare file e/o cartelle.
  • Creare i file in questa gerarchia usando un editor o copiando e rinominando i file esistenti.
  • Creare una gerarchia di cartelle che corrisponda a un diagramma dato.

Creazione di directory


Ora sappiamo come esplorare i file e le cartelle, ma come crearli?

In questo episodio impareremo a creare e spostare file e directory, utilizzando come esempio la cartella exercise-data/writing.

Fase uno: vedere dove siamo e cosa abbiamo già

Dovremmo ancora trovarci nella cartella shell-lesson-data sul Desktop, che possiamo controllare usando:

BASH

$ pwd

OUTPUT

/Users/nelle/Desktop/shell-lesson-data

Ora ci sposteremo nella cartella exercise-data/writing e vedremo cosa contiene:

BASH

$ cd exercise-data/writing/
$ ls -F

OUTPUT

haiku.txt  LittleWomen.txt

Creare una cartella

Creiamo una nuova cartella chiamata thesis usando il comando mkdir thesis (che non ha output):

BASH

$ mkdir thesis

Come si può intuire dal nome, mkdir significa “directory make”. Poiché thesis è un percorso relativo (cioè non ha una barra iniziale, come /what/ever/thesis), la nuova cartella viene creata nella cartella di lavoro corrente:

BASH

$ ls -F

OUTPUT

haiku.txt  LittleWomen.txt  thesis/

Dato che abbiamo appena creato la cartella thesis, non c’è ancora nulla al suo interno:

BASH

$ ls -F thesis

Si noti che mkdir non si limita a creare singole cartelle una alla volta. L’opzione -p permette a mkdir di creare una cartella con sottocartelle annidate in una singola operazione:

BASH

$ mkdir -p ../project/data ../project/results

L’opzione -R del comando ls elenca tutte le sottocartelle annidate all’interno di una cartella. Usiamo ls -FR per elencare ricorsivamente la nuova gerarchia di cartelle appena creata nella cartella project:

BASH

$ ls -FR ../project

OUTPUT

../project/:
data/  results/

../project/data:

../project/results:
Richiamo

Due modi di fare la stessa cosa

l’uso della shell per creare una cartella non è diverso dall’uso di un file explorer. Se si apre la cartella corrente utilizzando il file explorer grafico del sistema operativo, la cartella thesis apparirà anche lì. Mentre la shell e il file explorer sono due modi diversi di interagire con i file, i file e le cartelle sono gli stessi.

Richiamo

Buoni nomi per i file e le directory

I nomi complicati di file e cartelle possono rendere la vita difficile quando si lavora alla riga di comando. Qui forniamo alcuni suggerimenti utili per i nomi dei file e delle cartelle.

  1. Non usare spazi.

Gli spazi possono rendere un nome più significativo, ma poiché gli spazi sono usati per separare gli argomenti sulla riga di comando, è meglio evitarli nei nomi di file e directory. Al loro posto si può usare - o _ (ad esempio north-pacific-gyre/ piuttosto che north pacific gyre/). Per verificarlo, provare a digitare mkdir north pacific gyre e vedere quale directory (o directory!) viene creata quando si controlla con ls -F.

  1. Non iniziare il nome con - (trattino).

I comandi trattano i nomi che iniziano con - come opzioni.

  1. attenersi a lettere, numeri, . (punto o ‘stop completo’), - (trattino) e _ (trattino basso).

Molti altri caratteri hanno un significato speciale sulla riga di comando. Ne conosceremo alcuni nel corso di questa lezione. Ci sono caratteri speciali che possono far sì che il vostro comando non funzioni come previsto e possono persino causare la perdita di dati.

Se è necessario fare riferimento a nomi di file o cartelle che contengono spazi o altri caratteri speciali, è necessario circondare il nome con [virgolette] singole (https://www.gnu.org/software/bash/manual/html_node/Quoting.html) ('').

Gli studenti possono talvolta rimanere intrappolati in editor di testo a riga di comando come Vim, Emacs o Nano. Chiudere l’emulatore di terminale e aprirne uno nuovo può essere frustrante, perché gli studenti devono navigare di nuovo nella cartella corretta. La nostra raccomandazione per attenuare questo problema è che gli istruttori usino lo stesso editor di testo degli studenti durante i workshop (nella maggior parte dei casi Nano).

Creare un file di testo

Cambiamo la nostra cartella di lavoro in thesis usando cd, quindi eseguiamo un editor di testo chiamato Nano per creare un file chiamato draft.txt:

BASH

$ cd thesis
$ nano draft.txt
Richiamo

Quale editor?

Quando diciamo che “nano è un editor di testo” intendiamo proprio “testo”. Può lavorare solo con i dati a caratteri semplici, non con tabelle, immagini o altri supporti di facile utilizzo. Lo usiamo negli esempi perché è uno degli editor di testo meno complessi. Tuttavia, a causa di questa caratteristica, potrebbe non essere abbastanza potente o flessibile per il lavoro che dovrete svolgere dopo questo workshop. Sui sistemi Unix (come Linux e macOS), molti programmatori usano Emacs o Vim (entrambi richiedono più tempo per essere appresi), oppure un editor grafico come Gedit o VScode. Su Windows, si può usare Notepad++. Windows ha anche un editor incorporato chiamato notepad che può essere eseguito dalla riga di comando allo stesso modo di nano per gli scopi di questa lezione.

Indipendentemente dall’editor utilizzato, è necessario sapere dove cerca e salva i file. Se lo si avvia dalla shell, (probabilmente) userà la cartella di lavoro corrente come posizione predefinita. Se si utilizza il menu di avvio del computer, potrebbe voler salvare i file nella cartella Desktop o Documenti. È possibile cambiare questa scelta navigando in un’altra cartella la prima volta che si fa “Salva con nome…”

digitiamo alcune righe di testo.

schermata dell'editor di testo nano in azione con il testo Non è più pubblicare o morire, è condividere e prosperare

Una volta che siamo soddisfatti del nostro testo, possiamo premere Ctrl+O (premere il tasto Ctrl o Control e, tenendolo premuto, premere il tasto O) per scrivere i nostri dati su disco. Verrà richiesto un nome per il file che conterrà il testo. Premete Retro per accettare il nome predefinito suggerito di draft.txt.

una volta salvato il nostro file, possiamo usare Ctrl+X per uscire dall’editor e tornare alla shell.

Richiamo

Tasto Control, Ctrl o ^

Il tasto Control è anche chiamato “Ctrl”. L’uso del tasto Control può essere descritto in vari modi. Ad esempio, si può vedere l’istruzione di premere il tasto Control e, tenendolo premuto, premere il tasto X, descritto come uno dei seguenti:

  • Control-X
  • Control+X
  • Ctrl-X
  • Ctrl+X
  • ^X
  • C-x

In nano, nella parte inferiore dello schermo si vedrà ^G Get Help ^O WriteOut. Ciò significa che si può usare Control-G per ottenere aiuto e Control-O per salvare il file.

nano non lascia alcun output sullo schermo dopo la sua uscita, ma ls ora mostra che è stato creato un file chiamato draft.txt:

BASH

$ ls

OUTPUT

draft.txt
Sfida

Creare file in modo diverso

Abbiamo visto come creare file di testo usando l’editor nano. Ora provate il seguente comando:

BASH

$ touch my_file.txt
  1. Cosa ha fatto il comando touch? Quando si guarda alla cartella corrente usando l’esploratore di file della GUI, il file appare?

  2. Utilizzare ls -l per ispezionare i file. Quanto è grande my_file.txt?

  3. Quando si potrebbe voler creare un file in questo modo?

  1. Il comando touch genera un nuovo file chiamato my_file.txt nella cartella corrente. È possibile osservare questo nuovo file generato digitando ls al prompt della riga di comando. il file my_file.txt può anche essere visualizzato nel file explorer della GUI.

  2. Quando si ispeziona il file con ls -l, si noti che la dimensione di my_file.txt è di 0 byte. In altre parole, non contiene dati. Se si apre my_file.txt con il proprio editor di testo, è vuoto.

  3. Alcuni programmi non generano direttamente i file di output, ma richiedono che siano già stati generati dei file vuoti. Quando il programma viene eseguito, cerca un file esistente da riempire con il suo output. Il comando touch consente di generare in modo efficiente un file di testo vuoto da utilizzare per tali programmi.

Discussione

Creare file in modo diverso (continued)

Per evitare confusione in seguito, si consiglia di rimuovere il file appena creato prima di procedere con il resto dell’episodio, altrimenti i risultati futuri potrebbero variare rispetto a quelli forniti nella lezione. A tale scopo, utilizzare il seguente comando:

BASH

$ rm my_file.txt
Richiamo

Cosa c’è in un nome?

Avrete notato che tutti i file di Nelle si chiamano “qualcosa punto qualcosa”, e in questa parte della lezione abbiamo sempre usato l’estensione .txt. Questa è solo una convenzione; possiamo chiamare un file mythesis o qualsiasi altra cosa vogliamo. Tuttavia, la maggior parte delle persone usa nomi divisi in due parti per aiutare loro (e i loro programmi) a distinguere i diversi tipi di file. La seconda parte del nome è chiamata estensione del nome del file e indica il tipo di dati contenuti nel file: .txt segnala un file di testo semplice, .pdf indica un documento PDF, .cfg è un file di configurazione pieno di parametri per qualche programma o altro, .png è un’immagine PNG e così via.

Questa è solo una convenzione, anche se importante. I file contengono semplicemente dei byte; sta a noi e ai nostri programmi interpretarli secondo le regole dei file di testo, dei documenti PDF, dei file di configurazione, delle immagini e così via.

Nominare un’immagine PNG di una balena come whale.mp3 non la trasforma magicamente in una registrazione di un canto di balena, anche se potrebbe far sì che il sistema operativo associ il file a un programma di riproduzione musicale. In questo caso, se qualcuno fa doppio clic su whale.mp3 in un programma di esplorazione file, il lettore musicale tenterà automaticamente (ed erroneamente) di aprire il file whale.mp3.

Spostamento di file e directory


Ritorno alla cartella shell-lesson-data/exercise-data/writing,

BASH

$ cd ~/Desktop/shell-lesson-data/exercise-data/writing

Nella nostra cartella thesis abbiamo un file draft.txt che non è un nome particolarmente informativo, quindi cambiamo il nome del file usando mv, che è l’abbreviazione di ‘move’:

BASH

$ mv thesis/draft.txt thesis/quotes.txt

Il primo argomento dice a mv che cosa stiamo ‘spostando’, mentre il secondo è dove deve andare. In questo caso, stiamo spostando thesis/draft.txt in thesis/quotes.txt, il che ha lo stesso effetto di rinominare il file. Sicuramente, ls ci mostra che thesis ora contiene un file chiamato quotes.txt:

BASH

$ ls thesis

OUTPUT

quotes.txt

È necessario prestare attenzione quando si specifica il nome del file di destinazione, poiché mv sovrascriverà silenziosamente qualsiasi file esistente con lo stesso nome, con conseguente perdita di dati. Per impostazione predefinita, mv non chiede conferma prima di sovrascrivere i file. Tuttavia, un’opzione aggiuntiva, mv -i (o mv --interactive), farà sì che mv richieda tale conferma.

Si noti che mv funziona anche sulle cartelle.

Spostiamo quotes.txt nella cartella di lavoro corrente. Usiamo ancora una volta mv, ma questa volta useremo solo il nome di una cartella come secondo argomento per dire a mv che vogliamo mantenere il nome del file ma metterlo in un posto nuovo. (In questo caso, il nome della cartella che utilizziamo è il nome speciale della cartella . di cui abbiamo parlato in precedenza.

BASH

$ mv thesis/quotes.txt .

L’effetto è quello di spostare il file dalla cartella in cui si trovava alla cartella di lavoro corrente. ls ora mostra che thesis è vuoto:

BASH

$ ls thesis

OUTPUT

$

In alternativa, possiamo confermare che il file quotes.txt non è più presente nella cartella thesis provando esplicitamente a elencarlo:

BASH

$ ls thesis/quotes.txt

ERRORE

ls: cannot access 'thesis/quotes.txt': No such file or directory

ls con un nome di file o una cartella come argomento elenca solo il file o la cartella richiesti. Se il file dato come argomento non esiste, la shell restituisce un errore come abbiamo visto sopra. Possiamo usare questa funzione per vedere che quotes.txt è ora presente nella nostra cartella corrente:

BASH

$ ls quotes.txt

OUTPUT

quotes.txt
Sfida

Spostamento dei file in una nuova cartella

Dopo aver eseguito i seguenti comandi, Jamie si rende conto di aver inserito i file sucrose.dat e maltose.dat nella cartella sbagliata. I file avrebbero dovuto essere inseriti nella cartella raw.

BASH

$ ls -F
 analyzed/ raw/
$ ls -F analyzed
fructose.dat glucose.dat maltose.dat sucrose.dat
$ cd analyzed

riempire gli spazi vuoti per spostare questi file nella cartella raw/ (cioè quella in cui si è dimenticato di metterli)

BASH

$ mv sucrose.dat maltose.dat ____/____

BASH

$ mv sucrose.dat maltose.dat ../raw

Ricordate che .. si riferisce alla cartella padre (cioè quella sopra la cartella corrente) e che . si riferisce alla cartella corrente.

Copia di file e directory


Il comando cp funziona in modo molto simile a mv, solo che copia un file invece di spostarlo. Possiamo verificare che abbia fatto la cosa giusta usando ls con due percorsi come argomenti — come la maggior parte dei comandi Unix, a ls possono essere dati più percorsi contemporaneamente:

BASH

$ cp quotes.txt thesis/quotations.txt
$ ls quotes.txt thesis/quotations.txt

OUTPUT

quotes.txt   thesis/quotations.txt

Possiamo anche copiare una cartella e tutto il suo contenuto usando l’opzione recursive -r, ad esempio per fare il backup di una cartella:

BASH

$ cp -r thesis thesis_backup

Possiamo verificare il risultato elencando il contenuto di entrambe le cartelle thesis e thesis_backup:

BASH

$ ls thesis thesis_backup

OUTPUT

thesis:
quotations.txt

thesis_backup:
quotations.txt

È importante includere il flag -r. Se si vuole copiare una cartella e si omette questa opzione, verrà visualizzato un messaggio che indica che la cartella è stata omessa perché -r not specified.

BASH

$ cp thesis thesis_backup
cp: -r not specified; omitting directory 'thesis'
Sfida

Rinominare i file

Supponiamo di aver creato un file di testo semplice nella nostra cartella corrente per contenere un elenco dei test statistici che dovremo fare per analizzare i nostri dati, e di averlo chiamato statstics.txt

Dopo aver creato e salvato questo file ci si accorge di aver sbagliato il nome del file! Per correggere l’errore, quale dei seguenti comandi si può utilizzare?

  1. cp statstics.txt statistics.txt
  2. mv statstics.txt statistics.txt
  3. mv statstics.txt .
  4. cp statstics.txt .
  1. No. Sebbene questa operazione crei un file con il nome corretto, il file con il nome sbagliato esiste ancora nella cartella e deve essere cancellato.
  2. Sì, questo funzionerebbe per rinominare il file.
  3. No, il punto(.) indica dove spostare il file, ma non fornisce un nuovo nome di file; non è possibile creare nomi di file identici.
  4. No, il punto(.) indica dove copiare il file, ma non fornisce un nuovo nome di file; non è possibile creare nomi di file identici.
Sfida

Spostare e copiare

Qual è l’output del comando di chiusura ls nella sequenza mostrata sotto?

BASH

$ pwd

OUTPUT

/Users/jamie/data

BASH

$ ls

OUTPUT

proteins.dat

BASH

$ mkdir recombined
$ mv proteins.dat recombined/
$ cp recombined/proteins.dat ../proteins-saved.dat
$ ls
  1. proteins-saved.dat recombined
  2. recombined
  3. proteins.dat recombined
  4. proteins-saved.dat

Partiamo dalla cartella /Users/jamie/data e creiamo una nuova cartella chiamata recombined. La seconda riga sposta (mv) il file proteins.dat nella nuova cartella (recombined). La terza riga crea una copia del file appena spostato. La parte difficile è dove il file è stato copiato. Ricordiamo che .. significa “salire di livello”, quindi il file copiato si trova ora in /Users/jamie. Si noti che .. è interpretato rispetto alla cartella di lavoro corrente, non rispetto alla posizione del file copiato. Quindi, l’unica cosa che verrà mostrata usando ls (in /Users/jamie/data) è la cartella ricombinata.

  1. No, vedi spiegazione sopra. proteins-saved.dat si trova in /Users/jamie
  2. Si
  3. No, vedi spiegazione sopra. proteins.dat si trova in /Users/jamie/data/recombined
  4. No, vedi spiegazione sopra. proteins-saved.dat si trova in /Users/jamie

Rimozione di file e cartella


Tornando alla cartella shell-lesson-data/exercise-data/writing, riordiniamola rimuovendo il file quotes.txt che abbiamo creato. Il comando Unix che useremo a questo scopo è rm (abbreviazione di ‘remove’):

BASH

$ rm quotes.txt

Possiamo confermare che il file è andato via usando ls:

BASH

$ ls quotes.txt

ERRORE

ls: cannot access 'quotes.txt': No such file or directory
Richiamo

L’eliminazione è per sempre

La shell di Unix non ha un cestino da cui recuperare i file cancellati (anche se la maggior parte delle interfacce grafiche di Unix ce l’ha). Invece, quando si cancellano i file, questi vengono scollegati dal file system in modo che il loro spazio di archiviazione sul disco possa essere riciclato. Esistono strumenti per trovare e recuperare i file cancellati, ma non è detto che funzionino in una particolare situazione, poiché il computer potrebbe riciclare subito lo spazio su disco del file.

Sfida

Usare rm in modo sicuro

Cosa succede quando si esegue rm -i thesis_backup/quotations.txt? Perché si vuole questa protezione quando si usa rm?

OUTPUT

rm: remove regular file 'thesis_backup/quotations.txt'? y

L’opzione -i chiederà prima di (ogni) rimozione (usare Y per confermare la cancellazione o N per mantenere il file). La shell Unix non ha un cestino, quindi tutti i file rimossi spariranno per sempre. Utilizzando l’opzione -i, si ha la possibilità di verificare che si stiano eliminando solo i file che si desidera rimuovere.

Se si tenta di rimuovere la cartella thesis usando rm thesis, si ottiene un messaggio di errore:

BASH

$ rm thesis

ERRORE

rm: cannot remove 'thesis': Is a directory

Questo accade perché rm per impostazione predefinita funziona solo sui file, non sulle cartelle.

rm può rimuovere una cartella e tutto il suo contenuto se si usa l’opzione ricorsiva -r, e lo farà senza alcuna richiesta di conferma:

BASH

$ rm -r thesis

Dato che non c’è modo di recuperare i file cancellati usando la shell, rm -r dovrebbe essere usato con grande cautela (si potrebbe considerare di aggiungere l’opzione interattiva rm -r -i).

Operazioni con più file e cartelle


Spesso è necessario copiare o spostare più file contemporaneamente. Questo può essere fatto fornendo un elenco di singoli nomi di file o specificando un modello di denominazione utilizzando i caratteri jolly. I caratteri jolly sono caratteri speciali che possono essere utilizzati per rappresentare caratteri sconosciuti o insiemi di caratteri durante la navigazione nel file system Unix.

Sfida

Copia con più nomi di file

Per questo esercizio, è possibile provare i comandi nella cartella shell-lesson-data/exercise-data.

Nell’esempio seguente, cosa fa cp quando gli vengono dati diversi nomi di file e un nome di cartella?

BASH

$ mkdir backup
$ cp creatures/minotaur.dat creatures/unicorn.dat backup/

Nell’esempio seguente, cosa fa cp quando gli vengono dati tre o più nomi di file?

BASH

$ cd creatures
$ ls -F

OUTPUT

basilisk.dat  minotaur.dat  unicorn.dat

BASH

$ cp minotaur.dat unicorn.dat basilisk.dat

Se viene dato più di un nome di file seguito da un nome di cartella (cioè la cartella di destinazione deve essere l’ultimo argomento), cp copia i file nella cartella nominata.

Se vengono dati tre nomi di file, cp lancia un errore come quello che segue, perché si aspetta il nome di una cartella come ultimo argomento.

ERRORE

cp: target 'basilisk.dat' is not a directory

Uso dei caratteri jolly per accedere a più file contemporaneamente

Richiamo

Caratteri jolly

* è un wildcard, che rappresenta zero o più altri caratteri. Consideriamo la cartella shell-lesson-data/exercise-data/alkanes: *.pdb rappresenta ethane.pdb, propane.pdb e ogni file che termina con ‘.pdb’. D’altra parte, p*.pdb rappresenta solo pentane.pdb e propane.pdb, perché la ‘p’ all’inizio può rappresentare solo nomi di file che iniziano con la lettera ‘p’.

Anche ? è un carattere jolly, ma rappresenta esattamente un carattere. Quindi ?ethane.pdb potrebbe rappresentare methane.pdb mentre *ethane.pdb rappresenta sia ethane.pdb che methane.pdb.

I caratteri jolly possono essere usati in combinazione tra loro. Ad esempio, ???ane.pdb indica tre caratteri seguiti da ane.pdb, che danno cubane.pdb ethane.pdb octane.pdb.

Quando la shell vede un carattere jolly, lo espande per creare un elenco di nomi di file corrispondenti prima di eseguire il comando precedente. Come eccezione, se un’espressione con il carattere jolly non corrisponde a nessun file, Bash passerà l’espressione come argomento al comando così com’è. Per esempio, digitando ls *.pdf nella cartella alkanes (che contiene solo file con nomi che terminano con .pdb) si ottiene un messaggio di errore che indica che non esiste un file chiamato *.pdf. Tuttavia, generalmente comandi come wc e ls vedono gli elenchi di nomi di file che corrispondono a queste espressioni, ma non i caratteri jolly stessi. È la shell, non gli altri programmi, a espandere i caratteri jolly.

Sfida

Elenca i nomi dei file che corrispondono a uno schema

Quando vengono eseguiti nella cartella alkanes, quali comandi ls produrranno questo output?

ethane.pdb methane.pdb

  1. ls *t*ane.pdb
  2. ls *t?ne.*
  3. ls *t??ne.pdb
  4. ls ethane.*

La soluzione è 3.

1. mostra tutti i file il cui nome contiene zero o più caratteri (*) seguiti dalla lettera t, poi zero o più caratteri (*) seguiti da ane.pdb. Si ottiene così ethane.pdb methane.pdb octane.pdb pentane.pdb.

2. mostra tutti i file il cui nome inizia con zero o più caratteri (*) seguiti dalla lettera t, poi un singolo carattere (?), poi ne. seguito da zero o più caratteri (*). In questo modo si ottiene octane.pdb e pentane.pdb, ma non corrisponde a nulla che finisca in thane.pdb.

3. risolve i problemi dell’opzione 2 facendo corrispondere due caratteri (??) tra t e ne. Questa è la soluzione.

4. mostra solo i file che iniziano con ethane..

Sfida

Altro sui caratteri jolly

Sam ha una cartella contenente i dati di calibrazione, i set di dati e le descrizioni dei set di dati:

BASH

.
├── 2015-10-23-calibration.txt
├── 2015-10-23-dataset1.txt
├── 2015-10-23-dataset2.txt
├── 2015-10-23-dataset_overview.txt
├── 2015-10-26-calibration.txt
├── 2015-10-26-dataset1.txt
├── 2015-10-26-dataset2.txt
├── 2015-10-26-dataset_overview.txt
├── 2015-11-23-calibration.txt
├── 2015-11-23-dataset1.txt
├── 2015-11-23-dataset2.txt
├── 2015-11-23-dataset_overview.txt
├── backup
│   ├── calibration
│   └── datasets
└── send_to_bob
    ├── all_datasets_created_on_a_23rd
    └── all_november_files

Prima di partire per un’altra gita, Sam vuole fare il backup dei suoi dati e inviare alcuni set di dati al suo collega Bob. Per farlo, Sam usa i seguenti comandi:

BASH

$ cp *dataset* backup/datasets
$ cp ____calibration____ backup/calibration
$ cp 2015-____-____ send_to_bob/all_november_files/
$ cp ____ send_to_bob/all_datasets_created_on_a_23rd/

Aiutate Sam a riempire gli spazi vuoti.

La struttura risultante dovrebbe essere la seguente

BASH

.
├── 2015-10-23-calibration.txt
├── 2015-10-23-dataset1.txt
├── 2015-10-23-dataset2.txt
├── 2015-10-23-dataset_overview.txt
├── 2015-10-26-calibration.txt
├── 2015-10-26-dataset1.txt
├── 2015-10-26-dataset2.txt
├── 2015-10-26-dataset_overview.txt
├── 2015-11-23-calibration.txt
├── 2015-11-23-dataset1.txt
├── 2015-11-23-dataset2.txt
├── 2015-11-23-dataset_overview.txt
├── backup
│   ├── calibration
│   │   ├── 2015-10-23-calibration.txt
│   │   ├── 2015-10-26-calibration.txt
│   │   └── 2015-11-23-calibration.txt
│   └── datasets
│       ├── 2015-10-23-dataset1.txt
│       ├── 2015-10-23-dataset2.txt
│       ├── 2015-10-23-dataset_overview.txt
│       ├── 2015-10-26-dataset1.txt
│       ├── 2015-10-26-dataset2.txt
│       ├── 2015-10-26-dataset_overview.txt
│       ├── 2015-11-23-dataset1.txt
│       ├── 2015-11-23-dataset2.txt
│       └── 2015-11-23-dataset_overview.txt
└── send_to_bob
    ├── all_datasets_created_on_a_23rd
    │   ├── 2015-10-23-dataset1.txt
    │   ├── 2015-10-23-dataset2.txt
    │   ├── 2015-10-23-dataset_overview.txt
    │   ├── 2015-11-23-dataset1.txt
    │   ├── 2015-11-23-dataset2.txt
    │   └── 2015-11-23-dataset_overview.txt
    └── all_november_files
        ├── 2015-11-23-calibration.txt
        ├── 2015-11-23-dataset1.txt
        ├── 2015-11-23-dataset2.txt
        └── 2015-11-23-dataset_overview.txt

BASH

$ cp *calibration.txt backup/calibration
$ cp 2015-11-* send_to_bob/all_november_files/
$ cp *-23-dataset* send_to_bob/all_datasets_created_on_a_23rd/
Sfida

Organizzazione di cartelle e file

Jamie sta lavorando a un progetto e vede che i suoi file non sono ben organizzati:

BASH

$ ls -F

OUTPUT

analyzed/  fructose.dat    raw/   sucrose.dat

I file fructose.dat e sucrose.dat contengono l’output dell’analisi dei dati. Quale/i comando/i trattato/i in questa lezione deve eseguire affinché i comandi sottostanti producano l’output mostrato?

BASH

$ ls -F

OUTPUT

analyzed/   raw/

BASH

$ ls analyzed

OUTPUT

fructose.dat    sucrose.dat

BASH

mv *.dat analyzed

Jamie deve spostare i suoi file fructose.dat e sucrose.dat nella cartella analyzed. La shell espanderà *.dat in modo che corrisponda a tutti i file .dat presenti nella cartella corrente. Il comando mv sposta quindi l’elenco dei file .dat nella cartella ‘analyzed’.

Sfida

Riprodurre la struttura della cartella

si sta iniziando un nuovo esperimento e si vuole duplicare la struttura delle directory dell’esperimento precedente per poter aggiungere nuovi dati.

Si supponga che l’esperimento precedente si trovi in una cartella chiamata 2016-05-18, che contiene una cartella data che a sua volta contiene cartelle chiamate raw e processed che contengono file di dati. L’obiettivo è copiare la struttura delle cartelle della cartella 2016-05-18 in una cartella chiamata 2016-05-20, in modo che la struttura finale delle cartelle sia la seguente:

OUTPUT

2016-05-20/
└── data
   ├── processed
   └── raw

Quale dei seguenti comandi raggiungerebbe questo obiettivo? Cosa farebbero gli altri comandi?

BASH

$ mkdir 2016-05-20
$ mkdir 2016-05-20/data
$ mkdir 2016-05-20/data/processed
$ mkdir 2016-05-20/data/raw

BASH

$ mkdir 2016-05-20
$ cd 2016-05-20
$ mkdir data
$ cd data
$ mkdir raw processed

BASH

$ mkdir 2016-05-20/data/raw
$ mkdir 2016-05-20/data/processed

BASH

$ mkdir -p 2016-05-20/data/raw
$ mkdir -p 2016-05-20/data/processed

BASH

$ mkdir 2016-05-20
$ cd 2016-05-20
$ mkdir data
$ mkdir raw processed

Le prime due serie di comandi raggiungono questo obiettivo. La prima serie utilizza percorsi relativi per creare la cartella di primo livello prima delle sottocartelle.

La terza serie di comandi darà un errore perché il comportamento predefinito di mkdir non crea una sottocartella di una cartella inesistente: le cartelle di livello intermedio devono essere create per prime.

La quarta serie di comandi raggiunge questo obiettivo. Ricordate che l’opzione -p, seguita da un percorso di una o più cartelle, farà sì che mkdir crei tutte le sottocartelle intermedie necessarie.

La serie finale di comandi genera le cartelle ‘raw’ e ‘processed’ allo stesso livello della cartella ‘data’.

Punti Chiave
  • cp [old] [new] copia un file.
  • mkdir [path] crea una nuova cartella.
  • mv [old] [new] sposta (rinomina) un file o una cartella.
  • rm [path] rimuove (elimina) un file.
  • * corrisponde a zero o più caratteri in un nome di file, quindi *.txt corrisponde a tutti i file che terminano in .txt.
  • ? corrisponde a qualsiasi singolo carattere in un nome di file, quindi ?.txt corrisponde a a.txt ma non a any.txt.
  • L’uso del tasto Control può essere descritto in molti modi, tra cui Ctrl-X, Control-X e ^X.
  • La shell non ha un cestino: una volta che qualcosa è stato cancellato, è davvero sparito.
  • La maggior parte dei nomi dei file è something.extension. L’estensione non è necessaria e non garantisce nulla, ma viene normalmente utilizzata per indicare il tipo di dati contenuti nel file.
  • A seconda del tipo di lavoro svolto, potrebbe essere necessario un editor di testo più potente di Nano.

Content from Tubi e filtri


Ultimo aggiornamento il 2025-11-07 | Modifica questa pagina

Tempo stimato: 35 minuti

Panoramica

Domande

  • Come posso combinare i comandi esistenti per produrre l’output desiderato?
  • Come posso mostrare solo una parte dell’output?

Obiettivi

  • Spiegare il vantaggio di collegare i comandi con pipe e filtri.
  • Combinare sequenze di comandi per ottenere un nuovo output
  • Reindirizzare l’output di un comando a un file.
  • Spiegate cosa succede di solito se a un programma o a una pipeline non viene dato alcun input da elaborare.

Ora che conosciamo alcuni comandi di base, possiamo finalmente esaminare la caratteristica più potente della shell: la facilità con cui ci permette di combinare programmi esistenti in modi nuovi. Inizieremo con la cartella shell-lesson-data/exercise-data/alkanes che contiene sei file che descrivono alcune semplici molecole organiche. L’estensione .pdb indica che questi file sono in formato Protein Data Bank, un semplice formato di testo che specifica il tipo e la posizione di ogni atomo nella molecola.

BASH

$ ls

OUTPUT

cubane.pdb    methane.pdb    pentane.pdb
ethane.pdb    octane.pdb     propane.pdb

Eseguiamo un comando di esempio:

BASH

$ wc cubane.pdb

OUTPUT

20  156 1158 cubane.pdb

wc è il comando “contaparole”: conta il numero di righe, parole e caratteri nei file (restituendo i valori in questo ordine, da sinistra a destra).

Se si esegue il comando wc *.pdb, il * in *.pdb corrisponde a zero o più caratteri, quindi la shell trasforma *.pdb in un elenco di tutti i file .pdb nella cartella corrente:

BASH

$ wc *.pdb

OUTPUT

  20  156  1158  cubane.pdb
  12  84   622   ethane.pdb
   9  57   422   methane.pdb
  30  246  1828  octane.pdb
  21  165  1226  pentane.pdb
  15  111  825   propane.pdb
 107  819  6081  total

Si noti che wc *.pdb mostra anche il numero totale di tutte le righe nell’ultima riga dell’output.

Se si esegue wc -l invece di wc, l’output mostra solo il numero di righe per file:

BASH

$ wc -l *.pdb

OUTPUT

  20  cubane.pdb
  12  ethane.pdb
   9  methane.pdb
  30  octane.pdb
  21  pentane.pdb
  15  propane.pdb
 107  total

Le opzioni -m e -w possono essere usate anche con il comando wc per mostrare rispettivamente solo il numero di caratteri o il numero di parole.

Richiamo

Perché non fa nulla?

Cosa succede se un comando deve elaborare un file, ma non gli viene assegnato un nome? Ad esempio, cosa succede se digitiamo:

BASH

$ wc -l

ma non digita *.pdb (o qualsiasi altra cosa) dopo il comando? Poiché non ha alcun nome di file, wc presume di dover elaborare l’input dato dal prompt dei comandi, quindi se ne sta lì ad aspettare che gli si forniscano dei dati in modo interattivo. Dall’esterno, però, tutto ciò che si vede è che è seduto lì e il comando non sembra fare nulla.

Se commettete questo tipo di errore, potete uscire da questo stato tenendo premuto il tasto control (Ctrl) e premendo una volta la lettera C: Ctrl+C. Quindi rilasciare entrambi i tasti.

Cattura dell’output da comandi


Quale di questi file contiene il minor numero di righe? È facile rispondere a questa domanda quando ci sono solo sei file, ma se ce ne fossero 6000? Il primo passo verso la soluzione consiste nell’eseguire il comando:

BASH

$ wc -l *.pdb > lengths.txt

Il simbolo maggiore di, >, indica alla shell di reindirizzare l’output del comando a un file invece di stamparlo sullo schermo. Questo comando non stampa alcun output sullo schermo, perché tutto ciò che wc avrebbe stampato è andato invece nel file lengths.txt. Se il file non esiste prima dell’emissione del comando, la shell lo creerà. Se il file esiste già, verrà sovrascritto silenziosamente, con conseguente perdita di dati. Pertanto, i comandi redirect richiedono cautela.

ls lengths.txt conferma che il file esiste:

BASH

$ ls lengths.txt

OUTPUT

lengths.txt

Ora possiamo inviare il contenuto di lengths.txt allo schermo usando cat lengths.txt. Il comando cat prende il nome da ‘concatenate’, cioè unire insieme, e stampa il contenuto dei file uno dopo l’altro. In questo caso c’è un solo file, quindi cat ci mostra solo ciò che contiene:

BASH

$ cat lengths.txt

OUTPUT

  20  cubane.pdb
  12  ethane.pdb
   9  methane.pdb
  30  octane.pdb
  21  pentane.pdb
  15  propane.pdb
 107  total
Richiamo

Output Pagina per Pagina

In questa lezione continueremo a usare cat per comodità e coerenza, ma ha lo svantaggio di scaricare sempre l’intero file sullo schermo. Più utile nella pratica è il comando less (ad esempio less lengths.txt). Questo comando visualizza una schermata del file e poi si ferma. Si può andare avanti di una schermata premendo la barra spaziatrice, o indietro di una premendo b. Premere q per uscire.

Filtraggio dell’output


Il prossimo passo sarà quello di usare il comando sort per ordinare il contenuto del file lengths.txt. Ma prima faremo un esercizio per imparare qualcosa sul comando sort:

Sfida

Cosa fa sort -n?

Il file shell-lesson-data/exercise-data/numbers.txt contiene le seguenti righe:

10
2
19
22
6

Se eseguiamo sort su questo file, l’output è:

OUTPUT

10
19
2
22
6

Se si esegue sort -n sullo stesso file, si ottiene invece questo:

OUTPUT

2
6
10
19
22

Spiegare perché -n ha questo effetto.

L’opzione -n specifica un ordinamento numerico anziché alfanumerico.

Useremo anche l’opzione -n per specificare che l’ordinamento è numerico invece che alfanumerico. Questo non modifica il file, ma invia il risultato dell’ordinamento allo schermo:

BASH

$ sort -n lengths.txt

OUTPUT

  9  methane.pdb
 12  ethane.pdb
 15  propane.pdb
 20  cubane.pdb
 21  pentane.pdb
 30  octane.pdb
107  total

Possiamo mettere l’elenco ordinato di righe in un altro file temporaneo chiamato sorted-lengths.txt mettendo > sorted-lengths.txt dopo il comando, proprio come abbiamo usato > lengths.txt per mettere l’output di wc in lengths.txt. Una volta fatto questo, si può eseguire un altro comando chiamato head per ottenere le prime righe in sorted-lengths.txt:

BASH

$ sort -n lengths.txt > sorted-lengths.txt
$ head -n 1 sorted-lengths.txt

OUTPUT

  9  methane.pdb

L’uso di -n 1 con head indica che vogliamo solo la prima riga del file; -n 20 otterrebbe le prime 20 e così via. Poiché sorted-lengths.txt contiene le lunghezze dei file ordinate dalla minore alla maggiore, l’output di head deve essere il file con il minor numero di righe.

Richiamo

Reindirizzamento allo stesso file

È una pessima idea cercare di reindirizzare l’output di un comando che opera su un file allo stesso file. Ad esempio:

BASH

$ sort -n lengths.txt > lengths.txt

Fare qualcosa del genere potrebbe dare risultati errati e/o cancellare il contenuto di lengths.txt.

Sfida

Cosa significa >>?

Abbiamo visto l’uso di >, ma esiste un operatore simile >> che funziona in modo leggermente diverso. Impareremo le differenze tra questi due operatori stampando alcune stringhe. Possiamo usare il comando echo per stampare delle stringhe, ad esempio

BASH

$ echo The echo command prints text

OUTPUT

The echo command prints text

Ora provate i comandi sottostanti per scoprire la differenza tra i due operatori:

BASH

$ echo hello > testfile01.txt

e:

BASH

$ echo hello >> testfile02.txt

Suggerimento: provate a eseguire ogni comando due volte di seguito e poi esaminate i file di output.

Nel primo esempio con >, la stringa ‘hello’ viene scritta in testfile01.txt, ma il file viene sovrascritto ogni volta che si esegue il comando.

Vediamo dal secondo esempio che anche l’operatore >> scrive ‘hello’ in un file (in questo caso testfile02.txt), ma aggiunge la stringa al file se questo esiste già (cioè quando lo eseguiamo per la seconda volta).

Sfida

Applicazione dei dati

Abbiamo già incontrato il comando head, che stampa le righe dall’inizio di un file. il comando tail è simile, ma stampa le righe dalla fine di un file.

Considerare il file shell-lesson-data/exercise-data/animal-counts/animals.csv. Dopo questi comandi, selezionare la risposta che corrisponde al file animals-subset.csv:

BASH

$ head -n 3 animals.csv > animals-subset.csv
$ tail -n 2 animals.csv >> animals-subset.csv
  1. Le prime tre righe di animals.csv
  2. le ultime due righe di animals.csv
  3. Le prime tre righe e le ultime due righe di animals.csv
  4. La seconda e la terza riga di animals.csv

L’opzione 3 è corretta. Affinché l’opzione 1 sia corretta, si deve eseguire solo il comando head. Per l’opzione 2 è corretto eseguire solo il comando tail. Affinché l’opzione 4 sia corretta, occorre eseguire il pipe dell’output di head in tail -n 2 facendo head -n 3 animals.csv | tail -n 2 > animals-subset.csv

Passaggio dell’output a un altro comando


Nel nostro esempio di ricerca del file con il minor numero di righe, stiamo usando due file intermedi lengths.txt e sorted-lengths.txt per memorizzare l’output. Questo è un modo confuso di lavorare, perché anche una volta capito cosa fanno wc, sort e head, questi file intermedi rendono difficile capire cosa sta succedendo. Si può semplificare la comprensione eseguendo sort e head insieme:

BASH

$ sort -n lengths.txt | head -n 1

OUTPUT

  9  methane.pdb

La barra verticale, |, tra i due comandi è chiamata pipe. Indica alla shell che vogliamo usare l’output del comando a sinistra come input del comando a destra.

Questo ha eliminato la necessità del file sorted-lengths.txt.

Combinazione di più comandi


Nulla ci impedisce di concatenare le pipe consecutivamente. Ad esempio, possiamo inviare l’output di wc direttamente a sort, e poi inviare l’output risultante a head. Questo elimina la necessità di file intermedi.

Inizieremo usando una pipe per inviare l’output di wc a sort:

BASH

$ wc -l *.pdb | sort -n

OUTPUT

   9 methane.pdb
  12 ethane.pdb
  15 propane.pdb
  20 cubane.pdb
  21 pentane.pdb
  30 octane.pdb
 107 total

Possiamo quindi inviare l’output attraverso un’altra pipe, a head, in modo che la pipeline completa diventi:

BASH

$ wc -l *.pdb | sort -n | head -n 1

OUTPUT

   9  methane.pdb

Questo è esattamente come un matematico che annida funzioni come log(3x) e dice “il log di tre volte x”. Nel nostro caso, l’algoritmo è “testa dell’ordinamento del numero di righe di *.pdb”.

Il reindirizzamento e le pipe utilizzate negli ultimi comandi sono illustrati di seguito:

{alt=‘Redirects and Pipes di diversi comandi: “wc -l *.pdb” indirizzerà l’output alla shell. “wc -l *.pdb > lunghezze” dirige l’output al file “lunghezze”. “wc -l *.pdb | sort -n | head -n 1” crea una pipeline in cui l’output del comando “wc” è l’input del comando “sort”, l’output del comando “sort” è l’input del comando “head” e l’output del comando “head” è diretto alla shell’}

Sfida

Comandi piping insieme

Nella nostra cartella corrente, vogliamo trovare i 3 file che hanno il minor numero di righe. Quale comando elencato di seguito potrebbe funzionare?

  1. wc -l * > sort -n > head -n 3
  2. wc -l * | sort -n | head -n 1-3
  3. wc -l * | head -n 3 | sort -n
  4. wc -l * | sort -n | head -n 3

L’opzione 4 è la soluzione. Il carattere pipe | viene usato per collegare l’uscita di un comando all’ingresso di un altro. il carattere > è usato per reindirizzare l’output standard a un file. Provatelo nella cartella shell-lesson-data/exercise-data/alkanes!

Strumenti progettati per funzionare insieme


Questa idea di collegare i programmi tra loro è il motivo per cui Unix ha avuto tanto successo. Invece di creare programmi enormi che cercano di fare molte cose diverse, i programmatori Unix si concentrano sulla creazione di molti strumenti semplici che svolgono bene un lavoro ciascuno e che funzionano bene tra loro. Questo modello di programmazione è chiamato “pipe e filtri”. Abbiamo già visto le pipe; un filtro è un programma come wc o sort che trasforma un flusso di input in un flusso di output. Quasi tutti gli strumenti standard di Unix possono funzionare in questo modo. A meno che non venga detto di fare diversamente, essi leggono dallo standard input, fanno qualcosa con ciò che hanno letto e scrivono sullo standard output.

La chiave è che ogni programma che legge righe di testo dallo standard input e scrive righe di testo sullo standard output può essere combinato con ogni altro programma che si comporta in questo modo. Potete e dovreste scrivere i vostri programmi in questo modo, in modo che voi e altre persone possiate inserire quei programmi nelle pipe per moltiplicarne la potenza.

Sfida

Comprensione della lettura di pipe

Un file chiamato animals.csv (nella cartella shell-lesson-data/exercise-data/animal-counts) contiene i seguenti dati:

2012-11-05,deer,5
2012-11-05,rabbit,22
2012-11-05,raccoon,7
2012-11-06,rabbit,19
2012-11-06,deer,2
2012-11-06,fox,4
2012-11-07,rabbit,16
2012-11-07,bear,1

Quale testo passa attraverso ciascuna delle pipe e il redirect finale nella pipeline sottostante? Si noti che il comando sort -r ordina in ordine inverso.

BASH

$ cat animals.csv | head -n 5 | tail -n 3 | sort -r > final.txt

Suggerimento: costruite la pipeline un comando alla volta per verificare la vostra comprensione

Il comando head estrae le prime 5 righe da animals.csv. Poi, le ultime 3 righe vengono estratte dalle precedenti 5 usando il comando tail. Con il comando sort -r queste 3 righe vengono ordinate in ordine inverso. Infine, l’output viene reindirizzato in un file: final.txt. Il contenuto di questo file può essere controllato eseguendo il comando cat final.txt. Il file dovrebbe contenere le seguenti righe:

2012-11-06,rabbit,19
2012-11-06,deer,2
2012-11-05,raccoon,7
Sfida

Costruzione di un tubo

Per il file animals.csv dell’esercizio precedente, considerate il seguente comando:

BASH

$ cut -d , -f 2 animals.csv

Il comando cut è usato per rimuovere o “tagliare” certe sezioni di ogni riga del file, e cut si aspetta che le righe siano separate in colonne da un carattere Tab. Un carattere usato in questo modo è chiamato delimitatore. Nell’esempio precedente si usa l’opzione -d per specificare la virgola come carattere delimitatore. Abbiamo anche usato l’opzione -f per specificare che vogliamo estrarre il secondo campo (colonna). Si ottiene così il seguente risultato:

OUTPUT

deer
rabbit
raccoon
rabbit
deer
fox
rabbit
bear

Il comando uniq filtra le righe adiacenti corrispondenti in un file. Come si potrebbe estendere questa pipeline (usando uniq e un altro comando) per scoprire quali animali contiene il file (senza duplicati nei loro nomi)?

BASH

$ cut -d , -f 2 animals.csv | sort | uniq
Sfida

Quale tubo?

Il file animals.csv contiene 8 righe di dati formattati come segue:

OUTPUT

2012-11-05,deer,5
2012-11-05,rabbit,22
2012-11-05,raccoon,7
2012-11-06,rabbit,19
...

Il comando uniq ha un’opzione -c che fornisce un conteggio del numero di volte in cui una riga è presente nel suo input. Supponendo che la vostra directory corrente sia shell-lesson-data/exercise-data/animal-counts, quale comando usereste per produrre una tabella che mostri il numero totale di ogni tipo di animale nel file?

  1. sort animals.csv | uniq -c
  2. sort -t, -k2,2 animals.csv | uniq -c
  3. cut -d, -f 2 animals.csv | uniq -c
  4. cut -d, -f 2 animals.csv | sort | uniq -c
  5. cut -d, -f 2 animals.csv | sort | uniq -c | wc -l

L’opzione 4. è la risposta corretta. Se avete difficoltà a capire perché, provate a eseguire i comandi o le sottosezioni delle pipeline (assicuratevi di essere nella cartella shell-lesson-data/exercise-data/animal-counts).

Pipeline di Nelle: Controllo dei file


Nelle ha fatto passare i suoi campioni attraverso le macchine di analisi e ha creato 17 file nella cartella north-pacific-gyre descritta in precedenza. Per un rapido controllo, partendo dalla cartella shell-lesson-data, Nelle digita:

BASH

$ cd north-pacific-gyre
$ wc -l *.txt

l’output è costituito da 18 righe che assomigliano a queste:

OUTPUT

300 NENE01729A.txt
300 NENE01729B.txt
300 NENE01736A.txt
300 NENE01751A.txt
300 NENE01751B.txt
300 NENE01812A.txt
... ...

Ora digita questo:

BASH

$ wc -l *.txt | sort -n | head -n 5

OUTPUT

 240 NENE02018B.txt
 300 NENE01729A.txt
 300 NENE01729B.txt
 300 NENE01736A.txt
 300 NENE01751A.txt

Ops: uno dei file è 60 righe più corto degli altri. Quando torna indietro e controlla, vede che ha fatto quel saggio alle 8:00 di lunedì mattina — probabilmente qualcuno ha usato la macchina nel fine settimana e lei ha dimenticato di resettarla. Prima di rieseguire il campione, controlla se qualche file ha troppi dati:

BASH

$ wc -l *.txt | sort -n | tail -n 5

OUTPUT

 300 NENE02040B.txt
 300 NENE02040Z.txt
 300 NENE02043A.txt
 300 NENE02043B.txt
5040 total

I numeri sembrano buoni, ma cosa ci fa quella “Z” nella terzultima riga? Tutti i suoi campioni dovrebbero essere contrassegnati con ‘A’ o ‘B’; per convenzione, il suo laboratorio usa la ‘Z’ per indicare i campioni con informazioni mancanti. Per trovare altri campioni simili, fa così:

BASH

$ ls *Z.txt

OUTPUT

NENE01971Z.txt    NENE02040Z.txt

Quando controlla il registro sul suo portatile, non c’è alcuna profondità registrata per nessuno dei due campioni. Poiché è troppo tardi per ottenere le informazioni in altro modo, deve escludere questi due file dalla sua analisi. Potrebbe eliminarli usando rm, ma in realtà ci sono analisi che potrebbe fare in seguito in cui la profondità non è importante, quindi dovrà fare attenzione a selezionare i file usando le espressioni jolly NENE*A.txt NENE*B.txt.

Sfida

Rimozione dei file non necessari

Supponiamo di voler cancellare i file dei dati elaborati e di voler conservare solo i file grezzi e lo script di elaborazione per risparmiare spazio. I file grezzi terminano in .dat e quelli elaborati in .txt. Quale delle seguenti operazioni rimuoverà tutti i file di dati elaborati e solo i file di dati elaborati?

  1. rm ?.txt
  2. rm *.txt
  3. rm * .txt
  4. rm *.*
  1. Questo rimuove i file .txt con nomi di un solo carattere
  2. Questa è la risposta corretta
  3. La shell espanderebbe * per far corrispondere tutto ciò che si trova nella cartella corrente, quindi il comando cercherebbe di rimuovere tutti i file corrispondenti e un file aggiuntivo chiamato .txt
  4. La shell espande *.* per far corrispondere tutti i nomi di file contenenti almeno un ., compresi i file elaborati (.txt) e i file grezzi (.dat)
Punti Chiave
  • wc conta righe, parole e caratteri nei suoi input.
  • cat mostra il contenuto dei suoi input.
  • sort ordina i suoi input.
  • head visualizza le prime 10 righe del suo input per impostazione predefinita, senza ulteriori argomenti.
  • tail visualizza le ultime 10 righe del suo input per impostazione predefinita, senza ulteriori argomenti.
  • command > [file] reindirizza l’output di un comando a un file (sovrascrivendo qualsiasi contenuto esistente).
  • command >> [file] aggiunge l’output di un comando a un file.
  • [first] | [second] è una pipeline: l’output del primo comando viene usato come input del secondo.
  • Il modo migliore per usare la shell è usare le pipe per combinare semplici programmi monouso (filtri).

Content from Cicli


Ultimo aggiornamento il 2025-11-07 | Modifica questa pagina

Tempo stimato: 50 minuti

Panoramica

Domande

  • Come posso eseguire le stesse azioni su molti file diversi?

Obiettivi

  • Scrivere un ciclo che applichi uno o più comandi separatamente a ciascun file di un insieme di file.
  • Tracciare dei valori assunti da una variabile del ciclo durante la sua esecuzione.
  • Spiegare la differenza tra il nome di una variabile e il suo valore.
  • Spiegare perché gli spazi e alcuni caratteri di punteggiatura non dovrebbero essere usati nei nomi dei file.
  • Dimostrare come vedere quali comandi sono stati eseguiti di recente.
  • Eseguire nuovamente i comandi eseguiti di recente senza riscriverli.

I loop sono un costrutto di programmazione che ci permette di ripetere un comando o un insieme di comandi per ogni elemento di un elenco. In quanto tali, sono fondamentali per migliorare la produttività attraverso l’automazione. Analogamente ai caratteri jolly e al completamento delle schede, l’uso dei cicli riduce anche la quantità di battitura necessaria (e quindi il numero di errori di battitura).

Supponiamo di avere diverse centinaia di file di dati del genoma denominati basilisk.dat, minotaur.dat e unicorn.dat. Per questo esempio, utilizzeremo la directory exercise-data/creatures che contiene solo tre file di esempio, ma i principi possono essere applicati a molti più file contemporaneamente.

La struttura di questi file è la stessa: il nome comune, la classificazione e la data di aggiornamento sono presentati sulle prime tre righe, con le sequenze di DNA sulle righe successive. Esaminiamo i file:

BASH

$ head -n 5 basilisk.dat minotaur.dat unicorn.dat

Vorremmo stampare la classificazione di ogni specie, che è riportata nella seconda riga di ogni file. Per ogni file, dobbiamo eseguire il comando head -n 2 e inviarlo a tail -n 1. Per risolvere questo problema utilizzeremo un ciclo, ma prima vediamo la forma generale di un ciclo, utilizzando lo pseudo-codice seguente:

BASH

# "for" indica l'inizio di un "For-loop" 
for thing in list_of_things 
# "do" indica l'inizio dell'esecuzione
do 
    # L'intentazione  non è richiesta ma rende il codice più leggibile
    operation_using/command $thing 
# "done" indica la fine del for loop
done  

e possiamo applicarla al nostro esempio in questo modo:

BASH

$ for filename in basilisk.dat minotaur.dat unicorn.dat
> do
>     echo $filename
>     head -n 2 $filename | tail -n 1
> done

OUTPUT

basilisk.dat
CLASSIFICATION: basiliscus vulgaris
minotaur.dat
CLASSIFICATION: bos hominus
unicorn.dat
CLASSIFICATION: equus monoceros
Richiamo

Seguire il prompt

Il prompt della shell cambia da $ a > e viceversa mentre digitiamo il nostro ciclo. Il secondo prompt, >, è diverso per ricordarci che non abbiamo ancora finito di digitare un comando completo. Il punto e virgola, ;, può essere usato per separare due comandi scritti su una singola riga.

Quando la shell vede la parola chiave for, sa che deve ripetere un comando (o un gruppo di comandi) una volta per ogni elemento di un elenco. Ogni volta che il ciclo viene eseguito (chiamato iterazione), un elemento dell’elenco viene assegnato in sequenza alla variabile e i comandi all’interno del ciclo vengono eseguiti, prima di passare all’elemento successivo dell’elenco. All’interno del ciclo, si richiede il valore della variabile anteponendo $. La $ indica all’interprete della shell di trattare la variabile come un nome di variabile e di sostituire il suo valore al suo posto, anziché trattarla come un testo o un comando esterno.

In questo esempio, l’elenco è costituito da tre nomi di file: basilisk.dat, minotaur.dat e unicorn.dat. Ogni volta che il ciclo itera, si usa prima echo per stampare il valore che la variabile $filename contiene attualmente. Questo non è necessario ai fini del risultato, ma è utile per seguire più facilmente la procedura. Successivamente, verrà eseguito il comando head sul file a cui fa riferimento $filename. La prima volta che viene eseguito il ciclo, $filename è basilisk.dat. L’interprete esegue il comando head su basilisk.dat e trasmette le prime due righe al comando tail, che stampa la seconda riga di basilisk.dat. Per la seconda iterazione, $filename diventa minotaur.dat. Questa volta, la shell esegue head su minotaur.dat e invia le prime due righe al comando tail, che stampa la seconda riga di minotaur.dat. Per la terza iterazione, $filename diventa unicorn.dat, quindi la shell esegue il comando head su quel file e tail sul suo output. Poiché l’elenco era composto da soli tre elementi, la shell esce dal ciclo for.

Richiamo

Stessi simboli, significati diversi

Qui vediamo > usato come prompt della shell, mentre > è usato anche per reindirizzare l’output. Allo stesso modo, $ è usato come prompt della shell, ma, come abbiamo visto prima, è anche usato per chiedere alla shell di ottenere il valore di una variabile.

Se la shell stampa > o $, allora si aspetta che venga digitato qualcosa, e il simbolo è un prompt.

Se si digita > o $, si tratta di un’istruzione che indica alla shell di reindirizzare l’output o di ottenere il valore di una variabile.

Quando si usano le variabili è anche possibile mettere i nomi tra parentesi graffe per delimitare chiaramente il nome della variabile: $filename è equivalente a ${filename}, ma è diverso da ${file}name. È possibile trovare questa notazione nei programmi di altre persone.

Abbiamo chiamato la variabile in questo ciclo filename per rendere il suo scopo più chiaro ai lettori umani. Alla shell stessa non interessa come viene chiamata la variabile; se scrivessimo questo ciclo come:

BASH

$ for x in basilisk.dat minotaur.dat unicorn.dat
> do
>     head -n 2 $x | tail -n 1
> done

o:

BASH

$ for temperature in basilisk.dat minotaur.dat unicorn.dat
> do
>     head -n 2 $temperature | tail -n 1
> done

funzionerebbe esattamente allo stesso modo. *I programmi sono utili solo se le persone possono capirli, quindi nomi senza senso (come x) o fuorvianti (come temperature) aumentano le probabilità che il programma non faccia ciò che i lettori pensano che faccia.

Negli esempi precedenti, alle variabili (thing, filename, x e temperature) si sarebbe potuto dare qualsiasi altro nome, purché significativo sia per chi scrive il codice sia per chi lo legge.

Si noti anche che i cicli possono essere usati per cose diverse dai nomi di file, come un elenco di numeri o un sottoinsieme di dati.

Sfida

Scrivete il vostro ciclo personale

Come si scrive un ciclo che fa l’eco di tutti i 10 numeri da 0 a 9?

BASH

$ for loop_variable in 0 1 2 3 4 5 6 7 8 9
> do
>     echo $loop_variable
> done

OUTPUT

0
1
2
3
4
5
6
7
8
9
Sfida

Variabili nei loop

Questo esercizio si riferisce alla cartella shell-lesson-data/exercise-data/alkanes. la cartella ls *.pdb fornisce il seguente output:

OUTPUT

cubane.pdb  ethane.pdb  methane.pdb  octane.pdb  pentane.pdb  propane.pdb

Qual è l’output del seguente codice?

BASH

$ for datafile in *.pdb
> do
>     ls *.pdb
> done

Ora, qual è l’output del seguente codice?

BASH

$ for datafile in *.pdb
> do
>     ls $datafile
> done

Perché questi due cicli danno risultati diversi?

Il primo blocco di codice dà lo stesso risultato a ogni iterazione del ciclo. Bash espande il carattere jolly *.pdb all’interno del corpo del ciclo (e anche prima dell’inizio del ciclo) in modo che corrisponda a tutti i file che terminano con .pdb e poi li elenca usando ls. Il ciclo espanso avrebbe il seguente aspetto:

BASH

$ for datafile in cubane.pdb  ethane.pdb  methane.pdb  octane.pdb  pentane.pdb  propane.pdb
> do
>     ls cubane.pdb  ethane.pdb  methane.pdb  octane.pdb  pentane.pdb  propane.pdb
> done

OUTPUT

cubane.pdb  ethane.pdb  methane.pdb  octane.pdb  pentane.pdb  propane.pdb
cubane.pdb  ethane.pdb  methane.pdb  octane.pdb  pentane.pdb  propane.pdb
cubane.pdb  ethane.pdb  methane.pdb  octane.pdb  pentane.pdb  propane.pdb
cubane.pdb  ethane.pdb  methane.pdb  octane.pdb  pentane.pdb  propane.pdb
cubane.pdb  ethane.pdb  methane.pdb  octane.pdb  pentane.pdb  propane.pdb
cubane.pdb  ethane.pdb  methane.pdb  octane.pdb  pentane.pdb  propane.pdb

Il secondo blocco di codice elenca un file diverso a ogni iterazione del ciclo. Il valore della variabile datafile viene valutato con $datafile e poi elencato con ls.

OUTPUT

cubane.pdb
ethane.pdb
methane.pdb
octane.pdb
pentane.pdb
propane.pdb
Sfida

Limitare gli insiemi di file

Quale sarebbe l’output dell’esecuzione del seguente ciclo nella directory shell-lesson-data/exercise-data/alkanes?

BASH

$ for filename in c*
> do
>     ls $filename
> done
  1. Non sono elencati file.
  2. Tutti i file sono elencati.
  3. Sono elencati solo cubane.pdb, octane.pdb e pentane.pdb.
  4. Viene elencato solo cubane.pdb.

4 è la risposta corretta. * corrisponde a zero o più caratteri, quindi qualsiasi nome di file che inizia con la lettera c, seguita da zero o più altri caratteri, verrà trovato.

Sfida

Limitare gli insiemi di file (continued)

In che modo l’output sarebbe diverso se si usasse invece questo comando?

BASH

$ for filename in *c*
> do
>     ls $filename
> done
  1. verrebbero elencati gli stessi file.
  2. Questa volta vengono elencati tutti i file.
  3. Questa volta non sono elencati file.
  4. I file cubane.pdb e octane.pdb saranno elencati.
  5. Verrà elencato solo il file octane.pdb.

4 è la risposta corretta. * corrisponde a zero o più caratteri, quindi un nome di file con zero o più caratteri prima di una lettera c e zero o più caratteri dopo la lettera c sarà corrisposto.

Sfida

Salvataggio su un file in un ciclo - Prima parte

Nella cartella shell-lesson-data/exercise-data/alkanes, qual è l’effetto di questo ciclo?

BASH

for alkanes in *.pdb
do
    echo $alkanes
    cat $alkanes > alkanes.pdb
done
  1. Stampa cubane.pdb, ethane.pdb, methane.pdb, octane.pdb, pentane.pdb e propane.pdb, e il testo di propane.pdb sarà salvato in un file chiamato alkanes.pdb.
  2. stampa cubane.pdb, ethane.pdb e methane.pdb, e il testo di tutti e tre i file viene concatenato e salvato in un file chiamato alkanes.pdb.
  3. Stampa cubane.pdb, ethane.pdb, methane.pdb, octane.pdb e pentane.pdb, e il testo di propane.pdb sarà salvato in un file chiamato alkanes.pdb.
  4. Nessuno dei precedenti.
  1. il testo di ogni file viene scritto a turno nel file alkanes.pdb. Tuttavia, il file viene sovrascritto a ogni iterazione del ciclo, quindi il contenuto finale di alkanes.pdb è il testo del file propane.pdb.
Sfida

Salvataggio su un file in un ciclo - Parte seconda

Sempre nella cartella shell-lesson-data/exercise-data/alkanes, quale sarebbe l’output del seguente ciclo?

BASH

for datafile in *.pdb
do
    cat $datafile >> all.pdb
done
  1. Tutto il testo di cubane.pdb, ethane.pdb, methane.pdb, octane.pdb e pentane.pdb verrebbe concatenato e salvato in un file chiamato all.pdb.
  2. Il testo di ethane.pdb verrà salvato in un file chiamato all.pdb.
  3. Tutto il testo di cubane.pdb, ethane.pdb, methane.pdb, octane.pdb, pentane.pdb e propane.pdb verrebbe concatenato e salvato in un file chiamato all.pdb.
  4. Tutto il testo di cubane.pdb, ethane.pdb, methane.pdb, octane.pdb, pentane.pdb e propane.pdb verrebbe stampato sullo schermo e salvato in un file chiamato all.pdb.

3 è la risposta corretta. >> aggiunge a un file, invece di sovrascriverlo con l’output rediretto da un comando. Dato che l’output del comando cat è stato reindirizzato, non viene stampato nulla sullo schermo.

Continuiamo con il nostro esempio nella cartella shell-lesson-data/exercise-data/creatures. Ecco un ciclo leggermente più complicato:

BASH

$ for filename in *.dat
> do
>     echo $filename
>     head -n 100 $filename | tail -n 20
> done

La shell inizia espandendo *.dat per creare l’elenco dei file che elaborerà. Il corpo del ciclo esegue quindi due comandi per ciascuno di questi file. Il primo comando, echo, stampa i suoi argomenti della riga di comando sullo standard output. Ad esempio:

BASH

$ echo hello there

stampa:

OUTPUT

hello there

In questo caso, poiché la shell espande $filename come nome di un file, echo $filename stampa il nome del file. Si noti che non si può scrivere come:

BASH

$ for filename in *.dat
> do
>     $filename
>     head -n 100 $filename | tail -n 20
> done

perché così la prima volta nel ciclo, quando $filename si espande in basilisk.dat, la shell cercherebbe di eseguire basilisk.dat come programma. Infine, la combinazione head e tail seleziona le righe da 81 a 100 da qualsiasi file venga elaborato (assumendo che il file abbia almeno 100 righe).

Richiamo

Spazi nei nomi

Gli spazi sono usati per separare gli elementi dell’elenco su cui si vuole eseguire il ciclo. Se uno di questi elementi contiene un carattere di spazio, dobbiamo circondarlo con le virgolette e fare la stessa cosa con la nostra variabile loop. Supponiamo che i nostri file di dati si chiamino:

red dragon.dat
purple unicorn.dat

Per eseguire il ciclo su questi file, occorre aggiungere i doppi apici in questo modo:

BASH

$ for filename in "red dragon.dat" "purple unicorn.dat"
> do
>     head -n 100 "$filename" | tail -n 20
> done

È più semplice evitare di usare spazi (o altri caratteri speciali) nei nomi dei file.

I file di cui sopra non esistono, quindi se si esegue il codice precedente, il comando head non sarà in grado di trovarli; tuttavia, il messaggio di errore restituito mostrerà il nome dei file che si aspetta:

ERRORE

head: cannot open ‘red dragon.dat' for reading: No such file or directory
head: cannot open ‘purple unicorn.dat' for reading: No such file or directory

Provate a rimuovere le virgolette intorno a $filename nel ciclo precedente per vedere l’effetto delle virgolette sugli spazi. Si noti che si ottiene un risultato dal comando loop per unicorn.dat quando si esegue questo codice nella directory creatures:

OUTPUT

head: cannot open ‘red' for reading: No such file or directory
head: cannot open ‘dragon.dat' for reading: No such file or directory
head: cannot open ‘purple' for reading: No such file or directory
CGGTACCGAA
AAGGGTCGCG
CAAGTGTTCC
...

Vogliamo modificare ciascuno dei file in shell-lesson-data/exercise-data/creatures, ma anche salvare una versione dei file originali. Vogliamo copiare i file originali in nuovi file chiamati original-basilisk.dat e original-unicorn.dat, per esempio. Non possiamo usare:

BASH

$ cp *.dat original-*.dat

perché si espanderebbe in:

BASH

$ cp basilisk.dat minotaur.dat unicorn.dat original-*.dat

Questo non avrebbe eseguito il backup dei nostri file, invece si ottiene un errore:

ERRORE

cp: target `original-*.dat' is not a directory

Questo problema si presenta quando cp riceve più di due input. Quando ciò accade, si aspetta che l’ultimo ingresso sia una cartella in cui copiare tutti i file che gli sono stati passati. Poiché non esiste una cartella denominata original-*.dat nella cartella creatures, si ottiene un errore.

Invece, possiamo usare un ciclo:

BASH

$ for filename in *.dat
> do
>     cp $filename original-$filename
> done

Questo ciclo esegue il comando cp una volta per ogni nome di file. La prima volta, quando $filename si espande a basilisk.dat, la shell esegue:

BASH

cp basilisk.dat original-basilisk.dat

La seconda volta, il comando è:

BASH

cp minotaur.dat original-minotaur.dat

La terza e ultima volta, il comando è:

BASH

cp unicorn.dat original-unicorn.dat

Poiché il comando cp normalmente non produce alcun output, è difficile verificare che il ciclo funzioni correttamente. Tuttavia, abbiamo imparato in precedenza a stampare stringhe usando echo e possiamo modificare il ciclo per usare echo per stampare i nostri comandi senza eseguirli. Possiamo quindi verificare quali comandi verrebbero eseguiti nel ciclo non modificato.

Il diagramma seguente mostra cosa succede quando viene eseguito il ciclo modificato e dimostra come l’uso giudizioso di echo sia una buona tecnica di debug.

Il ciclo for "for filename in .dat; do echo cp $filename original-$filename;done" assegnerà successivamente i nomi di tutti i file ".dat" nella cartella corrente alla variabile "$filename" e quindi eseguirà il comando. Con i file "basilisk.dat", "minotaur.dat" e "unicorn.dat" nella cartella corrente, il ciclo richiamerà successivamente il comando echo tre volte e stamperà tre righe: "cp basislisk.dat original-basilisk.dat", poi "cp minotaur.datoriginal-minotaur.dat" e infine "cp unicorn.datoriginal-unicorn.dat"

Pipeline di Nelle: Elaborazione dei file


Nelle è ora pronta a elaborare i suoi file di dati usando goostats.sh — uno script di shell scritto dal suo supervisore. Questo calcola alcune statistiche da un file campione di proteine e prende due argomenti:

  1. un file di ingresso (contenente i dati grezzi)
  2. un file di uscita (per memorizzare le statistiche calcolate)

Poiché sta ancora imparando a usare la shell, decide di costruire i comandi necessari per gradi. Il primo passo consiste nell’assicurarsi di poter selezionare i giusti file di input — ricordate, questi sono quelli i cui nomi terminano in ‘A’ o ‘B’, piuttosto che in ‘Z’. Spostandosi nella cartella north-pacific-gyre, Nelle digita:

BASH

$ cd
$ cd Desktop/shell-lesson-data/north-pacific-gyre
$ for datafile in NENE*A.txt NENE*B.txt
> do
>     echo $datafile
> done

OUTPUT

NENE01729A.txt
NENE01736A.txt
NENE01751A.txt

...
NENE02040B.txt
NENE02043B.txt

Il passo successivo è decidere come chiamare i file che il programma di analisi goostats.sh creerà. Prefissare il nome di ogni file di input con “stats” sembra semplice, quindi modifica il suo ciclo per farlo:

BASH

$ for datafile in NENE*A.txt NENE*B.txt
> do
>     echo $datafile stats-$datafile
> done

OUTPUT

NENE01729A.txt stats-NENE01729A.txt
NENE01736A.txt stats-NENE01729A.txt
NENE01751A.txt stats-NENE01729A.txt
...
NENE02040B.txt stats-NENE02040B.txt
NENE02043B.txt stats-NENE02043B.txt

Non ha ancora eseguito goostats.sh, ma ora è sicura di poter selezionare i file giusti e generare i giusti nomi di file di output.

Digitare più volte i comandi sta diventando noioso e Nelle è preoccupata di commettere errori, quindi invece di rientrare nel ciclo, preme . In risposta, la shell ripropone l’intero ciclo su una riga (usando i punti e virgola per separare i pezzi):

BASH

$ for datafile in NENE*A.txt NENE*B.txt; do echo $datafile stats-$datafile; done

Utilizzando il comando , Nelle passa al comando echo e lo cambia in bash goostats.sh:

BASH

$ for datafile in NENE*A.txt NENE*B.txt; do bash goostats.sh $datafile stats-$datafile; done

quando si preme Invio, la shell esegue il comando modificato. Tuttavia, non sembra accadere nulla: non c’è output. Dopo un attimo, Nelle si rende conto che, poiché il suo script non stampa più nulla sullo schermo, non ha idea se sia in esecuzione, e tanto meno a quale velocità. Uccide il comando in esecuzione digitando Ctrl+C, usa per ripetere il comando e lo modifica in modo da leggere:

BASH

$ for datafile in NENE*A.txt NENE*B.txt; do echo $datafile;
bash goostats.sh $datafile stats-$datafile; done
Richiamo

Inizio e fine

Possiamo spostarci all’inizio di una riga nella shell digitando Ctrl+A e alla fine usando Ctrl+E.

Quando il programma viene eseguito ora, produce una riga di output ogni cinque secondi circa:

OUTPUT

NENE01729A.txt
NENE01736A.txt
NENE01751A.txt
...

1518 per 5 secondi, diviso per 60, indica che il suo script impiegherà circa due ore per essere eseguito. Come controllo finale, apre un’altra finestra di terminale, entra in north-pacific-gyre e usa cat stats-NENE01729B.txt per esaminare uno dei file di output. Sembra tutto a posto, quindi decide di prendere un caffè e di mettersi in pari con la lettura.

Richiamo

Chi conosce la storia può scegliere di ripeterla

Un altro modo per ripetere il lavoro precedente è quello di usare il comando history per ottenere un elenco delle ultime centinaia di comandi eseguiti, e poi usare !123 (dove ‘123’ è sostituito dal numero del comando) per ripetere uno di quei comandi. Ad esempio, se Nelle digita questo:

BASH

$ history | tail -n 5

OUTPUT

456  for datafile in NENE*A.txt NENE*B.txt; do   echo $datafile stats-$datafile; done
457  for datafile in NENE*A.txt NENE*B.txt; do echo $datafile stats-$datafile; done
458  for datafile in NENE*A.txt NENE*B.txt; do bash goostats.sh $datafile stats-$datafile; done
459  for datafile in NENE*A.txt NENE*B.txt; do echo $datafile; bash goostats.sh $datafile
stats-$datafile; done
460  history | tail -n 5

quindi può eseguire nuovamente goostats.sh sui file semplicemente digitando !459.

Richiamo

Altri comandi della cronologia

Esistono numerosi altri comandi di scelta rapida per accedere alla cronologia.

  • Ctrl+R entra in modalità di ricerca nella cronologia ‘reverse-i-search’ e trova il comando più recente nella cronologia che corrisponde al testo inserito successivamente. Premere Ctrl+R una o più volte per cercare le corrispondenze precedenti. È quindi possibile utilizzare i tasti freccia sinistra e destra per scegliere la riga e modificarla, quindi premere Return per eseguire il comando.
  • !! recupera il comando immediatamente precedente (si può trovare o meno più conveniente che usare )
  • !$ recupera l’ultima parola dell’ultimo comando. Questo è utile più spesso di quanto ci si possa aspettare: dopo bash goostats.sh NENE01729B.txt stats-NENE01729B.txt, si può digitare less !$ per guardare il file stats-NENE01729B.txt, il che è più veloce che fare e modificare la riga di comando.
Sfida

Esecuzione a secco

Un ciclo è un modo per fare molte cose contemporaneamente — o per fare molti errori contemporaneamente se fa la cosa sbagliata. Un modo per verificare cosa farebbe un ciclo è quello di echo i comandi che eseguirebbe invece di eseguirli effettivamente.

Supponiamo di voler vedere in anteprima i comandi che il seguente ciclo eseguirà senza eseguirli effettivamente:

BASH

$ for datafile in *.pdb
> do
>     cat $datafile >> all.pdb
> done

Qual è la differenza tra i due cicli sottostanti e quale vogliamo eseguire?

BASH

# Version 1
$ for datafile in *.pdb
> do
>     echo cat $datafile >> all.pdb
> done

BASH

# Version 2
$ for datafile in *.pdb
> do
>     echo "cat $datafile >> all.pdb"
> done

La seconda versione è quella che vogliamo eseguire. Questa stampa sullo schermo tutto ciò che è racchiuso tra le virgolette, espandendo il nome della variabile del ciclo perché lo abbiamo preceduto da un segno di dollaro. Inoltre, non modifica e non crea il file all.pdb, poiché >> viene trattato letteralmente come parte di una stringa e non come un’istruzione di reindirizzamento.

La prima versione aggiunge l’output del comando echo cat $datafile al file all.pdb. Questo file conterrà solo l’elenco: cat cubane.pdb, cat ethane.pdb, cat methane.pdb ecc.

provate voi stessi entrambe le versioni per vedere l’output! Assicurarsi di aprire il file all.pdb per visualizzarne il contenuto.

Sfida

Cicli annidati

Supponiamo di voler impostare una struttura di directory per organizzare alcuni esperimenti di misurazione delle costanti di velocità di reazione con diversi composti e diverse temperature. Quale sarebbe il risultato del seguente codice:

BASH

$ for species in cubane ethane methane
> do
>     for temperature in 25 30 37 40
>     do
>         mkdir $species-$temperature
>     done
> done

Abbiamo un ciclo annidato, cioè contenuto all’interno di un altro ciclo, quindi per ogni specie nel ciclo esterno, il ciclo interno (il ciclo annidato) itera sull’elenco delle temperature e crea una nuova cartella per ogni combinazione.

provate voi stessi a eseguire il codice per vedere quali cartelle vengono create!

Punti Chiave
  • Un ciclo for ripete i comandi una volta per ogni cosa in un elenco.
  • Ogni ciclo for ha bisogno di una variabile per riferirsi alla cosa su cui sta operando.
  • Usare $name per espandere una variabile (cioè per ottenere il suo valore). si può usare anche ${name}.
  • Non usare spazi, virgolette o caratteri jolly come ‘*’ o ‘?’ nei nomi dei file, perché complicano l’espansione delle variabili.
  • Dare ai file nomi coerenti e facili da abbinare con i caratteri jolly, per facilitarne la selezione per il loop.
  • Usare il tasto freccia su per scorrere i comandi precedenti e modificarli e ripeterli.
  • Usare Ctrl+R per cercare tra i comandi precedentemente inseriti.
  • Usare history per visualizzare i comandi recenti e ![number] per ripetere un comando per numero.

Content from Script di shell


Ultimo aggiornamento il 2025-11-07 | Modifica questa pagina

Tempo stimato: 45 minuti

Panoramica

Domande

  • Come posso salvare e riutilizzare i comandi?

Obiettivi

  • Scrivere uno script di shell che esegua un comando o una serie di comandi per un insieme fisso di file.
  • Eseguire uno script di shell dalla riga di comando.
  • Scrive uno script di shell che opera su un insieme di file definiti dall’utente sulla riga di comando.
  • Creare pipeline che includano script di shell scritti da voi e da altri.

Siamo finalmente pronti a vedere cosa rende la shell un ambiente di programmazione così potente. Prenderemo i comandi che ripetiamo frequentemente e li salveremo in file, in modo da poter rieseguire tutte le operazioni in un secondo momento digitando un solo comando. Per ragioni storiche, un gruppo di comandi salvati in un file viene solitamente chiamato scritto di shell, ma non fatevi ingannare: si tratta in realtà di piccoli programmi.

La scrittura di script di shell non solo renderà il lavoro più veloce, ma eviterà anche di dover ridigitare gli stessi comandi più volte. Inoltre, il lavoro sarà più accurato (meno possibilità di errori di battitura) e più riproducibile. Se si torna al proprio lavoro in un secondo momento (o se qualcun altro trova il proprio lavoro e vuole basarsi su di esso), sarà possibile riprodurre gli stessi risultati semplicemente eseguendo il proprio script, invece di dover ricordare o digitare nuovamente un lungo elenco di comandi.

Iniziamo tornando a alkanes/ e creando un nuovo file, middle.sh, che diventerà il nostro script di shell:

BASH

$ cd alkanes
$ nano middle.sh

Il comando nano middle.sh apre il file middle.sh all’interno dell’editor di testo ‘nano’ (che viene eseguito all’interno della shell). Se il file non esiste, verrà creato. È possibile utilizzare l’editor di testo per modificare direttamente il file inserendo la seguente riga:

head -n 15 octane.pdb | tail -n 5

Questa è una variante della pipe costruita in precedenza, che seleziona le righe 11-15 del file octane.pdb. Ricordate che non lo stiamo ancora eseguendo come comando; stiamo solo incorporando i comandi in un file.

Poi si salva il file (Ctrl-O in nano) e si esce dall’editor di testo (Ctrl-X in nano). Verificare che la cartella alkanes contenga ora un file chiamato middle.sh.

Una volta salvato il file, possiamo chiedere alla shell di eseguire i comandi in esso contenuti. La nostra shell si chiama bash, quindi eseguiamo il seguente comando:

BASH

$ bash middle.sh

OUTPUT

ATOM      9  H           1      -4.502   0.681   0.785  1.00  0.00
ATOM     10  H           1      -5.254  -0.243  -0.537  1.00  0.00
ATOM     11  H           1      -4.357   1.252  -0.895  1.00  0.00
ATOM     12  H           1      -3.009  -0.741  -1.467  1.00  0.00
ATOM     13  H           1      -3.172  -1.337   0.206  1.00  0.00

Sicuramente l’output del nostro script è esattamente quello che otterremmo se eseguissimo direttamente la pipeline.

Richiamo

Testo vs. Qualsiasi cosa

Di solito chiamiamo “editor di testo” programmi come Microsoft Word o LibreOffice Writer, ma dobbiamo essere un po’ più attenti quando si tratta di programmazione. Per impostazione predefinita, Microsoft Word utilizza i file .docx per memorizzare non solo il testo, ma anche le informazioni di formattazione relative a caratteri, intestazioni e così via. Queste informazioni extra non sono memorizzate come caratteri e non hanno alcun significato per strumenti come head, che si aspetta che i file di input contengano solo le lettere, le cifre e la punteggiatura di una tastiera standard. Quando si modificano i programmi, quindi, è necessario utilizzare un editor di testo normale o fare attenzione a salvare i file come testo normale.

E se volessimo selezionare delle righe da un file arbitrario? Potremmo modificare middle.sh ogni volta per cambiare il nome del file, ma questo probabilmente richiederebbe più tempo che digitare nuovamente il comando nella shell ed eseguirlo con un nuovo nome di file. Modifichiamo invece middle.sh e rendiamolo più versatile:

BASH

$ nano middle.sh

Ora, all’interno di “nano”, sostituire il testo octane.pdb con la variabile speciale chiamata $1:

head -n 15 "$1" | tail -n 5

All’interno di uno script di shell, $1 significa “il primo nome di file (o altro argomento) sulla riga di comando”. Ora possiamo eseguire il nostro script in questo modo:

BASH

$ bash middle.sh octane.pdb

OUTPUT

ATOM      9  H           1      -4.502   0.681   0.785  1.00  0.00
ATOM     10  H           1      -5.254  -0.243  -0.537  1.00  0.00
ATOM     11  H           1      -4.357   1.252  -0.895  1.00  0.00
ATOM     12  H           1      -3.009  -0.741  -1.467  1.00  0.00
ATOM     13  H           1      -3.172  -1.337   0.206  1.00  0.00

o su un file diverso come questo:

BASH

$ bash middle.sh pentane.pdb

OUTPUT

ATOM      9  H           1       1.324   0.350  -1.332  1.00  0.00
ATOM     10  H           1       1.271   1.378   0.122  1.00  0.00
ATOM     11  H           1      -0.074  -0.384   1.288  1.00  0.00
ATOM     12  H           1      -0.048  -1.362  -0.205  1.00  0.00
ATOM     13  H           1      -1.183   0.500  -1.412  1.00  0.00
Richiamo

Doppie virgolette intorno agli argomenti

Per la stessa ragione per cui mettiamo la variabile loop tra virgolette doppie, nel caso in cui il nome del file contenga spazi, circondiamo $1 con virgolette doppie.

Attualmente, dobbiamo modificare middle.sh ogni volta che vogliamo regolare l’intervallo di righe che viene restituito. Risolviamo questo problema configurando il nostro script in modo che utilizzi tre argomenti della riga di comando. Dopo il primo argomento da riga di comando ($1), ogni ulteriore argomento fornito sarà accessibile tramite le variabili speciali $1, $2, $3, che si riferiscono rispettivamente al primo, secondo e terzo argomento da riga di comando.

Sapendo questo, possiamo usare argomenti aggiuntivi per definire l’intervallo di righe da passare rispettivamente a head e tail:

BASH

$ nano middle.sh
head -n "$2" "$1" | tail -n "$3"

Ora possiamo eseguire:

BASH

$ bash middle.sh pentane.pdb 15 5

OUTPUT

ATOM      9  H           1       1.324   0.350  -1.332  1.00  0.00
ATOM     10  H           1       1.271   1.378   0.122  1.00  0.00
ATOM     11  H           1      -0.074  -0.384   1.288  1.00  0.00
ATOM     12  H           1      -0.048  -1.362  -0.205  1.00  0.00
ATOM     13  H           1      -1.183   0.500  -1.412  1.00  0.00

Cambiando gli argomenti del nostro comando, possiamo cambiare il comportamento del nostro script:

BASH

$ bash middle.sh pentane.pdb 20 5

OUTPUT

ATOM     14  H           1      -1.259   1.420   0.112  1.00  0.00
ATOM     15  H           1      -2.608  -0.407   1.130  1.00  0.00
ATOM     16  H           1      -2.540  -1.303  -0.404  1.00  0.00
ATOM     17  H           1      -3.393   0.254  -0.321  1.00  0.00
TER      18              1

Questo funziona, ma la prossima persona che leggerà middle.sh potrebbe metterci un attimo a capire cosa fa. Possiamo migliorare il nostro script aggiungendo alcuni commenti all’inizio:

BASH

$ nano middle.sh
# Select lines from the middle of a file.
# Usage: bash middle.sh filename end_line num_lines
head -n "$2" "$1" | tail -n "$3"

Un commento inizia con un carattere # e arriva fino alla fine della riga. Il computer ignora i commenti, ma sono preziosi per aiutare le persone (compreso il vostro futuro io) a capire e usare gli script. L’unica avvertenza è che ogni volta che si modifica lo script, bisogna controllare che il commento sia ancora corretto. Una spiegazione che manda il lettore nella direzione sbagliata è peggiore di nessuna.

Cosa succede se si vogliono elaborare molti file in una singola pipeline? Per esempio, se vogliamo ordinare i nostri file .pdb per lunghezza, digitiamo:

BASH

$ wc -l *.pdb | sort -n

perché wc -l elenca il numero di righe nei file (ricordate che wc sta per “conteggio delle parole”, aggiungendo l’opzione -l significa invece “conteggio delle righe”) e sort -n ordina le cose numericamente. Si potrebbe inserire in un file, ma in questo modo verrebbe ordinato solo un elenco di file .pdb nella directory corrente. Se vogliamo essere in grado di ottenere un elenco ordinato di altri tipi di file, abbiamo bisogno di un modo per inserire tutti questi nomi nello script. Non possiamo usare $1, $2 e così via perché non sappiamo quanti file ci sono. Si usa invece la variabile speciale $@, che significa “Tutti gli argomenti della riga di comando dello script di shell”. Si dovrebbe anche mettere $@ tra doppi apici per gestire il caso di argomenti contenenti spazi ("$@" è una sintassi speciale ed è equivalente a "$1" "$2" …).

Ecco un esempio:

BASH

$ nano sorted.sh
# Sort files by their length.
# Usage: bash sorted.sh one_or_more_filenames
wc -l "$@" | sort -n

BASH

$ bash sorted.sh *.pdb ../creatures/*.dat

OUTPUT

9 methane.pdb
12 ethane.pdb
15 propane.pdb
20 cubane.pdb
21 pentane.pdb
30 octane.pdb
163 ../creatures/basilisk.dat
163 ../creatures/minotaur.dat
163 ../creatures/unicorn.dat
596 total
Sfida

Elenco delle specie uniche

Leah ha diverse centinaia di file di dati, ognuno dei quali è formattato in questo modo:

2013-11-05,deer,5
2013-11-05,rabbit,22
2013-11-05,raccoon,7
2013-11-06,rabbit,19
2013-11-06,deer,2
2013-11-06,fox,1
2013-11-07,rabbit,18
2013-11-07,bear,1

Un esempio di questo tipo di file è riportato in shell-lesson-data/exercise-data/animal-counts/animals.csv.

Possiamo usare il comando cut -d , -f 2 animals.csv | sort | uniq per produrre le specie uniche in animals.csv. Per evitare di dover digitare ogni volta questa serie di comandi, uno scienziato può scegliere di scrivere uno script di shell.

Scrivere uno script di shell chiamato species.sh che accetta un numero qualsiasi di nomi di file come argomenti della riga di comando e utilizza una variante del comando precedente per stampare un elenco delle specie uniche che compaiono in ciascuno di questi file separatamente.

BASH

# Script per trovare le specie uniche nei file CSV, dove la specie è il secondo campo di dati.
# Questo script accetta un numero qualsiasi di nomi di file come argomenti da riga di comando.

# Loop over all files
for file in $@
do
    echo "Unique species in $file:"
    # Estrae i nomi delle specie
    cut -d , -f 2 $file | sort | uniq
done

Supponiamo di aver appena eseguito una serie di comandi che hanno fatto qualcosa di utile, ad esempio la creazione di un grafico che vorremmo utilizzare in un articolo. Se vogliamo essere in grado di ricreare il grafico in un secondo momento, vogliamo salvare i comandi in un file. Invece di digitarli di nuovo (e potenzialmente sbagliarli), possiamo fare così:

BASH

$ history | tail -n 5 > redo-figure-3.sh

Il file redo-figure-3.sh ora contiene:

297 bash goostats.sh NENE01729B.txt stats-NENE01729B.txt
298 bash goodiff.sh stats-NENE01729B.txt /data/validated/01729.txt > 01729-differences.txt
299 cut -d ',' -f 2-3 01729-differences.txt > 01729-time-series.txt
300 ygraph --format scatter --color bw --borders none 01729-time-series.txt figure-3.png
301 history | tail -n 5 > redo-figure-3.sh

Dopo un attimo di lavoro in un editor per rimuovere i numeri di serie sui comandi e per rimuovere la riga finale in cui abbiamo chiamato il comando history, abbiamo una registrazione completamente accurata di come abbiamo creato la figura.

Sfida

Perché registrare i comandi nella cronologia prima di eseguirli?

Se si esegue il comando:

BASH

$ history | tail -n 5 > recent.sh

l’ultimo comando nel file è il comando history stesso, cioè la shell ha aggiunto history al registro dei comandi prima di eseguirlo. In effetti, la shell aggiunge sempre comandi al log prima di eseguirli. Perché pensate che lo faccia?

Se un comando provoca un arresto anomalo o un blocco, potrebbe essere utile sapere qual è il comando in questione, per poter indagare sul problema. Se il comando venisse registrato solo dopo la sua esecuzione, non avremmo una registrazione dell’ultimo comando eseguito in caso di crash.

In pratica, la maggior parte delle persone sviluppa script eseguendo i comandi al prompt della shell un paio di volte per assicurarsi che stiano facendo la cosa giusta, poi li salva in un file per riutilizzarli. Questo stile di lavoro consente di riciclare ciò che si scopre sui propri dati e sul proprio flusso di lavoro con una sola chiamata a history e un po’ di modifiche per ripulire l’output e salvarlo come script di shell.

Pipeline di Nelle: Creazione di uno script


Il supervisore di Nelle ha insistito sul fatto che tutte le sue analisi devono essere riproducibili. Il modo più semplice per catturare tutti i passaggi è uno script.

Per prima cosa torniamo alla directory del progetto di Nelle:

BASH

$ cd ../../north-pacific-gyre/

Crea un file usando nano

BASH

$ nano do-stats.sh

… che contiene quanto segue:

BASH

# Calcola le statistiche per i file di dati.
for datafile in "$@"
do
    echo $datafile
    bash goostats.sh $datafile stats-$datafile
done

Salva il tutto in un file chiamato do-stats.sh, in modo da poter rifare la prima fase della sua analisi digitando:

BASH

$ bash do-stats.sh NENE*A.txt NENE*B.txt

Può anche fare questo:

BASH

$ bash do-stats.sh NENE*A.txt NENE*B.txt | wc -l

in modo che l’output sia solo il numero di file elaborati piuttosto che i nomi dei file elaborati.

Una cosa da notare dello script di Nelle è che lascia che sia la persona che lo esegue a decidere quali file elaborare. Avrebbe potuto scriverlo come:

BASH

# Calculate stats for Site A and Site B data files.
for datafile in NENE*A.txt NENE*B.txt
do
    echo $datafile
    bash goostats.sh $datafile stats-$datafile
done

Il vantaggio è che in questo modo si selezionano sempre i file giusti, senza doversi ricordare di escludere i file “Z”. Lo svantaggio è che seleziona sempre solo quei file — non può eseguirlo su tutti i file (compresi i file ‘Z’), o sui file ‘G’ o ‘H’ che i suoi colleghi in Antartide stanno producendo, senza modificare lo script. Se volesse essere più avventurosa, potrebbe modificare il suo script per verificare la presenza di argomenti da riga di comando e usare NENE*A.txt NENE*B.txt se non ne vengono forniti. Naturalmente, questo introduce un altro compromesso tra flessibilità e complessità.

Sfida

Variabili negli script di shell

Nella cartella alkanes, immaginate di avere uno script di shell chiamato script.sh contenente i seguenti comandi:

BASH

head -n $2 $1
tail -n $3 $1

Mentre ci si trova nella cartella alkanes, si digita il seguente comando:

BASH

$ bash script.sh '*.pdb' 1 1

Quale dei seguenti risultati vi aspettereste di vedere?

  1. Tutte le righe tra la prima e l’ultima di ogni file che termina con .pdb nella cartella alkanes
  2. La prima e l’ultima riga di ogni file che termina con .pdb nella cartella alkanes
  3. La prima e l’ultima riga di ogni file nella cartella alkanes
  4. Errore a causa delle virgolette intorno a *.pdb

la risposta corretta è 2.

Le variabili speciali $1, $2 e $3 rappresentano gli argomenti della riga di comando dati allo script, in modo che i comandi eseguiti siano:

BASH

$ head -n 1 cubane.pdb ethane.pdb octane.pdb pentane.pdb propane.pdb
$ tail -n 1 cubane.pdb ethane.pdb octane.pdb pentane.pdb propane.pdb

La shell non espande '*.pdb' perché è racchiuso tra virgolette. Pertanto, il primo argomento dello script è '*.pdb' che viene espanso all’interno dello script da head e tail.

Sfida

Trova il file più lungo con una data estensione

Scrivere uno script di shell chiamato longest.sh che prenda come argomenti il nome di una cartella e l’estensione di un nome di file e stampi il nome del file con il maggior numero di righe in quella cartella con quell’estensione. Ad esempio:

BASH

$ bash longest.sh shell-lesson-data/exercise-data/alkanes pdb

stamperebbe il nome del file .pdb in shell-lesson-data/exercise-data/alkanes che ha il maggior numero di righe.

Sentitevi liberi di testare il vostro script su un’altra directory, ad es.

BASH

$ bash longest.sh shell-lesson-data/exercise-data/writing txt

BASH

# Shell script which takes two arguments:
#    1. a directory name
#    2. a file extension
# and prints the name of the file in that directory
# with the most lines which matches the file extension.

wc -l $1/*.$2 | sort -n | tail -n 2 | head -n 1

La prima parte della pipeline, wc -l $1/*.$2 | sort -n, conta le righe di ogni file e le ordina numericamente (la più grande per ultima). Quando c’è più di un file, wc produce anche una riga finale di riepilogo, che dà il numero totale di righe in tutti i file. Si usa tail -n 2 | head -n 1 per eliminare quest’ultima riga.

Con wc -l $1/*.$2 | sort -n | tail -n 1 vedremo la riga di riepilogo finale: possiamo costruire la nostra pipeline a pezzi per essere sicuri di capire l’output.

Sfida

Comprensione della lettura degli script

Per questa domanda, consideriamo ancora una volta la cartella shell-lesson-data/exercise-data/alkanes. Questa contiene una serie di file .pdb oltre ad altri file eventualmente creati. Spiegare che cosa farebbe ciascuno dei tre script seguenti se eseguito rispettivamente come bash script1.sh *.pdb, bash script2.sh *.pdb e bash script3.sh *.pdb.

BASH

# Script 1
echo *.*

BASH

# Script 2
for filename in $1 $2 $3
do
    cat $filename
done

BASH

# Script 3
echo $@.pdb

In ogni caso, la shell espande il carattere jolly in *.pdb prima di passare l’elenco risultante di nomi di file come argomenti allo script.

Lo script 1 stampa un elenco di tutti i file che contengono un punto nel loro nome. Gli argomenti passati allo script non vengono utilizzati in nessun punto dello script.

Lo script 2 stampa il contenuto dei primi 3 file con estensione .pdb. $1, $2 e $3 si riferiscono rispettivamente al primo, al secondo e al terzo argomento.

Lo script 3 stamperebbe tutti gli argomenti dello script (cioè tutti i file .pdb), seguiti da .pdb. $@ si riferisce a tutti gli argomenti dati a uno script di shell.

OUTPUT

cubane.pdb ethane.pdb methane.pdb octane.pdb pentane.pdb propane.pdb.pdb
Sfida

Script di Debug

Si supponga di aver salvato il seguente script in un file chiamato do-errors.sh nella cartella north-pacific-gyre di Nelle:

BASH

# Calculate stats for data files.
for datafile in "$@"
do
    echo $datfile
    bash goostats.sh $datafile stats-$datafile
done

Quando lo si esegue dalla cartella north-pacific-gyre:

BASH

$ bash do-errors.sh NENE*A.txt NENE*B.txt

l’output è vuoto. Per capirne il motivo, rieseguire lo script utilizzando l’opzione -x:

BASH

$ bash -x do-errors.sh NENE*A.txt NENE*B.txt

Cosa mostra l’output? Quale riga è responsabile dell’errore?

L’opzione -x fa sì che bash venga eseguito in modalità di debug. In questo modo viene stampato ogni comando man mano che viene eseguito, il che aiuta a individuare gli errori. In questo esempio, possiamo vedere che echo non sta stampando nulla. Abbiamo commesso un errore di battitura nel nome della variabile del ciclo e la variabile datfile non esiste, quindi restituisce una stringa vuota.

Punti Chiave
  • Salva i comandi in file (di solito chiamati script di shell) per riutilizzarli.
  • bash [filename] esegue i comandi salvati in un file.
  • $@ si riferisce a tutti gli argomenti della riga di comando di uno script di shell.
  • $1, $2, ecc. si riferiscono al primo argomento della riga di comando, al secondo argomento della riga di comando, ecc.
  • Mettere le variabili tra virgolette se i valori possono contenere spazi.
  • Lasciare che siano gli utenti a decidere quali file elaborare è più flessibile e più coerente con i comandi Unix integrati.

Content from Trovare le cose


Ultimo aggiornamento il 2025-11-07 | Modifica questa pagina

Tempo stimato: 45 minuti

Panoramica

Domande

  • Come posso trovare i file?
  • Come posso trovare le cose nei file?

Obiettivi

  • Usare grep per selezionare righe da file di testo che corrispondono a modelli semplici.
  • Usare find per trovare file e cartelle i cui nomi corrispondono a schemi semplici.
  • Utilizzare l’output di un comando come argomento della riga di comando di un altro comando.
  • Spiegare cosa si intende per file “di testo” e “binari” e perché molti strumenti comuni non gestiscono bene questi ultimi.

Allo stesso modo in cui molti di noi usano ‘Google’ come verbo che significa ‘trovare’, i programmatori Unix usano spesso la parola ‘grep’. ‘grep’ è una contrazione di ‘global/regular expression/print’, una sequenza comune di operazioni nei primi editor di testo Unix. È anche il nome di un programma a riga di comando molto utile.

grep trova e stampa le righe dei file che corrispondono a uno schema. Per i nostri esempi, useremo un file che contiene tre haiku tratti da un concorso 1998 della rivista Salon (merito degli autori Bill Torcaso, Howard Korder e Margaret Segall, rispettivamente. Si vedano i messaggi di errore Haiku archiviati Pagina 1 e Pagina 2). Per questa serie di esempi, lavoreremo nella sottocartella della scrittura:

BASH

$ cd
$ cd Desktop/shell-lesson-data/exercise-data/writing
$ cat haiku.txt

OUTPUT

The Tao that is seen
Is not the true Tao, until
You bring fresh toner.

With searching comes loss
and the presence of absence:
"My Thesis" not found.

Yesterday it worked
Today it is not working
Software is like that.

Troviamo le righe che contengono la parola “non”:

BASH

$ grep not haiku.txt

OUTPUT

Is not the true Tao, until
"My Thesis" not found
Today it is not working

Qui, not è lo schema che stiamo cercando. Il comando grep cerca nel file le corrispondenze con lo schema specificato. Per utilizzarlo, digitare grep, quindi lo schema che si vuole cercare e infine il nome del file (o dei file) in cui si vuole effettuare la ricerca.

L’output è costituito dalle tre righe del file che contengono le lettere “non”.

Per impostazione predefinita, grep cerca un modello in modo sensibile alle maiuscole e alle minuscole. Inoltre, il modello di ricerca selezionato non deve necessariamente formare una parola completa, come vedremo nel prossimo esempio.

Cerchiamo il modello: ‘Il’.

BASH

$ grep The haiku.txt

OUTPUT

The Tao that is seen
"My Thesis" not found.

Questa volta vengono prodotte due righe che includono le lettere “The”, una delle quali conteneva il nostro modello di ricerca all’interno di una parola più grande, “Thesis”.

Per limitare le corrispondenze alle righe contenenti la parola ‘The’ da sola, si può dare a grep l’opzione -w. Questo limiterà le corrispondenze ai confini della parola.

Più avanti in questa lezione vedremo anche come modificare il comportamento di ricerca di grep rispetto alla sensibilità alle maiuscole.

BASH

$ grep -w The haiku.txt

OUTPUT

The Tao that is seen

Si noti che un “confine di parola” include l’inizio e la fine di una riga, quindi non solo le lettere circondate da spazi. A volte non si vuole cercare una singola parola, ma una frase. È possibile farlo anche con grep, mettendo la frase tra virgolette.

BASH

$ grep -w "is not" haiku.txt

OUTPUT

Today it is not working

Abbiamo visto che non è necessario mettere le virgolette intorno alle singole parole, ma è utile usare le virgolette quando si cercano più parole. Inoltre, aiuta a distinguere più facilmente tra il termine o la frase di ricerca e il file ricercato. Negli altri esempi utilizzeremo le virgolette.

Un’altra opzione utile è -n, che numera le righe corrispondenti:

BASH

$ grep -n "it" haiku.txt

OUTPUT

5:With searching comes loss
9:Yesterday it worked
10:Today it is not working

Qui possiamo vedere che le righe 5, 9 e 10 contengono le lettere “it”.

Possiamo combinare le opzioni (cioè i flag) come facciamo con altri comandi Unix. Per esempio, cerchiamo di trovare le righe che contengono la parola “il”. Possiamo combinare l’opzione -w per trovare le righe che contengono la parola ‘the’ e -n per numerare le righe che corrispondono:

BASH

$ grep -n -w "the" haiku.txt

OUTPUT

2:Is not the true Tao, until
6:and the presence of absence:

Ora vogliamo usare l’opzione -i per rendere la nostra ricerca insensibile alle maiuscole e alle minuscole:

BASH

$ grep -n -w -i "the" haiku.txt

OUTPUT

1:The Tao that is seen
2:Is not the true Tao, until
6:and the presence of absence:

Ora vogliamo usare l’opzione -v per invertire la nostra ricerca, cioè vogliamo produrre le righe che non contengono la parola ‘the’.

BASH

$ grep -n -w -v "the" haiku.txt

OUTPUT

1:The Tao that is seen
3:You bring fresh toner.
4:
5:With searching comes loss
7:"My Thesis" not found.
8:
9:Yesterday it worked
10:Today it is not working
11:Software is like that.

Se si usa l’opzione -r (ricorsiva), grep può cercare un modello in modo ricorsivo in un insieme di file in sottodirectory.

Cerchiamo ricorsivamente Yesterday nella directory shell-lesson-data/exercise-data/writing:

BASH

$ grep -r Yesterday .

OUTPUT

./LittleWomen.txt:"Yesterday, when Aunt was asleep and I was trying to be as still as a
./LittleWomen.txt:Yesterday at dinner, when an Austrian officer stared at us and then
./LittleWomen.txt:Yesterday was a quiet day spent in teaching, sewing, and writing in my
./haiku.txt:Yesterday it worked

grep ha molte altre opzioni. Per scoprire quali sono, si può digitare:

BASH

$ grep --help

OUTPUT

Usage: grep [OPTION]... PATTERN [FILE]...
Search for PATTERN in each FILE or standard input.
PATTERN is, by default, a basic regular expression (BRE).
Example: grep -i 'hello world' menu.h main.c

Regexp selection and interpretation:
  -E, --extended-regexp     PATTERN is an extended regular expression (ERE)
  -F, --fixed-strings       PATTERN is a set of newline-separated fixed strings
  -G, --basic-regexp        PATTERN is a basic regular expression (BRE)
  -P, --perl-regexp         PATTERN is a Perl regular expression
  -e, --regexp=PATTERN      use PATTERN for matching
  -f, --file=FILE           obtain PATTERN from FILE
  -i, --ignore-case         ignore case distinctions
  -w, --word-regexp         force PATTERN to match only whole words
  -x, --line-regexp         force PATTERN to match only whole lines
  -z, --null-data           a data line ends in 0 byte, not newline

Miscellaneous:
...        ...        ...
Sfida

Uso di grep

Quale comando produrrebbe il seguente output:

OUTPUT

and the presence of absence:
  1. grep "of" haiku.txt
  2. grep -E "of" haiku.txt
  3. grep -w "of" haiku.txt
  4. grep -i "of" haiku.txt

La risposta corretta è 3, perché l’opzione -w cerca solo le corrispondenze con le parole intere. Le altre opzioni corrispondono anche a “di” quando è parte di un’altra parola.

Richiamo

Caratteri jolly

la vera potenza di grep non deriva dalle sue opzioni, ma dal fatto che i pattern possono includere caratteri jolly. (Il nome tecnico di queste espressioni è espressioni regolari, che è il significato di “re” in “grep”) Le espressioni regolari sono complesse e potenti; se volete fare ricerche complesse, consultate la lezione su il nostro sito web. Come assaggio, possiamo trovare le righe che hanno una ‘o’ in seconda posizione come questa:

BASH

$ grep -E "^.o" haiku.txt

OUTPUT

You bring fresh toner.
Today it is not working
Software is like that.

Usiamo l’opzione -E e mettiamo il pattern tra virgolette per evitare che la shell cerchi di interpretarlo. (Se il pattern contenesse un *, per esempio, la shell cercherebbe di espanderlo prima di eseguire grep) Il ^ nel pattern fissa la corrispondenza all’inizio della riga. La . corrisponde a un singolo carattere (proprio come la ? nella shell), mentre la o corrisponde a una “o” vera e propria.

Sfida

Tracciare una specie

Leah ha diverse centinaia di file di dati salvati in una directory, ognuno dei quali è formattato come segue:

2012-11-05,deer,5
2012-11-05,rabbit,22
2012-11-05,raccoon,7
2012-11-06,rabbit,19
2012-11-06,deer,2
2012-11-06,fox,4
2012-11-07,rabbit,16
2012-11-07,bear,1

Vuole scrivere uno script di shell che prenda una specie come primo argomento della riga di comando e una directory come secondo argomento. Lo script dovrebbe restituire un file chiamato <species>.txt contenente un elenco di date e il numero di specie osservate in ciascuna data. Ad esempio, utilizzando i dati mostrati sopra, rabbit.txt conterrebbe:

2012-11-05,22
2012-11-06,19
2012-11-07,16

Di seguito, ogni riga contiene un singolo comando, o pipe. Organizzare la loro sequenza in un comando per raggiungere l’obiettivo di Leah:

BASH

cut -d : -f 2
>
|
grep -w $1 -r $2
|
$1.txt
cut -d , -f 1,3

Suggerimento: usare man grep per cercare come grepare il testo in modo ricorsivo in una cartella e man cut per selezionare più di un campo in una riga.

Un esempio di tale file è fornito in shell-lesson-data/exercise-data/animal-counts/animals.csv

grep -w $1 -r $2 | cut -d : -f 2 | cut -d , -f 1,3 > $1.txt

In realtà, è possibile scambiare l’ordine dei due comandi di taglio e funziona ancora. Alla riga di comando, provate a cambiare l’ordine dei comandi di taglio e date un’occhiata all’output di ogni passaggio per capire perché è così.

Lo script precedente viene chiamato in questo modo:

BASH

$ bash count-species.sh bear .
Sfida

Piccole donne

Tu e la tua amica, dopo aver appena finito di leggere Piccole donne di Louisa May Alcott, state discutendo. Delle quattro sorelle del libro, Jo, Meg, Beth e Amy, la vostra amica pensa che Jo sia la più citata. Voi, invece, siete certi che si tratti di Amy. Fortunatamente si dispone di un file LittleWomen.txt contenente il testo completo del romanzo (shell-lesson-data/exercise-data/writing/LittleWomen.txt). Utilizzando un ciclo for, come si potrebbe tabulare il numero di volte in cui ciascuna delle quattro sorelle viene menzionata?

Suggerimento: una soluzione potrebbe utilizzare i comandi grep e wc e un |, mentre un’altra potrebbe utilizzare le opzioni grep. Spesso esiste più di un modo per risolvere un compito di programmazione, quindi una particolare soluzione viene solitamente scelta in base a una combinazione di risultati corretti, eleganza, leggibilità e velocità.

for sis in Jo Meg Beth Amy
do
    echo $sis:
    grep -ow $sis LittleWomen.txt | wc -l
done

Soluzione alternativa, leggermente inferiore:

for sis in Jo Meg Beth Amy
do
    echo $sis:
    grep -ocw $sis LittleWomen.txt
done

Questa soluzione è inferiore perché grep -c riporta solo il numero di righe corrispondenti. Il numero totale di corrispondenze riportato da questo metodo sarà inferiore se c’è più di una corrispondenza per riga.

Gli osservatori più attenti avranno notato che i nomi dei personaggi a volte appaiono in tutte le maiuscole nei titoli dei capitoli (per esempio, “MEG VA ALLA FIERA DELLA VANITÀ”). Se si volesse contare anche questi, si potrebbe aggiungere l’opzione -i per l’insensibilità alle maiuscole e minuscole (anche se in questo caso non influisce sulla risposta a quale sorella è menzionata più frequentemente).

Mentre grep trova le righe nei file, il comando find trova i file stessi. Anche in questo caso, ha molte opzioni; per mostrare come funzionano le più semplici, useremo l’albero shell-lesson-data/exercise-data mostrato di seguito.

OUTPUT

.
├── animal-counts/
│   └── animals.csv
├── creatures/
│   ├── basilisk.dat
│   ├── minotaur.dat
│   └── unicorn.dat
├── numbers.txt
├── alkanes/
│   ├── cubane.pdb
│   ├── ethane.pdb
│   ├── methane.pdb
│   ├── octane.pdb
│   ├── pentane.pdb
│   └── propane.pdb
└── writing/
    ├── haiku.txt
    └── LittleWomen.txt

La cartella exercise-data contiene un file, numbers.txt e quattro cartelle: animal-counts, creatures, alkanes e writing contenenti vari file.

Per il nostro primo comando, eseguiamo find . (ricordate di eseguire questo comando dalla cartella shell-lesson-data/exercise-data).

BASH

$ find .

OUTPUT

.
./writing
./writing/LittleWomen.txt
./writing/haiku.txt
./creatures
./creatures/basilisk.dat
./creatures/unicorn.dat
./creatures/minotaur.dat
./animal-counts
./animal-counts/animals.csv
./numbers.txt
./alkanes
./alkanes/ethane.pdb
./alkanes/propane.pdb
./alkanes/octane.pdb
./alkanes/pentane.pdb
./alkanes/methane.pdb
./alkanes/cubane.pdb

Come sempre, . da solo significa la cartella di lavoro corrente, che è il punto da cui vogliamo iniziare la nostra ricerca. l’output di find è il nome di ogni file e cartella sotto la cartella di lavoro corrente. All’inizio può sembrare inutile, ma find ha molte opzioni per filtrare l’output e in questa lezione ne scopriremo alcune.

La prima opzione del nostro elenco è -type d che significa “cose che sono directory”. Certamente, l’output di find è il nome delle cinque cartelle (inclusa .):

BASH

$ find . -type d

OUTPUT

.
./writing
./creatures
./animal-counts
./alkanes

Si noti che gli oggetti trovati da find non sono elencati in un ordine particolare. Se si cambia -type d con -type f, si ottiene invece un elenco di tutti i file:

BASH

$ find . -type f

OUTPUT

./writing/LittleWomen.txt
./writing/haiku.txt
./creatures/basilisk.dat
./creatures/unicorn.dat
./creatures/minotaur.dat
./animal-counts/animals.csv
./numbers.txt
./alkanes/ethane.pdb
./alkanes/propane.pdb
./alkanes/octane.pdb
./alkanes/pentane.pdb
./alkanes/methane.pdb
./alkanes/cubane.pdb

Ora proviamo a fare una corrispondenza per nome:

BASH

$ find . -name *.txt

OUTPUT

./numbers.txt

Ci si aspettava che trovasse tutti i file di testo, ma stampa solo ./numbers.txt. Il problema è che la shell espande i caratteri jolly come * prima dell’esecuzione dei comandi. Poiché *.txt nella cartella corrente si espande a ./numbers.txt, il comando effettivamente eseguito è stato:

BASH

$ find . -name numbers.txt

find ha fatto quello che abbiamo chiesto; abbiamo solo chiesto la cosa sbagliata.

Per ottenere ciò che vogliamo, facciamo come con grep: mettiamo *.txt tra virgolette per evitare che la shell espanda il carattere jolly *. In questo modo, find ottiene effettivamente il pattern *.txt, non il nome del file espanso numbers.txt:

BASH

$ find . -name "*.txt"

OUTPUT

./writing/LittleWomen.txt
./writing/haiku.txt
./numbers.txt
Richiamo

Elenco vs. ricerca

ls e find possono fare cose simili con le giuste opzioni, ma in circostanze normali, ls elenca tutto ciò che può, mentre find cerca cose con certe proprietà e le mostra.

Come abbiamo detto prima, la potenza della riga di comando sta nel combinare gli strumenti. Abbiamo visto come farlo con le pipe; vediamo un’altra tecnica. Come abbiamo appena visto, find . -name "*.txt" ci fornisce un elenco di tutti i file di testo presenti nella cartella corrente o al di sotto di essa. Come possiamo combinarlo con wc -l per contare le righe in tutti questi file?

Il modo più semplice è quello di inserire il comando find all’interno di $():

BASH

$ wc -l $(find . -name "*.txt")

OUTPUT

  21022 ./writing/LittleWomen.txt
     11 ./writing/haiku.txt
      5 ./numbers.txt
  21038 total

Quando la shell esegue questo comando, la prima cosa che fa è eseguire qualsiasi cosa si trovi all’interno della $(). Quindi sostituisce l’espressione $() con l’output di quel comando. Poiché l’output di find è costituito dai tre nomi di file ./writing/LittleWomen.txt, ./writing/haiku.txt e ./numbers.txt, la shell costruisce il comando:

BASH

$ wc -l ./writing/LittleWomen.txt ./writing/haiku.txt ./numbers.txt

che è quello che volevamo. Questa espansione è esattamente ciò che fa la shell quando espande i caratteri jolly come * e ?, ma ci permette di usare qualsiasi comando come nostro ‘carattere jolly’.

È molto comune usare find e grep insieme. Il primo trova i file che corrispondono a uno schema; il secondo cerca le righe all’interno di quei file che corrispondono a un altro schema. Qui, per esempio, possiamo trovare i file txt che contengono la parola “searching” cercando la stringa ‘searching’ in tutti i file .txt nella directory corrente:

BASH

$ grep "searching" $(find . -name "*.txt")

OUTPUT

./writing/LittleWomen.txt:sitting on the top step, affected to be searching for her book, but was
./writing/haiku.txt:With searching comes loss
Sfida

Corrispondenza e sottrazione

L’opzione -v di grep inverte la corrispondenza dei pattern, in modo che vengano stampate solo le righe che non corrispondono al pattern. In base a ciò, quale dei seguenti comandi troverà tutti i file .dat in creatures tranne che in unicorn.dat? Dopo aver pensato alla risposta, si possono testare i comandi nella directory shell-lesson-data/exercise-data.

  1. find creatures -name "*.dat" | grep -v unicorn
  2. find creatures -name *.dat | grep -v unicorn
  3. grep -v "unicorn" $(find creatures -name "*.dat")
  4. Nessuno dei precedenti.

L’opzione 1 è corretta. Mettere l’espressione di corrispondenza tra virgolette impedisce alla shell di espanderla, quindi viene passata al comando find.

L’opzione 2 funziona anche in questo caso, perché la shell tenta di espandere *.dat ma non ci sono file *.dat nella cartella corrente, quindi l’espressione jolly viene passata a find. L’abbiamo incontrato per la prima volta in [episodio 3] (03-create.md).

L’opzione 3 non è corretta perché cerca nel contenuto dei file le righe che non corrispondono a “unicorno”, anziché cercare nei nomi dei file.

Richiamo

File binari

Ci siamo concentrati esclusivamente sulla ricerca di modelli nei file di testo. Cosa succede se i dati sono archiviati come immagini, in database o in altri formati?

Una manciata di strumenti estende grep per gestire alcuni formati diversi dal testo. Ma un approccio più generalizzabile è quello di convertire i dati in testo o di estrarre gli elementi simili al testo dai dati. Da un lato, questo rende le cose semplici facili da fare. D’altra parte, le cose complesse sono di solito impossibili. Per esempio, è abbastanza facile scrivere un programma che estragga le dimensioni X e Y da file di immagini per giocare con grep, ma come si potrebbe scrivere qualcosa per trovare valori in un foglio di calcolo le cui celle contengono formule?

Un’ultima opzione è riconoscere che la shell e l’elaborazione del testo hanno i loro limiti e utilizzare un altro linguaggio di programmazione. Quando arriva il momento di farlo, non siate troppo duri con la shell. Molti linguaggi di programmazione moderni hanno preso in prestito molte idee da essa, e l’imitazione è anche la forma più sincera di lode.

La shell Unix è più vecchia della maggior parte delle persone che la usano. È sopravvissuta così a lungo perché è uno degli ambienti di programmazione più produttivi mai creati, forse addirittura il più produttivo. La sua sintassi può essere criptica, ma chi la padroneggia può sperimentare diversi comandi in modo interattivo e poi usare ciò che ha imparato per automatizzare il proprio lavoro. Le interfacce utente grafiche possono essere più facili da usare all’inizio, ma una volta imparate, la produttività della shell è imbattibile. E come scrisse Alfred North Whitehead nel 1911, “la civiltà progredisce ampliando il numero di operazioni importanti che possiamo eseguire senza pensarci”

Sfida

find Comprensione della lettura del gasdotto

Scrivere un breve commento esplicativo per il seguente script di shell:

BASH

wc -l $(find . -name "*.dat") | sort -n
  1. Trova tutti i file con estensione .dat ricorsivamente dalla cartella corrente
  2. Conta il numero di righe contenute in ciascuno di questi file
  3. Ordina numericamente l’output del passo 2
Punti Chiave
  • find trova file con proprietà specifiche che corrispondono ai modelli.
  • grep seleziona le righe dei file che corrispondono ai modelli.
  • --help è un’opzione supportata da molti comandi bash e programmi che possono essere eseguiti da Bash, per visualizzare ulteriori informazioni sull’uso di tali comandi o programmi.
  • man [command] visualizza la pagina del manuale per un determinato comando.
  • $([command]) inserisce l’output di un comando al suo posto.