bash処理後のパラメータからオプションを削除する方法

bash処理後のパラメータからオプションを削除する方法

位置パラメータのリストを取得して繰り返し、パラメータのあるフラグとオプションを解析し、解析後にそれらを削除して、デフォルトのパラメータのみを残すスクリプトをどこで見たか覚えていますbash。その後、スクリプトによって処理されます。caseshift

たとえば、コマンドラインを解析するときは、cp -R file1 -t /mybackup file2 -fまずパラメータを繰り返し、ユーザーがディレクトリにドロップを要求したかどうかを識別し、ターゲットを指定して-R強制-t /mybackup的にコピーし、パラメータリストからそのパラメータを削除してプログラムを次のように-f残します。file1 file2処理する残りのパラメーター。

しかし、毎回見るスクリプトを覚えたり、把握できないようです。私はそうすることができるようにしたいです。私は様々なサイトをインターネットで検索し、私が確認した関連ページのリストを添付しました。

サイトの1つの質問は「順序に依存しないオプション」について具体的に質問しましたが、単一の回答や複製された質問に対する回答は、オプションが一般的な引数と混在する上記の状況を考慮しません。この人のために特別な言及理由順番に関係なくオプション。

bash組み込み関数はオプションではなく最初の引数で停止するようで、getopts解決策では十分ではないようです。これがWooledge BashFAQページ(以下を参照)でパラメータを並べ替える方法を説明する理由です。しかし、パラメータリストが非常に長い場合は、複数の配列を作成しないでください。

shiftパラメータリストの真ん中で単一のパラメータを表示することはサポートされていないので、私が望むことを達成する簡単な方法が何であるかわかりません。

まったく新しい配列を作成せずにパラメータリストの途中からパラメータを削除できるソリューションがあるかどうかを聞きたいです。

すでに見たページ:

答え1

POSIXly、オプションの構文解析は、最初の--非オプション(または非オプションパラメータ)引数の最初のものから停止する必要があります。だから

cp -R file1 -t /mybackup file2 -f

file1にあるので、cpすべてfile1-tおよびがディレクトリに/mybackup繰り返しコピーする必要があります。file2-f

ただし、GNU getopt(3)(オプションを解析するために使用されます(ここではGNU固有のオプションを使用するためcpGNUを使用します))は、環境変数が設定されていない限り、引数の後にオプションを受け入れます。したがって、実際にはPOSIXオプションスタイルの解析と同じです。cp-t$POSIXLY_CORRECT

cp -R -t /mybackup -f -- file1 file2

getoptsGNUシェル()でも、組み込みシェルはbashPOSIXバージョンのみを処理します。また、長いオプションやオプションのパラメータを持つオプションもサポートされていません。

GNUなどのオプションを解析するには、cpGNU APIを使用する必要がありますgetopt(3)。これを行うには、Linuxの場合は次のものを使用できますgetoptutil-linuxこのコマンドgetoptの拡張バージョンまた、他のUnicesに移植されました。FreeBSD)。

これにより、getoptオプションが正式に並べ替えられ、ループを介して簡単に解析できますwhile/case

$ getopt -n "$0" -o t:Rf -- -Rf file1 -t/mybackup file2
 -R -f -t '/mybackup' -- 'file1' 'file2'

通常、次のように使用します。

parsed_options=$(
  getopt -n "$0" -o t:Rf -- "$@"
) || exit
eval "set -- $parsed_options"
while [ "$#" -gt 0 ]; do
  case $1 in
    (-[Rf]) shift;;
    (-t) shift 2;;
    (--) shift; break;;
    (*) exit 1 # should never be reached.
  esac
done
echo "Now, the arguments are $*"

また、getoptGNUと同じ方法でオプションを解析することに注意してくださいcp。特にGNUと同様に、長いオプションをサポートし(短縮形式で入力)、$POSIXLY_CORRECT環境変数を尊重します(設定時に引数後のオプションのサポートが無効になります)cp

gdbを使用して受信したパラメータを印刷すると、getopt_long()パラメータを作成するのに役立ちますgetopt(1)

(gdb) bt
#0  getopt_long (argc=2, argv=0x7fffffffdae8, options=0x4171a6 "abdfHilLnprst:uvxPRS:T", long_options=0x417c20, opt_index=0x0) at getopt1.c:64
(gdb) set print pretty on
(gdb) p *long_options@40
$10 = {{
    name = 0x4171fb "archive",
    has_arg = 0,
    flag = 0x0,
    val = 97
  }, {
    name = 0x417203 "attributes-only",
[...]

getoptその後、次のように使用できます。

getopt -n cp -o abdfHilLnprst:uvxPRS:T -l archive... -- "$@"

cpGNUでサポートされているオプションのリストはバージョンごとに変更される可能性があり、オプションに正当な値を渡すことを確認するgetopt方法がないことに注意してください。--sparse

答え2

したがって、引数が処理されるたびに処理する必要がある引数リストの次の数字でシェル変数を設定し、ゼロ以外の値を返すことはgetopts期待しません。値を1に設定する$OPTINDと、POSIXは新しいパラメーターのリストが許可されることを指定します。したがって、これは単にリターンを観察し、失敗したリターンのインクリメントカウンタプラス記号を保存し、失敗したパラメータを削除し、失敗した各試行をリセットします。ループをカスタマイズするか、変数に保存し、その部分を 。$OPTINDgetoptsgetopts$OPTIND$OPTINDopts "$@"caseeval $case

opts() while getopts :Rt:f opt || {
             until command shift "$OPTIND" || return 0
                   args=$args' "${'"$((a=${a:-0}+$OPTIND))"'}"'
                   [ -n "${1#"${1#-}"}" ]
             do OPTIND=1; done 2>/dev/null; continue
};     do case "$opt"  in
             R) Rflag=1      ;;
             t) tflag=1      ;
                targ=$OPTARG ;;
             f) fflag=1      ;;
       esac; done

$args実行時に処理されなかったすべての引数に設定されますgetopts...だから...

set -- -R file1 -t /mybackup file2 -f
args= opts "$@"; eval "set -- $args"
printf %s\\n "$args"
printf %s\\n "$@"         
printf %s\\n "$Rflag" "$tflag" "$fflag" "$targ"

出力

 "${2}" "${5}"
file1
file2
1
1
1
/mybackup

これは、、、、、bash...に機能します。まあ、私はその時点で試みを放棄しました。各シェルにも含まれています。重要なのは、処理したくないパラメータのすべての数字が維持されることです。dashzshksh93mksh$[Rtf]flag$targgetopts

オプションスタイルを変更しても違いはありません。これは-Rf -t/mybackupまたは同様に機能します-R -f -t /mybackup。リストの中間、リストの終わり、またはリストの先頭で動作します。

それにもかかわらず、最良の方法パラメータリストの最後にforオプションを貼り付け、処理の--実行shift "$(($OPTIND-1))"が終わったら実行しますgetopts。この方法で処理されたすべてのパラメータを削除できます。そしてパラメータリストの終わりを維持します。

私が好きなものの1つは、長期オプションを短期オプションに切り替えることです。私はこれを非常に似た方法で行うので、この答えを簡単に得ることができます。今後私は走るgetopts

i=0
until [ "$((i=$i+1))" -gt "$#" ]
do case "$1"                   in
--Recursive) set -- "$@" "-R"  ;;
--file)      set -- "$@" "-f"  ;;
--target)    set -- "$@" "-t"  ;;
*)           set -- "$@" "$1"  ;;
esac; shift; done

関連情報