Kun je deze Bash Scripting-oefening oplossen?

Als je It's FOSS op Facebook volgt, ben je je misschien bewust van de wekelijkse Bash Challenge. Het is een gezamenlijke inspanning van Yes I Know It en It's FOSS om je een Bash script-oefening te geven om je Linux-vaardigheden te testen.

We brengen deze Bash Challenge van Facebook naar een breder publiek op het reguliere internet. Dit is het vijfde deel van deze serie. De eerste 4 uitdagingen zijn te vinden op onze Facebook-pagina's. Je kunt deze uitdagingen ook in boekvorm kopen:

Bash-uitdaging 5

We zullen u een terminal screenshot laten zien en u vragen uit te leggen waarom het resultaat niet het resultaat is dat we verwachtten. Natuurlijk, het leukste en meest creatieve deel van de uitdaging zal zijn om te vinden hoe de opdracht (en) die op het scherm worden weergegeven, kunnen worden hersteld om het juiste resultaat te verkrijgen.

Klaar om te spelen? Dus hier is de uitdaging van deze week:

My Bash weet niet hoe te tellen [Moeilijkheidsgraad 1]

Deze week heb ik een bestand met hele getallen, één op elke regel:

cat sample.data 102 071 210 153 

En ik wil de som van al die getallen berekenen:

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

Helaas is het resultaat dat ik verkrijg verkeerd (het verwachte resultaat was 536):

 Sum is: 522 

Uitdaging

Jouw uitdaging is om te vinden:

  • Waarom was dat resultaat verkeerd?
  • Hoe mijn opdrachten herstellen om het juiste resultaat te verkrijgen?

★ Bonus eenhoornpunt als je een oplossing kunt vinden met alleen Bash interne commando's en / of shell-substituties.

We kijken er naar uit om uw oplossingen te lezen in de commentaarsectie hieronder! Vergeet niet creatief te zijn.

Weinig details

Om deze uitdaging te creëren, gebruikte ik:

  • GNU Bash, versie 4.4.5 (x86_64-pc-linux-gnu)
  • Debian 4.8.7-1 (amd64)
  • Alle opdrachten zijn die met een standaard Debian-distributie
  • Geen opdracht is gealiast

Oplossing

Hoe te reproduceren

Dit is de ruwe code die we hebben gebruikt om deze uitdaging aan te gaan. Als u dat in een terminal uitvoert, kunt u exact hetzelfde resultaat reproduceren als in de illustratie van de uitdaging (ervan uitgaande dat u dezelfde softwareversie als mij gebruikt):

 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" 

Wat was het probleem ?

Het probleem werd veroorzaakt door de 071 waarde. Zoals je hebt gemerkt, begint dit nummer met een 0 - waarschijnlijk om er zeker van te zijn dat alle gegevens op drie cijfers zijn opgemaakt. Niets ingewikkelds hier, behalve dat ... na een ongelukkige conventie overgenomen van de C-programmeertaal, een nummer prefixen met 0 een manier is om op te geven dat getal wordt uitgedrukt in octaal en niet in decimaal .

Octale getallen worden uitgedrukt met cijfers van 0 tot 7 . Hier is een eenvoudige conversietabel:

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

Deze laatste waarde is de oorzaak van de fout bij het evalueren van de som. De Bash lezen 071 en interpreteerde het als een octaal getal dat de 57 decimale waarde vertegenwoordigt. U kunt dat eenvoudig controleren:

 echo $((071)) 57 

Hoe dat op te lossen?

Ik zie twee hoofdstrategieën om dit probleem op te lossen. De voorloopnullen verwijderen. Of een manier vinden om de shell te laten begrijpen dat al mijn getallen decimale waarden zijn.

Voorloopnullen verwijderen

Hier is een eenvoudige oplossing met het sed externe commando om de voorloopnullen te verwijderen:

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

(bonusvraag: waarom heb ik geen pijp gebruikt in plaats van een procesvervanging ?)

De basis expliciet opgeven

De vorige oplossing is (meestal) eenvoudig - maar met de Bash kunnen we dingen verbeteren. In plaats van te proberen de gegevens te herstellen, zullen we eenvoudigweg expliciet specificeren dat onze getallen worden uitgedrukt in base 10 (decimaal), in plaats van base 8 (octaal). U kunt dat doen door de syntaxis base#value .

Vergelijk die drie voorbeelden:

 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 

Om mijn eerste opdracht te herstellen en het juiste resultaat te krijgen, hoef ik alleen maar de basis 10 expliciet op te geven voor al mijn gegevens:

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

En hier is het juiste resultaat. We hopen dat je die uitdaging leuk vond. Blijf kijken voor meer plezier!

Auteur Bio: Ik ben Sylvain Leroux, een software engineer van passie, een leraar door roeping. Ik heb 15 jaar ervaring in het lesgeven in informatica en informatietechnologie op alle niveaus. Ik ben een groot voorstander van Linux & OpenSource-technologieën. Ik heb Yes I Know IT opgericht om die ervaring met een breder publiek te delen via online cursussen en gratis video's. Aarzel niet om mij op Twitter te bereiken.

Aanbevolen

Popcorn Time installeren op Ubuntu 18.04 en andere Linux-distributies
2019
SuiteCRM: een open source CRM richt zich op Salesforce
2019
Een nieuw Linux OS "OSU" dat Ubuntu Of Arch Linux World wil zijn
2019