Shell

在保留空格的同時替換命令行參數

  • August 22, 2022

我想有選擇地替換正在傳遞的命令行參數,以便為正在執行的下游 cmd 自動格式化它。論點將有空格,這就是爭論的焦點。

我目前正在這樣做:

set -- $(echo $* | sed -e "s/$_ARG/--description=\"$_ID - $_SUMMARY\"/")

新的論點,--description="$_ID - $_SUMMARY"被分裂了。

我執行下游 cmd:

<cmd> "$@"

編輯:

我可能有任意數量的參數,但一個範例案例是:

activity --description='handle null'

至:

activity --description='$SOME_VARIABLE - handle null'

最終,當我使用“$@”執行下游命令時,它已經在那里拆分了,所以它不能按我的意願工作。它最終像

activity --description=value - handle null

--description=value, -, handle, 和nullthen 被認為是單獨的參數。

您的程式碼中有一些問題。其中之一是使用不帶$*引號的,這將導致 shell 將原始參數拆分為任何字元$IFS(預設情況下是空格、製表符、換行符)上的單詞,並對生成的單詞應用文件名萬用字元。如果您想要支持包含空格、製表符或換行符的多個參數,引用$*as"$*"也不是您想要的,因為這將是一個字元串。切換到 using"$@"將無濟於事,因為echo只會產生一個 each 參數,其間有空格以供sed閱讀。

echo可以對任何包含反斜杠序列的字元串進行特殊處理,例如\nand \t,具體取決於 shell 及其目前設置。在某些 shell 中,echo -n可能不會輸出-n(可能還有其他有問題的字元串,例如-e)。

如果您願意將sed其視為文本(參數可能是多行字元串),則用於修改參數可能會在單個參數上起作用,但在這種情況下,您正在一次對所有參數應用一些編輯腳本,這可能失火。

但是,拆分結果字元串的是不引用與 . 一起使用的命令替換set。這將重新拆分結果sed並再次對結果應用文件名萬用字元。

您將需要解析您打算修改的命令行選項。簡而言之,循環遍歷參數,並修改您要修改的參數。

以下腳本在long 選項的每個實例的選項參數的開頭sh添加字元串。如果 long 選項後面緊跟一個空格,如,則在將其修改為 final 之前,將用 a 重寫,就好像腳本已用 呼叫一樣。hello - --description``--description "my thing"``=``--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' "$@"

${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>

如果您總是期望有 long 選項及其由=字元分隔的選項參數,則程式碼可能會大大簡化:

#!/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' "$@"

使用與上述相同的參數進行測試執行(--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>

上面較短的第二個腳本的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' "$@"

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

set -- "${@/#--description=*/--description=$NEW_DESCRIPTION}"

用with替換起始位置參數(#在開始處錨定模式)。--description=``--description=<contents-of-NEW_DESCRIPTION-variable

ksh93可以縮短為:

set -- "${@/#@(--description=)*/\1$NEW_DESCRIPTION}"

等價於zsh -o extendedglob

set -- "${@/#(#b)(--description=)*/$match[1]$NEW_DESCRIPTION}"

但也許你也可以這樣做:

set -- "$@" "--description=$NEW_DESCRIPTION"

大多數實用程序都接受多次採用相同的選項,並且這是最後一次優先發生¹。例如:

$ echo x | grep -H --label=foo --label=bar .
bar:x

zsh中,您可以執行以下操作:

argv[(i)--description=*]=--description=$NEW_DESCRIPTION

替換以 開頭的第一個參數--description=--description=<contents-of-NEW_DESCRIPTION-variable或者如果沒有找到,則將其作為新參數添加到末尾。

或者:

argv[(I)--description=*]=--description=$NEW_DESCRIPTION

相同,除了它是最後一個被替換的匹配項,如果找不到,它會在開頭插入。

也可以用多個參數替換一個參數:

argv[(i)--description=]=(--description=$NEW_DESCRIPTION --other-args)

或者用--description以下參數替換一個參數--description=$NEW_DESCRIPTION

argv[n=argv[(i)--description],n+1]=--description=$NEW_DESCRIPTION

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

要刪除以開頭並在末尾添加一個的所有參數:--description=

set -- "${@:#--description=*}" --description=$NEW_DESCRIPTION

bash4.4+ 中,對參數進行一些轉換的另一個選擇是訴諸perl,將位置參數作為參數傳遞並將它們作為 NUL 分隔列表讀回(因為bash變數無論如何都不能包含 NUL):

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

sed你必須為 SEARCH 和 REPLACE 進行一些轉義更合適。


¹ 除了那些--quiet --quiet--quiet某些實用程序更安靜的累積值,或者-o pid -o ppid指定ps多個輸出欄位的 。在某些情況下,順序很重要。例如,更改--description=foo --no-description--description=bar --no-description可能與更改為 不同--description=foo --no-description --description=bar

引用自:https://unix.stackexchange.com/questions/714371