无法通过 Bash 变量将带引号的标志传递给可执行文件

Unable to pass flags with quotation marks via Bash variables to an executable

我正在尝试在 Bash 中创建一个脚本来调用带有大量参数的可执行文件。为了提高可读性,我将不同的参数集分组为变量,并将这些变量传递给可执行文件。

我遇到的问题是,当我通过变量将标志传递给可执行文件时,Bash 似乎无法正确评估标志,而不是直接传递它们。如果我愿意,我希望能够通过变量传递任何类型的标志,即使它需要引号。

直接传递参数

我发现如果我这样做,脚本就会运行并且我得到了预期的输出:

$LINT_EXECUTABLE \
    $SYSTEM_INCLUDES \
    $RTE_INCLUDES \
    $SRC_INCLUDES \
    $LINT_CONFIG_INCLUDES \
    $OBJECT_INCLUDES \
    $INDIRECT_FILES \
    -format="*** LINT: %(%f(%l) %)%t %n: %m" \
    src/**/*.c

输出

--- Module:   src\c\error.c (C)
        Including file src/include\error.h (hdr)
*** LINT: src/include\error.h(8) note 9071: defined macro '__ERROR_H' matches a
    pattern reserved to the compiler [MISRA 2012 Rule 21.1, required]
#define __ERROR_H

// ...

通过变量传递参数

但是,如果我这样做,我最终会得到不正确的输出:

OPTIONS="-format=\"*** LINT: %(%f(%l) %)%t %n: %m\""

$LINT_EXECUTABLE \
    $SYSTEM_INCLUDES \
    $RTE_INCLUDES \
    $SRC_INCLUDES \
    $LINT_CONFIG_INCLUDES \
    $OBJECT_INCLUDES \
    $INDIRECT_FILES \
    $OPTIONS \
    src/**/*.c

输出

$ ./lint.sh
PC-lint Plus 1.1 TRIAL for Windows, Copyright Gimpel Software LLC 1985-2018
LICENSED FOR EVALUATION USE ONLY
evaluation license expires in 17 days
"***
LINT:
^

我试过的

我尝试用单引号替换 Bash 变量中的转义双引号,但这没有任何区别。我还临时将 LINT_EXECUTABLE 设置为 echo 以在 运行 $ ./my-script.sh.

时将评估的参数集打印到命令行

在直接传递标志的情况下,我看到它输出 ... -format=*** LINT: %(%f(%l) %)%t %n: %m(即 -format 值周围没有引号)但是如果我通过变量传递标志,我看到 ... -format="*** LINT: %(%f(%l) %)%t %n: %m" ... 代替。

环境

我 运行 在 Windows 64 位机器上使用 Cygwin 中可用的最新版本 bash:

$ bash --version
GNU bash, version 4.4.12(3)-release (x86_64-unknown-cygwin)

编辑

需要说明的是,OPTIONS 不仅仅包含 -format 标志。现在我将其定义为:

OPTIONS="
    +libh(co-arm_TLE9844_AppKit.h) \
    -header(co-arm_TLE9844_AppKit.h) \
    -wlib(4) \
    -wlib(1) \
    +libdir(C:/Keil_v5/ARM/ARMCC/include) \
    -hsfb^3 \
    -format=\"*** LINT: %(%f(%l) %)%t %n: %m\" \
    -width(160,4)"

为了回应将变量引用为“$OPTIONS”的建议,我尝试了这个,但它给了我一个不同的(但仍然不正确)输出:

$LINT_EXECUTABLE \
    $SYSTEM_INCLUDES \
    $RTE_INCLUDES \
    $SRC_INCLUDES \
    $LINT_CONFIG_INCLUDES \
    $OBJECT_INCLUDES \
    $INDIRECT_FILES \
    "$OPTIONS" \
    src/**/*.c

这是将传递给执行此操作的可执行文件的评估参数:

$ ./lint.sh
-iC:/Keil_v5/UV4/Lint -iC:/Keil_v5/ARM/ARMCC/include -iC:/Keil_v5/ARM/PACK/ARM/CMSIS/5.3.0/CMSIS/Include -iC:/Keil_v5/ARM/PACK/Infineon/TLE984x_DFP/1.1.1/Device/Include -i./RTE/Device/TLE9844-2QX -i./RTE/_TLE9844_AppKit -i./src/include -i./src/include/drivers -i./src/include/utils -i./config/linting -i./Objects ./config/linting/co-ARMCC-5.lnt ./config/linting/std.lnt
    +libh(co-arm_TLE9844_AppKit.h)     -header(co-arm_TLE9844_AppKit.h)     -wlib(4)     -wlib(1)     +libdir(C:/Keil_v5/ARM/ARMCC/include)     -hsfb^3     -format="*** LINT: %(%f(%l) %)%t %n: %m"     -width(160,4) src/c/error.c src/c/main.c ... (other files)

我按照建议使用了 shellcheck.net,并按照建议将所有变量用引号引起来。这是我的脚本的最新版本:

#!/bin/bash

LINT_EXECUTABLE="pclp64"

SYSTEM_INCLUDES="
    -iC:/Keil_v5/UV4/Lint \
    -iC:/Keil_v5/ARM/ARMCC/include \
    -iC:/Keil_v5/ARM/PACK/ARM/CMSIS/5.3.0/CMSIS/Include \
    -iC:/Keil_v5/ARM/PACK/Infineon/TLE984x_DFP/1.1.1/Device/Include"

RTE_INCLUDES="
    -i./RTE/Device/TLE9844-2QX \
    -i./RTE/_TLE9844_AppKit"

SRC_INCLUDES="
    -i./src/include \
    -i./src/include/drivers \
    -i./src/include/utils"

LINT_CONFIG_INCLUDES="
    -i./config/linting"

OBJECT_INCLUDES="
    -i./Objects"

INDIRECT_FILES="
    ./config/linting/co-ARMCC-5.lnt \
    ./config/linting/std.lnt"

OPTIONS="
    +libh(co-arm_TLE9844_AppKit.h) \
    -header(co-arm_TLE9844_AppKit.h) \
    -wlib(4) \
    -wlib(1) \
    +libdir(C:/Keil_v5/ARM/ARMCC/include) \
    -hsfb^3 \
    -format=\"*** LINT: %(%f(%l) %)%t %n: %m\" \
    -width(160,4)"

$LINT_EXECUTABLE \
    "$SYSTEM_INCLUDES" \
    "$RTE_INCLUDES" \
    "$SRC_INCLUDES" \
    "$LINT_CONFIG_INCLUDES" \
    "$OBJECT_INCLUDES" \
    "$INDIRECT_FILES" \
    "$OPTIONS" \
    src/**/*.c

Shellcheck.net 报告此脚本现在没有问题,但是当我尝试执行脚本时脚本失败了很多:

$ ./lint.sh
PC-lint Plus 1.1 TRIAL for Windows, Copyright Gimpel Software LLC 1985-2018
LICENSED FOR EVALUATION USE ONLY
evaluation license expires in 17 days
<command line>  2  error 305: unable to open module '-iC:\Keil_v5\UV4\Lint
    -iC:\Keil_v5\ARM\ARMCC\include
    -iC:\Keil_v5\ARM\PACK\ARM\CMSIS.3.0\CMSIS\Include
    -iC:\Keil_v5\ARM\PACK\Infineon\TLE984x_DFP.1.1\Device\Include'

问题的根源似乎是您需要一种方法来将每个参数维护为一个单独的词,即使该参数包含 spaces/quotes。如果引用变量不能解决问题,将 args 存储在数组中并扩展它会。

所以我们动态创建一个命令,将每个参数放在数组的一个单独元素中,

lint_executable=(
                  pclp64
                )

system_includes=( 
                  '-iC:/Keil_v5/UV4/Lint' 
                  '-iC:/Keil_v5/ARM/ARMCC/include'
                  '-iC:/Keil_v5/ARM/PACK/ARM/CMSIS/5.3.0/CMSIS/Include'
                  '-iC:/Keil_v5/ARM/PACK/Infineon/TLE984x_DFP/1.1.1/Device/Include'
                )

rte_includes=(
                '-i./RTE/Device/TLE9844-2QX'
                '-i./RTE/_TLE9844_AppKit'
             )

src_includes=(
                '-i./src/include'
                '-i./src/include/drivers'
                '-i./src/include/utils'
             )

lint_config_includes=(
                       '-i./config/linting'
                     )   

object_includes=(
                  '-i./Objects'
                )

indirect_files=(
                  './config/linting/co-ARMCC-5.lnt'
                  './config/linting/std.lnt'
               )

options=(
          '+libh(co-arm_TLE9844_AppKit.h)'
          '-header(co-arm_TLE9844_AppKit.h)'
          '-wlib(4)'
          '-wlib(1)'
          '+libdir(C:/Keil_v5/ARM/ARMCC/include)'
          '-hsfb^3'
          '-format="*** LINT: %(%f(%l) %)%t %n: %m"'
          '-width(160,4)'
        )

现在已经打包了数组,使用适当的引用扩展调用命令行

"${lint_executable[@]}" \
    "${system_includes[@]}" \
    "${rte_includes[@]}" \
    "${src_includes[@]}" \
    "${lint_config_includes[@]}" \
    "${object_includes[@]}" \
    "${indirect_files[@]}" \
    "${options[@]}" \
    src/**/*.c

请注意,单个单词元素不必打包在数组中,为了统一,我已经在上面的例子中进行了演示。还要注意用户定义 variables/arrays 使用小写名称的方式。它更像是一种推荐的 shell 脚本编写实践。这个想法是因为 shell 维护自己的一组大写环境变量,使用小写名称将它们分开区分。