Spela Swemantle! Du vet att du vill.
Ibland har jag fel, men då är det någon annans fel.
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!
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'
.
Spela Swemantle! Du vet att du vill.
Ibland har jag fel, men då är det någon annans fel.
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å?
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
@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å.
Abstractions all the way down.
Copyright © 1999–2024 Geeks AB. Allt innehåll tillhör Geeks AB.
Citering är tillåten om källan anges.