Lär mig Powershell, hjälp med skript som ska kolla vädret

Permalänk
Medlem

Lär mig Powershell, hjälp med skript som ska kolla vädret

Hej

Jag är helt ny på programmering generellt och i detta fallet Powershell. Detta är mitt första stora skript som jag ger mig på, och har suttit i många timmar men får det inte riktigt att fungera som det är tänkt.

I korta drag så frågar skriptet dig om du vill kolla vädret i Göteborg eller Stockholm. Sen laddar den antingen ner eller uppdaterar en XML-fil som hämtas från YR.no. Sen läser den in XML-filen och skriver ut väderdatan i konsolen.

Här har jag delat skriptet via Pastebin: https://pastebin.com/BXCiTdVw
Men det är nog lättare om ni klistrar in det i Powershell ISE

Problemet är just nu att valet av plats inte riktigt fungerar. Om du vill ha vädret för GBG så ska variablen $location sättas till just 'GBG'. Men när den sen kör själva funktionen (Get-Weather) så ändras variablen $location automatiskt till 'STO'?

Tar gärna allmänna råd på vad jag gör rätt och fel och vad som kan göras bättre.
Tack på förhand!

Permalänk
Medlem

Problemet ligger i dina "jämförelser" på rad 23 och 24. = är (som i de flesta programmeringsspråk) tilldelningsoperatorn och kan inte användas för jämförelser. Som du skrivit det nu är ekvivalent med:

$location = 'GBG' if ($location) {$XMLPath = 'c:\outputgbg.xml'} $location = 'STO' if ($location) {$XMLPath = 'c:\outputsto.xml'}

När du vill jämföra två värden ska du använda en av jämförelseoperatorerna istället, dvs $location -eq 'GBG'.

Visa signatur

Spela Swemantle! Du vet att du vill.

Ibland har jag fel, men då är det någon annans fel.

Permalänk
Medlem
Skrivet av LemonIllusion:

Problemet ligger i dina "jämförelser" på rad 23 och 24. = är (som i de flesta programmeringsspråk) tilldelningsoperatorn och kan inte användas för jämförelser. Som du skrivit det nu är ekvivalent med:

$location = 'GBG' if ($location) {$XMLPath = 'c:\outputgbg.xml'} $location = 'STO' if ($location) {$XMLPath = 'c:\outputsto.xml'}

När du vill jämföra två värden ska du använda en av jämförelseoperatorerna istället, dvs $location -eq 'GBG'.

Toppen, tack! Jag stötte på det problemet tidigare men det hade tydligen inte riktigt satt sig i min hjärna än! Nu fungerar det bättre.

MEN, den skriver fortfarande inte ut väderdatan.
Just nu har jag ju först en funktion som heter "ReadXML" där den kör Get-Content och läser in XML-filen till ett objekt [xml]$xml.
Men när nästa funktion "WriteResultsToConsole" körs så följer inte datan i variablarna med från "ReadXML".
T.ex, kör jag istället "write-host $weather" i funktionen "ReadXML" så funkar det och rätt data skrivs ut, men kör jag "write-host $weather" i funktionen WriteResultsToConsole så kommer ingenting.

Varför blir det så?

Eller är det överflödigt att dela upp i det i så många funktioner? Jag skulle ju bara kunna slå ihop "ReadXML" och "WriteResultsToConsole" till samma funktion och då funkar det ju. Men jag hade uppfattningen om att det är bra att dela upp allt i små funktioner. Har jag fel då?

Permalänk
Medlem

Efter lite googlande så provade jag att lägga till GLOBAL: i variablarna under "ReadXML"-funktionen och nu funkar det!
Var detta rätt lösning? Måste man lägga till GLOBAL: när man använder variablar inuti funktioner och vill att dom ska följa med till andra funktioner?

Alltså
$GLOBAL:temperature = $xml.weatherdata.forecast.tabular.time[0].temperature.value
istället för
$temperature = $xml.weatherdata.forecast.tabular.time[0].temperature.value

Permalänk
Legendarisk

@naikon: Det är ofta bättre om du kan skriva funktioner som ka ta vad de behöver som parametrar och sedan returnera ett resultat utan sidoeffekter. Just nu anropar du t.ex. ReadXML utan parametrar, och den läser själv in vad den behöver från det omgivande scopet och förändrar sedan det istället för att returnera ett svar. Förutom att det tenderar att göra koden svårare att förstå och arbeta med så drar du inte nytta av Powershells pipeline på det sättet. Något sådant här vore lättare för en enskild funktion:

# Säger vad den behöver: function Read-XML($filename) { [xml]$xml = Get-Content $filename # Returnerar resultatet: return @{ temperature = $xml.weatherdata.forecast.tabular.time[0].temperature.value weather = $xml.weatherdata.forecast.tabular.time[0].symbol.name weathertomorrow = $xml.weatherdata.forecast.tabular.time[4].symbol.name sunrise = $xml.weatherdata.sun.rise sunset = $xml.weatherdata.sun.set } }

Istället för nästlade funktioner så skulle du även kunna strukturera det som fristående funktioner där resultaten är avsedda att pipas vidare till nästa steg på det här sättet:

function Get-WeatherLocation() { do { $location = Read-Host -Prompt "Stad" } while($location -ne "GBG") return $location } function Get-WeatherData( [Parameter(ValueFromPipeline=$true)] $location ) { return @{ Väder = "Sharknado" Plats = $location } } function Format-WeatherReport( [Parameter(ValueFromPipeline=$true)] $data ) { return "Vädret i {0} är: {1}" -f ` $data.Plats, ` $data.Väder } Get-WeatherLocation | ` # Be användaren om stad Get-WeatherData | ` # Hämta aktuell väderdata Format-WeatherReport # Skriv ut rapport


Sedan bör du inte anropa Set-ExecutionPolicy i början av ditt script. Det kommer påverka din miljö på ett oväntat och potentiellt skadligt sätt, det kräver att du kör scriptet som administratör, och du skulle inte komma så långt utan att få köra scriptet ändå.

Visa signatur

Abstractions all the way down.