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
、-
、handle
、null
然后被视为单独的参数。
在 ksh93、zsh 或 bash 中,您可以执行以下操作:
\nset -- "${@/#--description=*/--description=$NEW_DESCRIPTION}"\n
Run Code Online (Sandbox Code Playgroud)\n#
要将开始位置参数(将模式锚定在开头)替换为--description=
with --description=<contents-of-NEW_DESCRIPTION-variable
。
使用ksh93
,可以缩短为:
set -- "${@/#@(--description=)*/\\1$NEW_DESCRIPTION}"\n
Run Code Online (Sandbox Code Playgroud)\n相当于zsh -o extendedglob
:
set -- "${@/#(#b)(--description=)*/$match[1]$NEW_DESCRIPTION}"\n
Run Code Online (Sandbox Code Playgroud)\n但也许你也可以这样做:
\nset -- "$@" "--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
,您可以执行以下操作:
argv[(i)--description=*]=--description=$NEW_DESCRIPTION\n
Run Code Online (Sandbox Code Playgroud)\n替换以 开头的第一个参数--description=
,--description=<contents-of-NEW_DESCRIPTION-variable
或者如果没有找到,则将其添加为新参数。
或者:
\nargv[(I)--description=*]=--description=$NEW_DESCRIPTION\n
Run Code Online (Sandbox Code Playgroud)\n相同,只是它是被替换的最后一个匹配项,并且如果找不到则将其插入到开头。
\n也可以用多个参数替换一个参数:
\nargv[(i)--description=*]=(--description=$NEW_DESCRIPTION --other-args)\n
Run Code Online (Sandbox Code Playgroud)\n或者将--description
参数及其后续参数替换为--description=$NEW_DESCRIPTION
:
argv[n=argv[(i)--description],n+1]=--description=$NEW_DESCRIPTION\n
Run Code Online (Sandbox Code Playgroud)\n--description
(再次,在元素中找不到if 在末尾添加)。
要删除所有以 1 开头--description=
并在末尾添加 1 的参数:
set -- "${@:#--description=*}" --description=$NEW_DESCRIPTION\n
Run Code Online (Sandbox Code Playgroud)\n在bash
4.4+ 中,对参数进行一些转换的另一种选择是诉诸perl
,将位置参数作为参数传递,并将它们作为 NUL 分隔列表读回(因为bash
变量无论如何都不能包含 NUL):
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)\nsed
比您必须对 SEARCH 和 REPLACE 进行一些转义更合适。
\xc2\xb9 除外,例如比某些实用程序--quiet --quiet
更安静的累积项--quiet
,或用于指定多个输出字段的-o pid -o ppid
of 项ps
。在某些情况下,顺序很重要。例如,更改--description=foo --no-description
为--description=bar --no-description
可能不会与更改为 执行相同的操作--description=foo --no-description --description=bar
。
您的代码中存在一些问题。其中之一是使用$*
不带引号的,这将导致 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)