Сценарий .ps1 получил сообщение «Отсутствует закрытие '}'» в сценарии файла .bat
Мне сложно преобразовать короткий сценарий PowerShell в сценарий файла cmd.exe .bat. Сообщение об ошибке (см. Ниже) жалуется на «Отсутствует закрытие»}. Сценарий .ps1 успешно выполняется, как и ожидалось.
Я использовал символы SEMICOLON в конце операторов присваивания. Я экранировал символ ВЕРТИКАЛЬНАЯ ЛИНИЯ (вертикальная черта) с помощью КАРТЫ (^). Что мне не хватает?
Вот сценарий .bat и вывод сообщения об ошибке.
PS C:\src\t> Get-Content -Path .\DistributeFiles2.bat
powershell -NoLogo -NoProfile -Command ^
"$ProjectPath = Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\Project';" ^
"$NFilesPerDirectory = 400;" ^
"Get-ChildItem -File -Path (Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\images') -Filter '*.jpeg' ^|" ^
"ForEach-Object {" ^
"# Check to see if the filename starts with four (4) digits.;" ^
"if ($_.BaseName -match '^(\d{4}).*') {" ^
"$FolderNumber = [math]::Floor([int]$Matches[1] / $NFilesPerDirectory);" ^
"$FolderName = 'Folder' + $FolderNumber.ToString();" ^
"$FolderPath = Join-Path -Path $ProjectPath -ChildPath $FolderName;" ^
"# If the destination directory does not exist, create it.;" ^
"if (-not (Test-Path -Path $FolderPath)) { mkdir $FolderPath -WhatIf ^| Out-Null }" ^
"# Move the file to the destination directory.;" ^
"Move-Item -Path $_.FullName -Destination $FolderPath -WhatIf" ^
"}" ^
"}"
Вернемся в оболочку cmd.exe ...
C:>DistributeFiles2.bat
9:55:41.67 C:\src\t
C:>powershell -NoLogo -NoProfile -Command "$ProjectPath = Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\Project';" "$NFilesPerDirectory = 400;" "Get-ChildItem -File -Path (Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\images') -Filter '*.jpeg' ^|" "ForEach-Object {" "# Check to see if the filename starts with four (4) digits.;" "if ($_.BaseName -match '^(\d{4}).*') {" "$FolderNumber = [math]::Floor([int]$Matches[1] / $NFilesPerDirectory);" "$FolderName = 'Folder' + $FolderNumber.ToString();" "$FolderPath = Join-Path -Path $ProjectPath -ChildPath $FolderName;" "# If the destination directory does not exist, create it.;" "if (-not (Test-Path -Path $FolderPath)) { mkdir $FolderPath -WhatIf ^| Out-Null }" "# Move the file to the destination directory.;" "Move-Item -Path $_.FullName -Destination $FolderPath -WhatIf" "}" "}"
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingEndCurlyBrace
Это исходный сценарий .ps1, который работает должным образом.
PS C:\src\t> Get-Content -Path .\DistributeFiles.ps1
$ProjectPath = Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\Project';
$NFilesPerDirectory = 400;
Get-ChildItem -File -Path (Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\images') -Filter '*.jpeg' |
ForEach-Object {
# Check to see if the filename starts with four (4) digits.;
if ($_.BaseName -match '^(\d{4}).*') {
$FolderNumber = [math]::Floor([int]$Matches[1] / $NFilesPerDirectory);
$FolderName = 'Folder' + $FolderNumber.ToString();
$FolderPath = Join-Path -Path $ProjectPath -ChildPath $FolderName;
# If the destination directory does not exist, create it.;
if (-not (Test-Path -Path $FolderPath)) { mkdir $FolderPath -WhatIf | Out-Null }
# Move the file to the destination directory.;
Move-Item -Path $_.FullName -Destination $FolderPath -WhatIf
}
}
PS C:\src\t> .\DistributeFiles.ps1
What if: Performing the operation "Create Directory" on target "Destination: C:\Users\lit\Desktop\Project\Folder0".
What if: Performing the operation "Move File" on target "Item: C:\Users\lit\Desktop\images\0000.jpeg Destination: C:\Users\lit\Desktop\Project\Folder0".
What if: Performing the operation "Create Directory" on target "Destination: C:\Users\lit\Desktop\Project\Folder1".
What if: Performing the operation "Move File" on target "Item: C:\Users\lit\Desktop\images\0401.jpeg Destination: C:\Users\lit\Desktop\Project\Folder1".
Запуск сценария .ps1 из сценария файла .bat также работает должным образом.
PS C:\src\t> Get-Content -Path .\DistributeFiles.bat
@powershell -NoLogo -NoProfile -File "%~dp0%~n0.ps1"
PS C:\src\t> .\DistributeFiles.bat
What if: Performing the operation "Create Directory" on target "Destination: C:\Users\lit\Desktop\Project\Folder0".
What if: Performing the operation "Move File" on target "Item: C:\Users\lit\Desktop\images\0000.jpeg Destination: C:\Users\lit\Desktop\Project\Folder0".
What if: Performing the operation "Create Directory" on target "Destination: C:\Users\lit\Desktop\Project\Folder1".
What if: Performing the operation "Move File" on target "Item: C:\Users\lit\Desktop\images\0401.jpeg Destination: C:\Users\lit\Desktop\Project\Folder1".
Обновлять:
Следуя совету @mklement0, я удалил символы QUOTATION MARK. Два (2) символа ВЕРТИКАЛЬНАЯ СТРОКА (вертикальная черта) экранируются, а символ CARET в начале строки
-match
регулярное выражение экранировано. Я с самого начала избегал использования символов QUOTATION MARK в скрипте .ps1. Ошибка «Отсутствует закрытие '}'» по-прежнему происходит. Что мне не хватает?
C:>powershell -NoLogo -NoProfile -Command ^
More? $ProjectPath = Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\Project'; ^
More? $NFilesPerDirectory = 400; ^
More? Get-ChildItem -File -Path (Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\images') -Filter '*.jpeg' ^| ^
More? ForEach-Object { ^
More? # Check to see if the filename starts with four (4) digits.; ^
More? if ($_.BaseName -match '^^(\d{4}).*') { ^
More? $FolderNumber = [math]::Floor([int]$Matches[1] / $NFilesPerDirectory); ^
More? $FolderName = 'Folder' + $FolderNumber.ToString(); ^
More? $FolderPath = Join-Path -Path $ProjectPath -ChildPath $FolderName; ^
More? # If the destination directory does not exist, create it.; ^
More? if (-not (Test-Path -Path $FolderPath)) { mkdir $FolderPath -WhatIf ^| Out-Null }; ^
More? # Move the file to the destination directory.; ^
More? Move-Item -Path $_.FullName -Destination $FolderPath -WhatIf; ^
More? }; ^
More? };
Missing closing '}' in statement block or type definition.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingEndCurlyBrace
Обновление 2:
Благодаря неизменно хорошему совету @mklement0 вот рабочий код.
powershell -NoLogo -NoProfile -Command ^
$ProjectPath = Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\Project'; ^
$NFilesPerDirectory = 400; ^
Get-ChildItem -File -Path (Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\images') -Filter '*.jpeg' ^| ^
ForEach-Object { ^
^<# Check to see if the filename starts with four (4) digits.#^> ^
if ($_.BaseName -match '^^(\d{4}).*') { ^
$FolderNumber = [math]::Floor([int]$Matches[1] / $NFilesPerDirectory); ^
$FolderName = 'Folder' + $FolderNumber.ToString(); ^
$FolderPath = Join-Path -Path $ProjectPath -ChildPath $FolderName; ^
^<# If the destination directory does not exist, create it.#^> ^
if (-not (Test-Path -Path $FolderPath)) { mkdir $FolderPath -WhatIf ^| Out-Null }; ^
^<# Move the file to the destination directory.#^> ^
Move-Item -Path $_.FullName -Destination $FolderPath -WhatIf; ^
}; ^
};
1 ответ
В и, следовательно, в пакетных файлах действует только как escape-символ в строках без кавычек ; поэтому, как правило, вам не нужно экранировать метасимволы, такие как внутри
"..."
строки как - если вы это сделаете, символы сохранятся .Однако похоже, что использование продолжения строки (окончание _line) со строками, заключенными в двойные кавычки для каждой строки , не работает надежно (и наличие одной строки в двойных кавычках, охватывающей несколько строк, принципиально не поддерживается), поэтому решение заключается в использовании некотируемых линий, на которых вы действительно нужны , чтобы избежать
|
в качестве^|
, среди других персонажей.
Вот пример, в котором используются различные синтаксические конструкции PowerShell:
powershell -NoProfile -Command ^
[Environment]::CommandLine; ^
$ProjectPath = Join-Path -Path $Env:USERPROFILE -ChildPath 'Desktop\Project'; ^
^<# This is a comment - note the required *inline* comment syntax #^> ^
$ProjectPath ^| ^
ForEach-Object { \"[$ProjectPath]\" }; ^
(42).ToString(); ^
\"A & B\"
Обратите внимание на включение
[Environment]::CommandLine;
в качестве первого оператора, который будет отображать командную строку в том виде, в каком ее видит PowerShell, что может помочь в устранении неполадок. В стороне: при использовании интерфейса командной строки PowerShell (Core) 7+,
pwsh.exe
, а не Windows PowerShell
powershell.exe
, сообщаемая командная строка является реконструированной формой, которая не обязательно отражает фактическую используемую командную строку; в частности,
""
последовательности превращаются в.
Примечание:
Каждая внутренняя строка должна иметь самый последний символ в строке.
Поскольку продолжение строки не включает новую строку, его необходимо использовать для явного завершения каждого оператора PowerShell (кроме последнего).
Примечание. Вставка пустой строки (без ее окончания) между операторами не работает: хотя технически это приводит к фактическому переходу строки при передаче в PowerShell, отсутствие общего двойного кавычки заставляет PowerShell обрабатывать такие символы новой строки так же, как пробелы , которые поэтому по-прежнему необходимо между заявлениями.
По этой причине однострочные комментарии (
# ...
) не поддерживаются этим методом вызова, учитывая, что такие комментарии неизменно охватывают остальную часть строки , без поддержки;
чтобы покончить с ними - см. следующий пункт.
Чтобы добавить комментарии , форма
^<# ... #^>
- т.е. (экранированные) встроенные комментарии должны использоваться - обычные однострочные комментарии (# ....
) не поддерживаются (см. предыдущий пункт).Для метасимволов требуется индивидуальное экранирование, а именно:
-
& | < > ^
- Кроме того, если вызывается из оператора :
-
= , ; ( )
-
-
"
символы должны быть экранированы как\"
так что PowerShell рассматривает их как часть выполняемых команд (в противном случае они удаляются во время синтаксического анализа командной строки).Внутри метасимволы не нужны
^
-эскейп, потому чтоcmd.exe
видит такую экранированную строку для PowerShell как обычную строку с двойными кавычками.Однако нормализация пробелов применяется к тому, что находится внутри
\"...\"
; то есть, пробеги из нескольких пробелов складываются в одну пробел каждая; если это вызывает беспокойство, используйте^"\"...\"^"
(так в оригинале).
Для получения дополнительной информации, включая
for /f
пример и как справиться с экранированием!
когдаsetlocal enabledelayedexpansion
действует, см. этот ответ .