Подтвердить что ты не робот

Зачем мне сначала писать мои функции в PowerShell script?

У меня есть script, что я использую функции для обертывания частей кода, которые позволяют мне перемещаться по разделам в указанной точке. Я обнаружил, что мне нужно иметь функции, перечисленные первыми в script, чтобы он работал правильно.

Нерабочий пример

$stepChoice = read-host 'Where would you like to start.'

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}

}

# Steps.ps1 
function Step1 { 
    'Step 1' 
    Step2 
} 
function Step2 { 
    'Step 2' 
    Step3 
} 
function Step3 { 
    'Step 3' 
    'Done!' 
}

Error

Это дает мне следующую ошибку:

Термин "Step1" не распознается как имя командлета, функции, файла script или исполняемой программы. Проверьте правильность написания имени или, если включен путь, проверьте правильность пути и повторите попытку.

 At C:\Tools\Scripts\functiontest.ps1:7 char:12
  +     1{Step1 <<<< }
  + CategoryInfo          : ObjectNotFound: (Step1:String) [], CommandNotFoundException
  + FullyQualifiedErrorId : CommandNotFoundException*

Рабочий пример

Если я изменяю порядок вокруг, он отлично работает:

# Steps.ps1 
function Step1 { 
    'Step 1' 
    Step2 
} 
function Step2 { 
    'Step 2' 
    Step3 
} 
function Step3 { 
    'Step 3' 
    'Done!' 
}

#steps
$stepChoice = read-host 'Where would you like to start.'

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}

}

Почему?

Я предполагаю, что это потому, что PS не загружает функции.

Почему это и есть лучший способ выложить эту структуру кода?

4b9b3361

Ответ 1

Изменить порядок сценария

PowerShell - это скрипт, а не скомпилированный язык. Таким образом, он проходит через скрипт построчно, сверху вниз (после токенизации скрипта) и оценивает каждую команду по пути. Если он еще не дошел до определения функции и вы уже пытаетесь вызвать эту функцию, PowerShell выдаст ошибку.

Следовательно, в этом случае вы должны переместить определения функций перед оператором switch - как вы обнаружили.

Форвардные декларации

Даже некоторые скомпилированные языки ведут себя так, особенно C/C++, и требуют предварительных объявлений, чтобы обойти эту проблему.

Другие скомпилированные языки, такие как С#, делают несколько проходов по коду во время компиляции, так что предварительные объявления не требуются.

Ответ 2

Помните, что в общем случае, что работает в script, должно работать в командной строке.

Это было неверно в CMD. GOTO и FOR %I IN (...) DO %%I - два примера.

В PowerShell я могу запускать команды в командной строке, пока не получу результат, который я хочу, затем вставьте историю в script, а затем отредактируйте посторонние биты.

Кроме того, я могу использовать script, который работает некорректно, вставьте его в интерактивную оболочку и изучите полученное состояние.

В интерактивной командной строке вы не можете написать это:

F
function F { "Hello, World!" }

Однако, читая script, я хочу сначала прочитать код верхнего уровня, а затем увидеть более подробно при прокрутке вниз. Один из подходов:

function Main 
{
    F
}

function F
{
    "Hello, World!"
}

Main

Ответ 3

Вы также можете указать определения своих функций из отдельного файла:

Steps-Lib.ps1

# Since this is just function definitions it is safe to source
function Step1 { 
    'Step 1' 
    Step2 
} 
function Step2 { 
    'Step 2' 
    Step3 
} 
function Step3 { 
    'Step 3' 
    'Done!' 
}

Steps.ps1

# This sources the Steps-Lib.ps1 so that the functions are available
. "./Steps-Lib.ps1"

$stepChoice = read-host 'Where would you like to start.'

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}
}

Ответ 4

В дополнение к тому, что Кейт сказал о заказе переводчика, он также является частью дизайна Powershell. Он действительно должен вести себя как интерфейс с объектами CLR и даже с его собственными командлетами. Таким образом, в "сценариях" "powershell" вы менее строя этот массово сложный список действий, которые нужно предпринять, и более собираете коллекцию других, более мелких частей логики и определяя, как с ними взаимодействовать.

Не вдаваясь в квазирелигиозную дискуссию Powershell и OOP, самый простой способ добиться того, что вы хотите, - это похоронить все ваши функции в отдельном файле (назовите его functions.ps1), затем включите это в начале.

Итак, если все было в functions1.ps1

выполните

$functions = "$($MyInvocation.MyCommand.path | split-path)\functions.ps1"
. $functions

то

switch($stepChoice)
{
    1{Step1}
    2{Step2}
    3{Step3}

}

Работает просто отлично

Ответ 5

Решение из блога Microsoft, заключить основной код в блок и позвонить в конце,

$MainFunction={
   $stepChoice = read-host 'Where would you like to start.'
   switch($stepChoice)
   {
       1{Step1}
       2{Step2}
       3{Step3}
   }
}
# Steps.ps1 
function Step1 { 
  'Step 1' 
   Step2 
} 
function Step2 { 
  'Step 2' 
   Step3 
} 
function Step3 { 
  'Step 3' 
  'Done!' 
}
#This line executes the program
& $MainFunction