替换命令行参数同时保留空格

Wal*_*ter 3 shell options whitespace

我想有选择地替换正在传递的命令行参数,以自动格式化它以适应正在执行的下游命令。论证会有空格,这就是争论的焦点。

我目前正在这样做:

set -- $(echo $* | sed -e "s/$_ARG/--description=\"$_ID - $_SUMMARY\"/")
Run Code Online (Sandbox Code Playgroud)

新的论点--description="$_ID - $_SUMMARY"被分裂了。

我运行下游命令:

<cmd> "$@"
Run Code Online (Sandbox Code Playgroud)

我可能有任意数量的参数,但示例用例是:

activity --description='handle null'
Run Code Online (Sandbox Code Playgroud)

到:

activity --description='$SOME_VARIABLE - handle null'
Run Code Online (Sandbox Code Playgroud)

最终,当我运行下游命令时,即使使用“$@”,它也已经在那里分割,所以它不能按我的预期工作。它最终就像

activity --description=value - handle null
Run Code Online (Sandbox Code Playgroud)

--description=value-handlenull然后被视为单独的参数。

Sté*_*las 9

在 ksh93、zsh 或 bash 中,您可以执行以下操作:

\n
set -- "${@/#--description=*/--description=$NEW_DESCRIPTION}"\n
Run Code Online (Sandbox Code Playgroud)\n

#要将开始位置参数(将模式锚定在开头)替换为--description=with --description=<contents-of-NEW_DESCRIPTION-variable

\n

使用ksh93,可以缩短为:

\n
set -- "${@/#@(--description=)*/\\1$NEW_DESCRIPTION}"\n
Run Code Online (Sandbox Code Playgroud)\n

相当于zsh -o extendedglob

\n
set -- "${@/#(#b)(--description=)*/$match[1]$NEW_DESCRIPTION}"\n
Run Code Online (Sandbox Code Playgroud)\n

但也许你也可以这样做:

\n
set -- "$@" "--description=$NEW_DESCRIPTION"\n
Run Code Online (Sandbox Code Playgroud)\n

大多数实用程序接受多次使用相同的选项,最后一次出现的选项优先\xc2\xb9。例如:

\n
$ echo x | grep -H --label=foo --label=bar .\nbar:x\n
Run Code Online (Sandbox Code Playgroud)\n

在 中zsh,您可以执行以下操作:

\n
argv[(i)--description=*]=--description=$NEW_DESCRIPTION\n
Run Code Online (Sandbox Code Playgroud)\n

替换以 开头的第一个参数--description=--description=<contents-of-NEW_DESCRIPTION-variable或者如果没有找到,则将其添加为新参数。

\n

或者:

\n
argv[(I)--description=*]=--description=$NEW_DESCRIPTION\n
Run Code Online (Sandbox Code Playgroud)\n

相同,只是它是被替换的最后一个匹配项,并且如果找不到则将其插入到开头。

\n

也可以用多个参数替换一个参数:

\n
argv[(i)--description=*]=(--description=$NEW_DESCRIPTION --other-args)\n
Run Code Online (Sandbox Code Playgroud)\n

或者将--description参数及其后续参数替换为--description=$NEW_DESCRIPTION

\n
argv[n=argv[(i)--description],n+1]=--description=$NEW_DESCRIPTION\n
Run Code Online (Sandbox Code Playgroud)\n

--description(再次,在元素中找不到if 在末尾添加)。

\n

要删除所有以 1 开头--description=并在末尾添加 1 的参数:

\n
set -- "${@:#--description=*}" --description=$NEW_DESCRIPTION\n
Run Code Online (Sandbox Code Playgroud)\n

bash4.4+ 中,对参数进行一些转换的另一种选择是诉诸perl,将位置参数作为参数传递,并将它们作为 NUL 分隔列表读回(因为bash变量无论如何都不能包含 NUL):

\n
readarray -td '' newargs < <(\n  SEARCH="$_ARG" REPLACE='--description=something' perl -l0e '\n    for (@ARGV) {\n      s/\\Q$ENV{SEARCH}\\E/$ENV{REPLACE}/;\n      print;\n    }' -- "$@"\n)\nset -- "${newargs[@]}"\n
Run Code Online (Sandbox Code Playgroud)\n

sed比您必须对 SEARCH 和 REPLACE 进行一些转义更合适。

\n
\n

\xc2\xb9 除外,例如比某些实用程序--quiet --quiet更安静的累积项--quiet,或用于指定多个输出字段的-o pid -o ppidof 项ps。在某些情况下,顺序很重要。例如,更改--description=foo --no-description--description=bar --no-description可能不会与更改为 执行相同的操作--description=foo --no-description --description=bar

\n


Kus*_*nda 5

您的代码中存在一些问题。其中之一是使用$*不带引号的,这将导致 shell 将原始参数拆分为单词中的任何字符$IFS(默认情况下是空格、制表符、换行符),并对生成的单词应用文件名通配。如果您想要支持包含空格、制表符或换行符的多个参数,那么引用$*as也不是您想要的,因为这将是单个字符串。"$*"切换到 using"$@"不会有帮助,因为echo只会产生一个每个参数,中间有空格以供sed阅读。

echo\n可以对任何包含反斜杠序列(如和 )的字符串进行特殊处理\t,具体取决于 shell 及其当前设置。在某些 shell 中,echo -n可能不会输出-n(也可能存在其他有问题的字符串,例如-e)。

sed如果您愿意将其视为文本(参数可能是多行字符串),则使用修改参数可能适用于单个参数,但在这种情况下,您一次对所有参数应用一些编辑脚本,这可能失火。

然而,分割结果字符串的是与 一起使用的命令替换的非引用set。这会重新分割结果sed并再次对结果应用文件名通配。

您将需要解析要修改的命令行选项。简而言之,循环遍历参数,并修改您想要修改的参数。

以下sh脚本将字符串添加hello - 到长选项的每个实例的选项参数的开头--description。如果长选项后紧跟一个空格(如 中所示) ,则在将其修改为最终的 之前,--description "my thing"将使用 a 重写该脚本,=就好像使用 调用脚本一样。--description="my thing"--description="hello - my thing"

#!/bin/sh

SOME_VARIABLE=hello

skip=false

for arg do
    if "$skip"; then
        skip=false
        continue
    fi

    # Re-write separate option-argument with "=".
    # This consumes an extra argument, so need to skip
    # next iteration of the loop.
    case $arg in
        --description)
            arg=--description=$2
            shift
            skip=true
    esac

    # Add the value "$SOME_VARIABLE - " to the start of the
    # option-argument of the --description long option.
    case $arg in
        --description=*)
            arg=--description="$SOME_VARIABLE - ${arg#--description=}"
    esac

    # Put the (possibly modified) argument back at the end
    # of the list of arguments and shift off the first item.
    set -- "$@" "$arg"
    shift
done

# Print out the list of arguments as strings within "<...>":
printf '<%s>\n' "$@"
Run Code Online (Sandbox Code Playgroud)

${arg#--description=}--description=从 的值中删除前缀字符串$arg,留下原始选项参数字符串。

运行示例:

$ sh ./script -a -b --description="my thing" -c -d --description "your thing" -e
<-a>
<-b>
<--description=hello - my thing>
<-c>
<-d>
<--description=hello - your thing>
<-e>
Run Code Online (Sandbox Code Playgroud)

如果您总是期望有长选项及其由字符分隔的选项参数,则代码可能会显着简化=

#!/bin/sh

SOME_VARIABLE=hello

for arg do
    # Add the value "$SOME_VARIABLE - " to the start of the
    # option-argument of the --description long option.
    case $arg in
        --description=*)
            arg=--description="$SOME_VARIABLE - ${arg#--description=}"
    esac

    # Put the (possibly modified) argument back at the end
    # of the list of arguments and shift off the first item.
    set -- "$@" "$arg"
    shift
done

printf '<%s>\n' "$@"
Run Code Online (Sandbox Code Playgroud)

--description使用与上面相同的参数进行测试运行(不会修改 的第二个实例,因为它与模式不匹配--description=*):

$ sh ./script -a -b --description="my thing" -c -d --description "your thing" -e
<-a>
<-b>
<--description=hello - my thing>
<-c>
<-d>
<--description>
<your thing>
<-e>
Run Code Online (Sandbox Code Playgroud)

bash上面较短的第二个脚本的变体,使用 shell 模式匹配来代替[[ ... ]]case ... esac并使用数组来保存循环过程中可能修改的参数:

#!/bin/bash

SOME_VARIABLE=hello

args=()
for arg do
    if [[ $arg == --description=* ]]; then
        arg=--description="$SOME_VARIABLE - ${arg#--description=}"
    fi

    args+=( "$arg" )
done

set -- "${args[@]}"

printf '<%s>\n' "$@"
Run Code Online (Sandbox Code Playgroud)