.ps1 脚本在 .bat 文件脚本中报告为“缺少结束符 '}'”

2024-01-04

我在将简短的 PowerShell 脚本转换为 cmd.exe .bat 文件脚本时遇到困难。错误消息(见下文)抱怨“缺少结束'}”。 .ps1 脚本按预期成功运行。

我在赋值语句末尾使用了分号字符。我用脱字符号 (^) 转义了垂直线(竖线)字符。我缺少什么?

这是 .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 shell...

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".

从 .bat 文件脚本运行 .ps1 脚本也可以按预期工作。

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".

Update:

根据@mklement0的建议,我删除了引号字符。两 (2) 个垂直线(竖线)字符被转义,并且行首插入符位于-match正则表达式被转义。我从一开始就避免在 .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; ^
            }; ^
        };

  • In cmd.exe因此也在批处理文件中,^只充当转义字符unquoted strings;因此,一般来说你会not需要逃跑cmd.exe元字符,例如| inside "..."字符串为^|- 如果你这样做,^字符是retained.

  • However, it looks like using cmd.exe's line continuation (a line-ending ^) with double-quoted-per-line strings doesn't work robustly[1] (and having a single double-quoted string span multiple lines is fundamentally unsupported), so the solution is to use unquoted lines, on which you do need to escape | as ^|, among other characters.

下面是一个练习各种 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\"

Note the inclusion of [Environment]::CommandLine; as the first statement, which will echo the command line as seen by PowerShell, which can help with troubleshooting. As an aside: When using PowerShell (Core) 7+'s CLI, pwsh.exe, rather than Windows PowerShell's powershell.exe, the command line reported is a reconstructed form that is not guaranteed to reflect the actual command line used; notably, "" sequences turn to \".

Note:

  • 每条内部线路必须有^ as the 最后特点在线上。

  • 因为续行不包括换行符,;必须用于显式终止每个 PowerShell 语句(最后一个除外)。

    • 注意:插入一个空行(没有结束它^) 语句之间确实not工作:虽然从技术上讲,它在传递给 PowerShell 时确实会产生实际的换行符,但由于缺乏整体双引号,PowerShell 将此类换行符视为与spaces,因此仍然需要;语句之间。

    • 正是由于这个原因,单行注释(# ...) are not支持此调用方法,因为这样的注释总是跨越该行的其余部分,不支持;结束它们 - 请参阅下一点。

  • 为了包括comments, 表格^<# ... #^>- 即(逃脱)inline必须使用注释- 正常的单行注释(# ....) are not支持(见上一点)。

  • cmd.exe的元字符需要单独的^- 逃避,即:

    • & | < > ^
    • Additionally, if called from a for /f statement:
      • = , ; ( )
  • "字符必须转义为\"以便电源外壳将它们视为要执行的命令的一部分(否则它们在命令行解析期间会被删除);所以,在可行的情况下,使用'...'(单引号)更容易.

    • 如果只有one or an 奇数 of "字符。在内部线上,将那个/最后一个转义为\^" (sic).

    • Inside \"...\", cmd.exe元字符做not need ^- 逃避,因为cmd.exe将这样的 PowerShell 转义字符串视为常规双引号字符串。

    • 但是,空白规范化适用于内部内容\"...\";也就是说,运行multiple空间被折叠成one每个空间;如果这是一个问题,请使用^"\"...\"^" (sic).

  • For 附加信息,包括一个for /f示例以及如何处理转义! when setlocal enabledelayedexpansion已生效,请参阅这个答案 https://stackoverflow.com/a/66085286/45375.


[1] It only works if you ensure that any interior "..."^ line and a closing "..." line starts with at least one whitespace character. However, in most cases it will be less effort and arguably more readable not to double-quote the individual lines and to instead selectively ^-escape cmd.exe's metacharacters, as shown.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

.ps1 脚本在 .bat 文件脚本中报告为“缺少结束符 '}'” 的相关文章

随机推荐