
実行中のダウンストリームコマンドに合わせて自動的にフォーマットするために渡されるコマンドライン引数をオプションで置き換えたいと思います。論争には空白があり、それが議論の内容です。
私は現在これをやっています:
set -- $(echo $* | sed -e "s/$_ARG/--description=\"$_ID - $_SUMMARY\"/")
新しい引数が--description="$_ID - $_SUMMARY"
分割されます。
ダウンストリームコマンドを実行します。
<cmd> "$@"
パラメータはいくらでも持つことができますが、サンプルのユースケースは次のとおりです。
~から
activity --description='handle null'
到着する:
activity --description='$SOME_VARIABLE - handle null'
結局のところ、ダウンストリームコマンドを実行すると、「$ @」を使用してもすでに分割されており、期待どおりに機能しません。それは結局次のようになる
activity --description=value - handle null
--description=value
、、、、-
は別々のパラメータとして扱われますhandle
。null
答え1
ksh93、zsh、またはbashでは、次のことができます。
set -- "${@/#--description=*/--description=$NEW_DESCRIPTION}"
開始位置パラメータ(#
先頭にパターンを固定する)--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
置換される最後の一致と呼ばれる点と一致しない場合は、最初に挿入される点以外は同じです。
1つのパラメータを複数のパラメータに置き換えることもできます。
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=
、最後に1つを追加します。
set -- "${@:#--description=*}" --description=$NEW_DESCRIPTION
4.4+でbash
パラメータを一部変換するもう1つのオプションは、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をいくつかのエスケープ処理よりも優れています。
1 例外は累積されます。たとえば、いくつかのユーティリティよりも--quiet --quiet
静かな、または複数の出力フィールドを指定する--quiet
ために使用されます。場合によっては、順序が重要です。たとえば、に変更すると、に変更するのと同じ操作が実行されない可能性があります。-o pid -o ppid
ps
--description=foo --no-description
--description=bar --no-description
--description=foo --no-description --description=bar
答え2
コードにはいくつかの問題があります。その1つは引用符なしで使用することです$*
。その後、シェルは生の引数を単語のすべての文字$IFS
(デフォルトでは空白、タブ、改行)に分割し、結果の単語にファイル名のグロービングを適用します。スペース、タブ、または改行文字を含む複数の引数をサポートしたい場合は、単一の文字列になるため、引用することも$*
望ましくありません。読みやすくするためにスペースを入れて各引数に1つだけを生成するので、"$*"
usingに切り替えるのは"$@"
役に立ちません。echo
sed
echo
バックスラッシュシーケンス(たとえば、および)を含む文字列は、シェルと現在の設定に従って\n
特別に処理できます。\t
一部のシェルにはecho -n
出力がない可能性があります-n
(たとえば、他の問題のある文字列がある可能性があります-e
)。
これをテキストとして処理したい場合(パラメータは複数行の文字列にすることができます)、修正パラメータをsed
使用すると単一のパラメータに対して機能できますが、この場合、すべてのパラメータに一度にいくつかの編集スクリプトを適用するため、誤って実行される可能性があります。あります。
ただし、結果の文字列を分割することは、コマンドで使用される引用符ではなく置換ですset
。これにより結果が再分割され、sed
ファイル名のグロービングが結果に再適用されます。
変更するコマンドラインオプションを解析する必要があります。つまり、パラメータを繰り返して、変更したいパラメータを変更します。
次のスクリプトは、長いオプションの各インスタンスのオプション引数の先頭にsh
文字列を追加します。長いオプションの後にスペースがある場合(たとえば)、呼び出しスクリプトを使用するのと同じように、スクリプトは最後に変更される前に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=
value からプレフィックス文字列を削除し、元の$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>
常に長いオプションと文字で区切られた対応するオプション引数が必要な場合は、=
コードを大幅に簡素化できます。
#!/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
上記と同じパラメータを使用してテストを実行します(2番目のインスタンスはパターンと一致しないため変更されません--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
[[ ... ]]
代わりに、シェルパターンマッチングを使用し、case ... esac
配列を使用してループ中に変更できるパラメータを保持する上記の短い2番目のスクリプトのバリアントです。
#!/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' "$@"
答え3
コマンド置換の出力は、シェルから単一の文字列として読み取られるバイトストリームです。単一のパラメーターの一部でなければならないスペースと、パラメーターを分離する必要があるスペースに関する情報はありません。
ただし、GNUツールセットとBashがあり、sedを使用して引数を処理したい場合は、引数(Bashおよび他のほとんどのシェルの変数と同様)はC文字列であり、NULバイトを含めることができないという事実を使用できます。 GNU sedはNULを行区切り文字として使用でき、BashはNULで終わる文字列のセットをreadarray
。
たとえば、次のようになります。
# test arguments
set -- activity --description='handle null'
SOME_VARIABLE="foo bar"
# prints args NUL-terminated, run through sed, read them in to the array 'new_args'
new_args=()
readarray -t -d '' new_args < <(printf "%s\0" "$@" | sed -ze "s/^--description=/&$SOME_VARIABLE - /")
# move the values from the array to the positional parameters $1, $2 ...
set -- "${new_args[@]}"
printf "%s\0" "$@"
末尾にNULがある位置引数を印刷すると、sedはNULを行区切り文字として使用し、sedコマンドはsed -z
inの内容を$SOME_VARIABLE
末尾に追加します。=
--description=...
これは$SOME_VARIABLE
sedコマンドに含まれているため、通常sedに特別なエントリ(/&\
改行文字など)がコマンドを中断することに注意することが重要です。また、printf
位置パラメータがなくても少なくとも1つの要素が印刷されるため、これが問題になる場合は、要素全体をif [[ "$#" -gt 0 ]]; then ...
。