将包含特殊字符的变量作为参数传递给 bash 脚本中的命令时出现额外单引号问题

问题描述 投票:0回答:1

首先感谢大家的帮忙!不幸的是,到目前为止还没有找到解决办法。我正在更新描述以使问题更容易重现。

  • Update1:使用 shellcheck.net 清除所有其他潜在问题。
  • Update2:描述我们在下面运行 cmake 的真实用例。

第 1 节:最小案例

首先发现

cmake
的问题,但可能不仅限于此,通过将以下最小可重现示例中的
cmake
更改为
make
或其他内容也可以重现问题。

#!/bin/bash

set -x

run() {
    arg=$1
    printf '%q\n' "$arg"
    cmake "$arg" 1>/dev/null 2>&1
}

TEXT="local -DCMAKE_ARG=\"arg1;arg2\""

CMAKE_OPTS1=$(echo "$TEXT" | awk '/local/ {for (i=1; i<=NF; i++) {if ($i ~ /^-D/) {printf "%s ", $i}}}' | tr -d '";')
CMAKE_OPTS2=$(echo "$TEXT" | awk '/local/ {for (i=1; i<=NF; i++) {if ($i ~ /^-D/) {printf "%s ", $i}}}' )
CMAKE_OPTS3=("$(echo "$TEXT" | awk '/local/ {for (i=1; i<=NF; i++) {if ($i ~ /^-D/) {printf "%s ", $i}}}' )")

run $CMAKE_OPTS1
run $CMAKE_OPTS2
run "${CMAKE_OPTS3[@]}"

打印出来的结果是(减少到兴趣线)

-DCMAKE_ARG=arg1arg2
+ cmake -DCMAKE_ARG=arg1arg2

-DCMAKE_ARG=\"arg1\;arg2\"
+ cmake '-DCMAKE_ARG="arg1;arg2"'

-DCMAKE_ARG=\"arg1\;arg2\"\
+ cmake '-DCMAKE_ARG="arg1;arg2" '

不幸的是,

cmake '-DCMAKE_ARG="arg1;arg2"'
无法正常工作。

  • CMAKE_OPTS1 仅供参考,在这种情况下,变量字符串中不存在特殊字符,并且它似乎按预期工作。
  • CMAKE_OPTS2 是我希望将包含特殊字符的变量作为参数传递给命令的真实情况。因此,单个标记似乎干扰了命令参数解析。
  • CMAKE_OPTS3 是我从这个论坛的类似问题中找到的,它也不起作用。

第二部分:原剧本

当然有很多修改以最小化脚本。因此,它涉及从 docker 和 GitHub 存储库中提取,都是公开的和开源的

首先,从 Docker Hub 中拉取 docker(以确保必要的 clang env):

docker pull rocm/miopen:ci

然后在docker中,使用以下脚本:

#!/bin/bash

set -x

build_miopen_ck() {
    echo "Building Composable Kernel"
    ck_commit=$1
    ck_cmake_opts=$2
    if [ -z "$ck_commit" ]; then
        echo "Composable Kernel entry was not found in requirements.txt"
        return
    fi
    mkdir -p /tmp/composable_kernel && cd /tmp/composable_kernel || exit
    wget -nv "https://www.github.com/ROCmSoftwarePlatform/composable_kernel/archive/${ck_commit}.tar.gz"
    tar -xzf "${ck_commit}.tar.gz"
    cd "composable_kernel-${ck_commit}" || exit
    rm -rf build
    mkdir -p build && cd build || exit
    cmake -DBUILD_DEV=OFF \
          -DCMAKE_BUILD_TYPE=Release \
          -DCMAKE_CXX_COMPILER="/opt/rocm/llvm/bin/clang++" \
          -DCMAKE_C_COMPILER="/opt/rocm/llvm/bin/clang" \
          -DCMAKE_PREFIX_PATH="$ROCM_PATH" \
          "${ck_cmake_opts}" \
          ..
    make -j"$(nproc)" install
    echo "Finished building Composable Kernel"
}

TEXT="ROCmSoftwarePlatform/composable_kernel@5f28614222bd590bc31d98838bc019e9c3a7ad45 -DGPU_TARGETS=\"gfx900;gfx906;gfx908;gfx90a;gfx1030;gfx1100;gfx1101;gfx1102\""

CK_COMMIT=$(echo "$TEXT" | awk '/composable_kernel/ {split($1, s, "@"); print s[2]}')
CK_CMAKE_OPTS=$(echo "$TEXT" | awk '/composable_kernel/ {for (i=1; i<=NF; i++) {if ($i ~ /^-D/) {printf "%s ", $i}}}')

build_miopen_ck "$CK_COMMIT" "$CK_CMAKE_OPTS"

很快(可能是一段时间)构建将失败并出现以下错误:

clang-15: error: invalid target ID 'gfx900 --offload-arch=gfx906 --offload-arch=gfx908 --offload-arch=gfx90a --offload-arch=gfx1030 --offload-arch=gfx1100 --offload-arch=gfx1101 --offload-arch=gfx1102'; format is a processor name followed by an optional colon-delimited list of features followed by an enable/disable sign (e.g., 'gfx908:sramecc+:xnack-')

通过诊断日志,单引号看起来很可疑:

cmake -DBUILD_DEV=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=/opt/rocm/llvm/bin/clang++ -DCMAKE_C_COMPILER=/opt/rocm/llvm/bin/clang -DCMAKE_PREFIX_PATH= '-DGPU_TARGETS="gfx900;gfx906;gfx908;gfx90a;gfx1030;gfx1100;gfx1101;gfx1102" ' ..

如果我转到目录,然后重新运行 cmake

cmake -DBUILD_DEV=OFF -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=/opt/rocm/llvm/bin/clang++ -DCMAKE_C_COMPILER=/opt/rocm/llvm/bin/clang -DCMAKE_PREFIX_PATH= -DGPU_TARGETS="gfx900;gfx906;gfx908;gfx90a;gfx1030;gfx1100;gfx1101;gfx1102"  ..

然后一切都按预期进行。

从上面的错误来看,应该和shell参数扩展有关:

  • 应该读作
    '--offload-arch=gfx900' '--offload-arch=gfx906' ...
but instead, the single quotes are messing up the **secondary** parsing process, and it reads as if it is:
    '--offload-arch=gfx900 --offload-arch=gfx906 ...'

与上面的最小示例相关,如果将包含特殊字符的变量作为参数传递给命令不会自动添加一对单引号,我们不应该有这个二次解析问题吗?

到目前为止的一些实验:

  • 不幸的是,根据以下一位用户的建议,删除
    set -x
    并不能解决此问题 :(
  • 也许上面的最小示例仍然是一个很好的起点?有没有办法避免添加单引号?它们何时添加以及如何删除?不幸的是,它不是字符串本身的一部分。

解决方法: 到目前为止,一个 working 解决方法是在 -DGPU_TARGETS=

 之后 grep 字符串 
,即代替
cmake "$arg"
我们可以进行以下工作
cmake -DGPU_TARGETS="${subset_arg}"
。但是,这是一种解决方法,原来的问题仍然存在。

bash shell arguments command-line-arguments single-quotes
1个回答
0
投票

您可能需要加倍反斜杠:

#!/bin/bash

set -x

run() {
    arg="$1"
    printf '%q\n' "$arg"
    cmake "$arg" 1>/dev/null 2>&1
}

TEXT="local -DCMAKE_ARG=arg1;arg2"

CMAKE_OPTS="${TEXT#local }" # Remove [local ]
CMAKE_OPTS="${CMAKE_OPTS//;/\\\\;}" # Add \ doubled
run "$CMAKE_OPTS"

# If four backslashes (\\\\) don't work, try two (\\)
© www.soinside.com 2019 - 2024. All rights reserved.