Riesci a risolvere questo esercizio di scripting Bash?

Se segui FOSS su Facebook, potresti essere informato della Bash Challenge settimanale. È uno sforzo congiunto di Yes I Know It e It's FOSS per darti un esercizio di script Bash per testare le tue abilità di Linux.

Stiamo portando questa Bash Challenge da Facebook a un pubblico più ampio sul web regolare. Questa è la quinta puntata di questa serie. Le prime 4 sfide possono essere trovate sulle nostre pagine di Facebook. Puoi anche comprare queste sfide in forma di libro:

Bash Challenge 5

Ti mostreremo uno screenshot del terminale e ti chiederemo di spiegare perché il risultato non è quello che ci aspettavamo. Naturalmente, la parte più divertente e più creativa della sfida sarà trovare come correggere i comandi visualizzati sullo schermo per ottenere il risultato corretto.

Pronto a giocare? Quindi ecco la sfida di questa settimana:

My Bash non so come contare [Livello di difficoltà 1]

Questa settimana, ho alcuni file di dati contenenti numeri interi, uno su ogni riga:

cat sample.data 102 071 210 153 

E voglio calcolare la somma di tutti quei numeri:

 declare -i SUM=0 while read X ; do SUM+=$X done < sample.data echo "Sum is: $SUM" 

Sfortunatamente, il risultato che ottengo è sbagliato (il risultato previsto era 536):

 Sum is: 522 

Sfida

La tua sfida è trovare:

  • Perché quel risultato era sbagliato?
  • Come risolvere i miei comandi per ottenere il risultato corretto?

★ Punto unicorno bonus se è possibile trovare una soluzione utilizzando solo comandi interni Bash e / o sostituzioni shell.

Non vediamo l'ora di leggere le tue soluzioni nella sezione commenti qui sotto! Non dimenticare di essere creativo.

Pochi dettagli

Per creare questa sfida, ho usato:

  • GNU Bash, versione 4.4.5 (x86_64-pc-linux-gnu)
  • Debian 4.8.7-1 (amd64)
  • Tutti i comandi sono quelli forniti con una distribuzione Debian standard
  • Nessun comando era alias

Soluzione

Come si riproduce

Ecco il codice raw che abbiamo usato per produrre questa sfida. Se lo esegui in un terminale, sarai in grado di riprodurre esattamente lo stesso risultato visualizzato nell'illustrazione della sfida (supponendo che tu stia utilizzando la stessa versione del software di me):

 rm -rf ItsFOSS mkdir -p ItsFOSS cd ItsFOSS cat > sample.data << 'EOT' 102 071 210 153 EOT clear cat sample.data declare -i SUM=0 while read X ; do SUM+=$X done < sample.data echo "Sum is: $SUM" 

Qual'era il problema ?

Il problema è stato causato dal valore 071 . Come hai notato, questo numero inizia con uno 0 - probabilmente per garantire che qui tutti i dati siano formattati su tre cifre. Niente di complicato qui, tranne che ... seguendo una sfortunata convenzione ereditata dal linguaggio di programmazione C, il prefisso di un intero per 0 è un modo per specificare che il numero è espresso in ottale e non in decimale .

I numeri ottali sono espressi con cifre da 0 a 7 . Ecco una semplice tabella di conversione:

OctalDecimale
00
11
22
33
44
55
66
77
108
119
1210
1311
1412
....
7157

Questo ultimo valore è ciò che ha causato l'errore durante la valutazione della somma. Il Bash legge 071 e lo interpreta come un numero ottale che rappresenta il valore 57 decimale. Puoi verificarlo facilmente:

 echo $((071)) 57 

Come risolverlo?

Posso vedere due strategie principali per risolvere quel problema. O rimuovere gli zeri iniziali. O trovare un modo per far capire alla shell che tutti i miei numeri sono valori decimali .

Rimozione degli zeri iniziali

Ecco una soluzione semplice che usa il comando esterno sed per rimuovere gli zeri iniziali:

 declare -i SUM=0 while read X ; do SUM+=$X done < <(sed -E s/^0+// sample.data) echo "Sum is: $SUM" 

(domanda bonus: perché non ho usato un tubo invece di una sostituzione di processo ?)

Specificare esplicitamente la base

La soluzione precedente è (principalmente) semplice - ma Bash ci consente di migliorare le cose. Invece di provare a correggere i dati, specificheremo semplicemente esplicitamente che i nostri numeri sono espressi in base 10 (decimale), anziché in base 8 (ottale). Puoi farlo usando la sintassi del base#value .

Confronta questi tre esempi:

 echo $((071)) # The leading `0` specify the number as octal 57 echo $((8#071)) # We *explicitly* specify base 8 (octal) 57 echo $((10#071)) # We *explicitly* specify base 10 (decimal) 71 

Per correggere il mio comando iniziale e ottenere il risultato corretto, devo solo specificare esplicitamente la base 10 per tutti i miei dati:

 declare -i SUM=0 while read X ; do SUM+=$((10#$X)) done < sample.data echo "Sum is: $SUM" 

Ed ecco il risultato corretto. Speriamo che questa sfida ti sia piaciuta. Restate sintonizzati per più divertimento!

Bio autore: Sono Sylvain Leroux, un ingegnere del software per passione, un insegnante per vocazione. Ho 15 anni di esperienza nell'insegnamento dell'informatica e delle tecnologie dell'informazione a tutti i livelli. Sono un forte sostenitore delle tecnologie Linux e OpenSource. Ho fondato Yes I Know IT per condividere questa esperienza con un pubblico più ampio attraverso corsi online e video gratuiti. Non esitate a contattarmi su Twitter.

Raccomandato

Il nuovo lettore musicale Elisa di KDE: così vicino, eppure così lontano
2019
MPV Player: un riproduttore video minimalista per Linux
2019
Le migliori distribuzioni Linux basate su Fedora
2019