Зачем мне сначала писать мои функции в 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 не загружает функции.
Почему это и есть лучший способ выложить эту структуру кода?
Ответы
Ответ 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