【问题标题】:How to split a command over multiple lines in appveyor.yml如何在 appveyor.yml 中将命令拆分为多行
【发布时间】:2016-10-04 06:42:53
【问题描述】:

我想在我的appveyor.yml 文件中将一个长构建命令拆分为多行,但是我无法将其解包,因此当第一个FOR 命令被切断并返回错误时,构建失败。我不确定如何正确拆分 .yml 文件中的行,以便在 Appveyor 中重新组装它们。如何做到这一点?

这是一个简化版:

build_script:
- cmd: >-
    @echo off
    FOR %%P IN (x86,x64) DO ( ^
      FOR %%C IN (Debug,Release) DO ( ^
        msbuild ^
          /p:Configuration=%%C ^
          /p:Platform=%%P ... ^
        || EXIT 1 ^
      ) ^
    )

我希望它在 AppVeyor 中显示为:

@echo off
FOR %%P IN (x86,x64) DO ( FOR %%C IN (Debug,Release) DO ( msbuild /p:Configuration=%%C /p:Platform=%%P ... || EXIT 1 ) )

多余的空格并不重要,重要的是以FOR 开头的行直到最后的) 出现在同一行。

请注意,理论上 Appveyor 也可以接受:

@echo off
FOR %%P IN (x86,x64) DO ( ^
  FOR %%C IN (Debug,Release) DO ( ^
    msbuild ^
      /p:Configuration=%%C ^
      /p:Platform=%%P ... ^
    || EXIT 1 ^
  ) ^
)

因为 Windows cmd.exe 解释器随后会在每一行的末尾看到继续标记 (^) 并将它们视为一个大长命令,除了 Appveyor 似乎没有识别^ 标记,因此它一次将每一行发送到cmd.exe,而不是一起发送整个多行块。

这意味着第一个选项看起来是唯一可行的解​​决方案,其中构建 YAML 时将 FOR 行及其后的所有内容合并为一行。

我试过了:

  • 每行末尾没有多余字符的单个间距。根据this guide 的说法,单行距的 YML 行应该被解包成一行,但 Appveyor 不会发生这种情况。
  • 双倍行距,每行末尾没有多余的字符。这应该使每一行成为一个单独的命令,实际上它们是,因为第一个 FOR 命令失败并出现 error 255 因为它不完整(只有 FOR 行存在,而不是循环的其余部分。)
  • 双倍行距以^ 结尾。 Appveyor 一次只运行一行,所以我在第一个不完整的FOR 命令上得到一个error 255
  • 单行距以^ 结尾,如上所示。与双倍行距相同的问题,error 255 来自不完整的 FOR 命令。
  • 在运行单独的命令(例如多个msbuild 语句)时,以&& ^ 结束每一行确实有效,但这不适用于FOR 循环,因为如果没有前面的命令,您就不能拥有&&它。

appveyor.yml 中将单个cmd 命令拆分为多行是否有技巧?

【问题讨论】:

    标签: yaml appveyor


    【解决方案1】:

    如何在 appveyor.yml 中将命令拆分为多行?

    以下是一些批处理、cmd、ps 的语法示例。

    我希望这些示例可以为您节省一些时间...

    语法示例

    批次

    # please note the & at EOL in the next example
    install:
        # Install VULKAN_SDK 
        - if not exist %VULKAN_SDK% (
           curl -L --silent --show-error --output Vulkan_SDK_Installer.exe https://sdk.lunarg.com/sdk/download/%VULKAN_VERSION%/windows/VulkanSDK-%VULKAN_VERSION%-Installer.exe?Human=true &
           Vulkan_SDK_Installer.exe /S
        )
        - dir %VULKAN_SDK%
    
    before_build:
      - |-
        set MINGW32_ARCH=i686-w64-mingw32
      - if exist %PREFIX% set NEEDDEPENDS=rem
    
      # Depends
      - |-
        %NEEDDEPENDS% mkdir %PREFIX%\include\SDL2
        %NEEDDEPENDS% mkdir %PREFIX%\lib
        %NEEDDEPENDS% cd %TEMP%
        %NEEDDEPENDS% appveyor DownloadFile https://sourceforge.net/projects/gnuwin32/files/gettext/0.14.4/gettext-0.14.4-lib.zip
        %NEEDDEPENDS% mkdir gettext-0.14.4-lib
        %NEEDDEPENDS% move gettext-0.14.4-lib.zip gettext-0.14.4-lib
        %NEEDDEPENDS% cd gettext-0.14.4-lib
        %NEEDDEPENDS% 7z x gettext-0.14.4-lib.zip > nul
        %NEEDDEPENDS% copy include\* %PREFIX%\include > nul
        %NEEDDEPENDS% copy lib\* %PREFIX%\lib > nul
        %NEEDDEPENDS% cd ..
    

    deploy_script:
      # if tagged commit, build/upload wheel
      - IF "%APPVEYOR_REPO_TAG%"=="true" IF NOT "%TESTENV%"=="check" (
          pip install twine &&
          python setup.py register &&
          twine upload -u %PYPI_USER% -p %PYPI_PASS% dist/*
        )
    

    CMD

    before_build:
        - cmd: >-     
    
            mkdir build
    
            cd .\build
    
            set OpenBLAS_HOME=%APPVEYOR_BUILD_FOLDER%/%MXNET_OPENBLAS_DIR%
    
            set OpenCV_DIR=%APPVEYOR_BUILD_FOLDER%/%MXNET_OPENCV_DIR%/build
    
            cmake .. -DOPENCV_DIR=%OpenCV_DIR% -DUSE_CUDA=0 -DUSE_CUDNN=0 -DUSE_NVRTC=0 -DUSE_OPENCV=1 -DUSE_OPENMP=1 -DUSE_BLAS=open -DUSE_DIST_KVSTORE=0 -G "Visual Studio 12 2013 Win64"
    

    附言

    install:
        - ps: >-
    
            git submodule init
    
            git submodule update
    
            if (!(Test-Path ${env:MXNET_OPENBLAS_FILE})) {
    
                echo "Downloading openblas from ${env:MXNET_OPENBLAS_PKG} ..."
    
                appveyor DownloadFile "${env:MXNET_OPENBLAS_PKG}" -FileName ${env:MXNET_OPENBLAS_FILE} -Timeout 1200000
            }
    

    install:
          - ps: |
              Add-Type -AssemblyName System.IO.Compression.FileSystem
              if (!(Test-Path -Path "C:\maven" )) {
                (new-object System.Net.WebClient).DownloadFile('https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip', 'C:\maven-bin.zip')
                [System.IO.Compression.ZipFile]::ExtractToDirectory("C:\maven-bin.zip", "C:\maven")
              }
    

    on_success:
      - ps: |
      if ($true)
      {
        Write-Host "Success"
      }
    

    【讨论】:

    • 感谢样品。其中哪一个将多个 .yml 行重新组合成 Appveyor 中的单行命令?
    • 在任何命令上使用 IF condition () 内的附加括号时要小心。它弄乱了批处理解析并给出了错误。我通过删除括号解决了这个问题。 setlocal enabledelayedexpansion 在这里也可能有帮助,但我还没有尝试过。
    【解决方案2】:

    除了批处理和 PS 之外,还有另一个提示要考虑。除了 mingw64 的“Git for Windows”版本之外,msys2/mingw32/mingw64 还安装在 appveyor VM C:\msys64\usr\bin\bash 上。

    不幸的是,让 bash 中的多行命令在 appveyor 中工作并不那么简单:

    例子:

    这样一段代码:

    cd /tmp
    for server in $(grep '^Server' /etc/pacman.d/mirrorlist.msys | awk '{print $3}' | shuf | arch=x86_64 envsubst); do
      echo Trying ${server}
      curl --connect-timeout 10 -LO ${server}msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz && break
    done"
    

    变成

      - >-
        C:\msys64\usr\bin\bash -lc "
        cd /tmp;
        for server in $(grep $'\x5eServer' /etc/pacman.d/mirrorlist.msys | awk '{print $3}' | shuf | arch=x86_64 /usr/bin/envsubst); do
        :;  echo Trying ${server};
        :;  curl --connect-timeout 10 -LO ${server}msys2-keyring-r21.b39fb11-1-any.pkg.tar.xz && break;
        done"
    

    为什么会如此不同?

    在 appveyor 中批量处理多行 bash 字符串会出现一些问题:

    1. 批处理中的多行字符串必须以^ 结尾。但是这样做
    # Literal newlines were not working
    - |
      bash -c "echo Not ^
               echo good"
    
    # Double quotes become a literal quote
    # Equivalent: echo This is""echo now dangling
    #             and "" is an escaped "
    - |
      bash -c "echo This is"^
              "echo now dangling"
    
    # Double double quote cancel out, but you're back
    # to a literal newline, not working
    - |
      bash -c "echo This does not"^
              """echo work either"
    
    # The `|` notation does not seem to be useful here
    # It just results in a string that won't run in appveyor 
    # as we desired, when using literal newlines.
    
    1. 折叠需要全部在同一个缩进上,所以不允许额外的缩进
    # > vs >- vs >+ isn't important, it just strips extra newlines at the end
    - >
      bash -c "echo This
      echo works"
    
    # Indent means literal newline again
    - >
      bash -c "echo Does not
               echo work"
    
    # Says "This echo says", not "This" and "says"
    - >-
      bash -c "
      echo This
      echo says"
    
    # Basically: bash -c "echo This; echo works"
    - >-
      bash -c "
      echo This;
      echo works"
    
    1. 为了有正确的缩进,我们需要用一些虚拟字符来欺骗 yaml
    # You cannot have ; after keywords then, do, etc...
    - >-
      bash -c "
      if [ 1 == 1 ]; then
      ;  echo No;
      ;  echo good;
      fi
    
    # Add in a dummy "true" + semicolon, as a no-op
    - >-
      bash -c "
      if [ 1 == 1 ]; then
      :;  echo This works;
      fi
    
    # Or if you prefer this style
    - >-
      bash -c "if [ 1 == 1 ]; then
      :;         echo ""This also works"";
      :;       fi
    
    1. 臭名昭著的插入符号。显然有些东西将^ 扩展为^^,并且没有什么好办法解决它
    # echoes ^^
    bash -c "echo ^"
    bash -c "echo \^"
    bash -c "echo \\^"
    
    # echoes ^^^^ 
    bash -c "echo ^^"
    
    # Uses a hex notation to get around the issue
    bash -c "echo $'\x5e'"
    
    1. 你必须小心 mingw64 中的一些命令,因为它们会引入 \r\n,而在 bash 中你只需要一个 \n
    # This is really using /mingw64/bin/envsubst in MINGW64 mode
    bash -c "for x in $(echo $'${PWD}\n${OLDPWD}' | envsubst); do
             :;  echo ""${x}"" | xxd;
             done"
    
    # Either need to have MSYSTEM set to MSYS, or
    bash -c "for x in $(echo $'${PWD}\n${OLDPWD}' | /usr/bin/envsubst); do
             :;  echo ""${x}"" | xxd;
             done"
    
    # or, as a last resort, use dos2unix
    bash -c "for x in $(echo $'${PWD}\n${OLDPWD}' | envsubst | dos2unix); do
             :;  echo ""${x}"" | xxd;
             done"
    

    【讨论】:

      【解决方案3】:

      使用双引号

      build_script:
      - cmd: "
          @echo off
          FOR %%P IN (x86,x64) DO (
            FOR %%C IN (Debug,Release) DO (
              msbuild
                /p:Configuration=%%C
                /p:Platform=%%P ...
              || EXIT 1
            )
          )"
      

      您可以检查 yaml ref,更准确地说是 this example

      我在使用appveyor 时遇到了同样的问题(多么奇怪的限制!)。使用双引号让我可以利用 YAML 的微妙之处:

      • 保持我这边易于读/写的多行表达式,
      • 具有有效存储应用程序的单行值。

      您可以看看where I use it,看看build passes 的效果如何。

      对于其他读者,请注意,行将被折叠,因此需要使用特殊的语法来支持...你不能省略@例如 OP 给出的表达式中的 987654326@。

      【讨论】:

        【解决方案4】:

        CMD 命令总是被分成单独的行并通过包装到.cmd 文件中一个接一个地运行。把你的代码放到build.cmd,提交到repo,然后调用:

        build_script:
        - build.cmd
        

        【讨论】:

        • 所以没有办法将命令放在appveyor.yml 中的单独行上,而是将它们解包以便在 Appveyor 中的一行上执行?我更愿意将所有与 Appveyor 相关的内容保存在一个文件中。
        • 不,抱歉。目前,唯一的方法是将该代码放入批处理文件中。
        • 所以每一行都放在一个单独的.cmd文件中?
        • 可以执行多行 PowerShell 脚本(使用 - ps: |),因此您可以将 CMD 脚本转换为 PS 并改用它。
        【解决方案5】:

        如果您知道 appveyor 的期望(我不知道),我们假设:

        @echo off
        FOR %%P IN (x86,x64) DO (
            FOR %%C IN (Debug,Release) DO ( msbuild /p:Configuration=%%C /p:Platform=%%P ... || EXIT 1 )
        )
        

        然后通过转储它很容易生成适当的 YAML,例如来自 Python:

        import sys
        import ruamel.yaml
        
        appveyor_str = """\
        @echo off
        FOR %%P IN (x86,x64) DO (
            FOR %%C IN (Debug,Release) DO ( msbuild /p:Configuration=%%C /p:Platform=%%P ... || EXIT 1 )
        )
        """
        
        data = dict(build_script=[dict(cmd=appveyor_str)])
        
        ruamel.yaml.round_trip_dump(data, sys.stdout)
        

        给你:

        build_script:
        - cmd: "@echo off\nFOR %%P IN (x86,x64) DO (\n    FOR %%C IN (Debug,Release) DO (\
            \ msbuild /p:Configuration=%%C /p:Platform=%%P ... || EXIT 1 )\n)\n"
        

        (上例中的任何换行符之前都没有空格)

        使用folded block style scalar(与>)几乎不能像您所经历的那样控制标量的折叠。在折叠(或文字)块样式标量中转义序列也是不可能的。

        如果您的多行字符串不需要转义,您可以尝试转储为块样式标量:

        import sys
        import ruamel.yaml
        
        appveyor_str = """\
        @echo off
        FOR %%P IN (x86,x64) DO (
            FOR %%C IN (Debug,Release) DO ( msbuild /p:Configuration=%%C /p:Platform=%%P ... || EXIT 1 )
        )
        """
        
        data = dict(build_script=[dict(cmd=ruamel.yaml.scalarstring.PreservedScalarString(appveyor_str))])
        
        ruamel.yaml.round_trip_dump(data, sys.stdout)
        

        给出:

        build_script:
        - cmd: |
            @echo off
            FOR %%P IN (x86,x64) DO (
                FOR %%C IN (Debug,Release) DO ( msbuild /p:Configuration=%%C /p:Platform=%%P ... || EXIT 1 )
            )
        

        (即您输入的内容)

        如果你正确对齐所有内容,这并不像你希望的那样可读,并在第一个换行符后加双换行符,你会得到所需的输出:

        import sys
        import ruamel.yaml
        
        yaml_str = """\
        build_script:
        - cmd: >-
            @echo off
        
            FOR %%P IN (x86,x64) DO (
            FOR %%C IN (Debug,Release) DO (
            msbuild
            /p:Configuration=%%C
            /p:Platform=%%P ...
            || EXIT 1
            )
            )
        """
        
        data = ruamel.yaml.load(yaml_str)
        
        print(data['build_script'][0]['cmd'])
        

        给予:

        @echo off
        FOR %%P IN (x86,x64) DO ( FOR %%C IN (Debug,Release) DO ( msbuild /p:Configuration=%%C /p:Platform=%%P ... || EXIT 1 ) )
        

        但你不能缩进(从折叠块样式标量的细节):

        以空白字符开头的行(更多缩进的行)不被折叠。

        【讨论】:

        • 不幸的是,您的输出仍然主要将所有内容都塞到同一行,这是我试图避免的,因为它难以阅读。我可以在.yml 文件中手动将所有内容放在一行中,但它一点也不容易阅读,所以我试图弄清楚如何在.yml 文件中添加真正的换行符。 Appveyor 期望所有内容都在同一行(换行符终止命令)。
        • @Malvineous 我用如何将你的 appveyor 字符串转储为文字块样式标量更新了我的答案。如果这对您不起作用,您能否更新您的问题以包含字符串,因为它应该进入 appveyor(尤其是换行符和前导/尾随空格)
        • 感谢您的更新。我已经更新了问题以显示我所追求的 - 问题是从 FOR 到最后一个 ) 在读取 YAML 后必须全部出现在同一行,但在我想要的 .yml 文件中他们在不同的行。
        • 我不认为你可以通过缩进来获得它,这会混淆展开。如果 appveyor 剥夺了您的 ^,您将不得不坚持使用左对齐代码(请参阅我的更新)。
        猜你喜欢
        • 2016-05-20
        • 2011-02-06
        • 2023-01-15
        • 2020-05-14
        • 1970-01-01
        • 1970-01-01
        • 2013-09-07
        • 2021-03-01
        • 2023-03-21
        相关资源
        最近更新 更多