如何在 bash 脚本文件中参数化我的命令?

如何在 bash 脚本文件中参数化我的命令?

编辑——不使用 bash$1变量。示例是使用字段指示符:$1$2$3awk输入记录字段不幸的是我太快地完成了示例并且混淆了示例。

我相信这是一个普遍问题。我正在使用它awk作为演示工具。目的是将 ( awk) 脚本参数暗示到awk(或任何工具)命令中。

我们使用从 STDIN 到 as 提供的字段来awk生成结果。标识符$1, $2,$3awkSTDIN 流中的字段。

所需的解决方案 A(首选概念),例如......

#!/bin/bash  -f
#   script: blue.sh
#
    awk -e '{ print $1 " and " $2 " silly example" ;}'
#

以及所需的输出:

$  echo  "red blue yellow" > ./blue.sh 
red and blue silly example
$

或许可以考虑替代方案 B:

#!/bin/bash  -f
#   script: green.sh
#
    wrk="awk -e '{ print \$3 \" and \" \$2 \" variable example\" ;}'  "
#
    echo "work is:"
    echo $wrk
    echo
    $wrk

以及所需的输出:

$  echo  "red blue yellow" > ./green.sh 
wrk is:
awk -e '{ print $3 " and " $2 " variable example" ;}'  

yellow and blue variable example
$

问题出在-e表达式的引用上。

有没有办法实现这些例子?

答案1

这可能是“我们如何运行存储在变量中的命令?”,但我会针对您的具体情况回答它,因为它有多个部分:

  1. 将命令存储在变量中并使用该变量运行它。
  2. 将数据传递到awk.

“变量中的运行命令”位

以下是脚本的替代实现的建议:

#!/bin/bash

wrk=( awk -v a="$3" -v b="$2"
      'BEGIN { printf "%s and %s variable example\n", a, b }' )

awk -v a="$1" -v b="$2" 'BEGIN { printf "%s and %s silly example\n", a, b }'

printf 'wrk is: %s\n' "${wrk[*]}"
"${wrk[@]}"

运行这个:

$ ./blue.sh "red" "blue" "yellow"
red and blue silly example
wrk is: awk -v a=yellow -v b=blue BEGIN { printf "%s and %s variable example\n", a, b }
yellow and blue variable example

我所做的是将awk命令存储在大批而不是在字符串中。这确保了数组的每个单独的元素都可以被正确引用,同时仍然保持为单独的元素,与数组的其他部分分开。例如,awk开头的单词是与数组中最后一个条目(整个awk代码)分开的一个单词。

用作命令时"${wrk[@]}",数组的每个元素都用作单独的命令行参数(第一个元素是命令)。扩展的引用"${wrk[@]}"确保每个元素都被单独引用(使用${wrk[@]}将不起作用)。

这就是在变量中存储和运行命令的方式,即使用数组并确保正确引用数组元素和数组的扩展。

对于printfshell 脚本中的调用,我使用"${wrk[*]}"从数组生成单个字符串wrk(默认情况下所有元素均以空格分隔)。您会注意到,这不会保留对各个元素的任何引用,这是预期的,因为 shell 在创建数组时删除了这些元素(就像在执行类似操作时删除引号一样a='hello world')。

要查看 的每个单独元素wrk,请使用类似的内容printf 'element: "%s"\n' "${wrk[@]}"。这将输出

element: "awk"
element: "-v"
element: "a=yellow"
element: "-v"
element: "b=blue"
element: "BEGIN { printf "%s and %s variable example\n", a, b }"

在这种特殊情况下,根据您的需要,您还可以使用 shell 函数:

#!/bin/bash

wrk () {
    awk -v a="$1" -v b="$2" 'BEGIN { printf "%s and %s variable example\n", a, b }'
}


awk -v a="$1" -v b="$2" 'BEGIN { printf "%s and %s silly example\n", a, b }'

wrk "$3" "$2"

请注意,函数中的$1and$2指的是函数的第一个和第二个参数,而不是脚本的。

运行这个:

$ ./blue.sh "red" "blue" "yellow"
red and blue silly example
yellow and blue variable example

-特定awk

为了将数据传递到awk 我使用的命令行上-v 初始化awk变量awk。这比通过 shell 扩展将变量注入awk代码更好,因为这样做可能允许用户注入可执行代码而不仅仅是数据(例如,通过简单地$3包含 a;和一些代码)。awk看 ”awk 中的命令行参数“ 和这个网站上有类似的问题了解更多相关信息。

您还可以awk使用环境变量传递数据:

a=$3 b=$2 awk 'BEGIN { printf "%s and %s variable example\n", ENVIRON["a"], ENVIRON["b"] }' )

或者,

export a="$3"
export b="$2"
awk 'BEGIN { printf "%s and %s variable example\n", ENVIRON["a"], ENVIRON["b"] }' )

我还在awk单个BEGIN块中执行代码,因为标准输入上没有可读取的输入。

答案2

我找到了一个解决方案,尽管结果与最初的设想有很大不同。我无法找到一种方法来扩展bash命令中的 -script 变量,因此必须“构造”它。事实证明,xargs由于与 shell 想要如何做它的事情有关的几个原因,这是不合适的。

我将介绍一个运行示例,然后介绍代码并描述发生的情况/情况。我用诸如“# (2)”之类的标签标记了要讨论的区域。

例子

输入...

$ svn status -qu ..
M            28637   /src/line.h
M            28637   /src/line.cpp
        *    28637   /testing/CheckWindows.py

加工...

重复脚本有两个参数:

  1. 要在输入列表上执行的模板或一个或多个命令
  2. 一些awk过滤输入流的命令

命令行:

 svn status -qu .. | repeat                                        \
                       'cp -v $ff  /mnt/testhost/testarea/r02/$ff' \
                       "{  if( $1 == "M" ){ print  $3; } }"

输出...

'/src/line.h' -> '/mnt/testhost/testarea/r02/src/line.h'
'/src/line.cpp' -> '/mnt/testhost/testarea/r02/src/line.cpp'

发生了什么

  • 仅将本地修改的文件复制到r02/testarea
    • 显然过滤可以是任何东西
  • 的目的repeat是在输入的过滤列表上重复一组命令

带注释的代码

    #!/bin/bash
    #   repeat.bash

    function  repeat
    {
        local -a    actions=()
        local       DELIMITER=";"
        local       filter="${2}"
        local       cmd=""

        local       wrk="${1}${DELIMITER}"        # (1)   Template
        local       str=""


        while [[ $wrk  ]]                         # (2)   Split-up individual commands
        do
            str="${wrk%%"${DELIMITER}"*}"
            actions+=( "${str}" )

            wrk="${wrk#*"${DELIMITER}"}"
        done

        echo    "actions: "                       # (3)   Debugging code

        for action in "${actions[@]}"
        do
            echo  "        => \"${action}\"."
        done
        echo  "filter  => '${filter}'."


        ex="${filter}"                            # (4)   Awk instructions to 
                                                  #       process input stream


        for ff in $(awk -e "${ex}")               # (5)   .
        do
            for action in "${actions[@]}"         # (6)   .
            do
                cmd="${action//\$\{ff\}/${ff}}"   # (7)   .
                cmd="${cmd//\$ff/${ff}}"

                ${cmd}                            # (8)   .
            done
        done

        return

    }

    repeat  "${@:1}"                              # (9)   .

笔记

  1. Arg$1是命令模板。
    *
    末尾添加额外的分隔符以供稍后解析
  2. $1通常是多个命令。将输入参数拆分为单独的分号分隔的命令字符串。
  3. 打印参数进行确认。
  4. 将过滤器加载到ex变量中。
    *
    在此示例中ex,仅将过滤器作为 传递$2
  5. 循环过滤后的输入行生成的每个字符串。
    *
    ex成为awk作为选择/生成过滤器执行的命令。
  6. 循环所有action命令模板。
  7. 扩展$ff命令字符串${ff}中的语法action
  8. 为每个文件执行每个生成的命令行:$ff.
  9. 调用该函数来执行脚本。

当它变成撅嘴时,这变得非常不同,因为不可能直接将参数注入到awk或 模板字符串中。然而,解决方法并不太不方便,并且展示了它是如何在没有神秘扩展等的情况下工作的。

我相信这个想法是有用的。现在我有了一个工作片段,我发现它非常方便。

相关内容