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


9

bashスクリプトを使用caseshiftて、位置パラメーターのリストをウォークスルーし、フラグとオプションを引数で解析し、それらを検出してそれらを削除し、構文解析後にそれらを削除して、裸の引数のみを残して、後で残りの残りで処理されるのを見た覚えがあります。脚本。

たとえば、のコマンドラインの解析では、cp -R file1 -t /mybackup file2 -f最初にパラメータを確認し、ユーザーがによってディレクトリに降り-R、ターゲットを指定して-t /mybackupコピーを強制し-f、それらをパラメータのリストから削除することを要求したことを認識します。file1 file2残りの引数として処理するプログラム。

しかし、私はいつでも目にしたスクリプトを覚えている/見つけることができないようです。それができるようになりたいのです。私はさまざまなサイトをグーグルで検索して、調べた関連ページのリストを追加しています。

このWebサイトの質問では、「順序に依存しないオプション」について具体的に質問しましたが、単一の回答とそれがダッピングされた質問の回答のどちらも、オプションが通常の引数と混合されている上記のようなケースを考慮していません。個人が順序に依存しないオプションについて具体的に言及する理由。

以降bashに建てられた-S」getopts最初の非オプションの引数で停止するようだ、解決策として十分であるとは思えません。これが、Wooledge BashFAQのページ(下記を参照)が引数を再配置する方法を説明する理由です。しかし、引数リストが非常に長い場合に備えて、複数の配列を作成しないようにしたいと思います。

以来shiftパラメータリストの真ん中オフ個々の引数をポップサポートしていない、私は私が求めています何を実装するための簡単な方法は何かわかりません。

まったく新しい配列を作成せずに、パラメーターリストの途中から引数を削除する解決策があるかどうか知りたいです。

私がすでに見たページ:


1
複雑なものが必要になったら、bashからPerlに切り替えます。
チョロバ2014

回答:


9

POSIXlyでは、オプションの解析--は、最初の非オプション(または非オプション引数)引数またはそのいずれかが先に来る場所で停止する必要があります。だから

cp -R file1 -t /mybackup file2 -f

それはATのfile1ように、cp再帰的にすべてコピーする必要がありfile1-t/mybackupおよびfile2-fディレクトリ。

GNUは、getopt(3)しかし、(GNUのことをcpあなたはGNUを使用しているここで、解析オプション(とするために使用するcpあなたはGNU固有の使用しているので、-t場合を除き、)オプション)$POSIXLY_CORRECT環境変数が設定されているが、引数の後にオプションを受け付けます。したがって、実際にはPOSIXオプションスタイルの構文解析と同等です。

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

getopts組み込みのシェルは、GNUシェル(bash)でも、POSIXスタイルのみを処理します。また、長いオプションやオプションの引数を持つオプションもサポートしていません。

GNUと同じ方法でオプションを解析する場合はcp、GNU getopt(3)API を使用する必要があります。そのため、Linuxの場合は、拡張getoptユーティリティを使用できますutil-linuxこの拡張バージョンのgetoptコマンドは、FreeBSDのような他のUnicesにも移植されています)。

それは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 $*"

またgetopt、GNUと同じ方法でオプションを解析することにも注意してくださいcp。特に、長いオプション(および省略形での入力)$POSIXLY_CORRECTをサポートし、GNUと同じ方法で環境変数(設定すると、引数の後のオプションのサポートが無効になります)を尊重します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... -- "$@"

GNU cpのサポートされているオプションのリストは、バージョンごとに変更される可能性があり、たとえばオプションにgetopt有効な値を渡したかどうかを確認できないことに注意してください--sparse


@all:返信ありがとうございます。@Stephane:while [ "$#" -gt 0 ]io を使用する理由はありますwhile (($#))か?バシズムを避けるためだけですか?
jamadagni 2014

はい、ただし、(($#))よりkshismです。
ステファンChazelas

1

したがってgetopts、引数を処理するたびに、シェル変数$OPTINDを引数リスト内の次の番号に$OPTIND設定し、処理する必要があり、0以外を返します。が1に設定されている場合、getoptsPOSIXは、新しい引数リスト。したがって、これは単にgetopts戻り値を監視$OPTINDし、失敗した戻り値のカウンタplusを保存し、失敗した引数をシフトし、失敗した$OPTINDすべての試行をリセットします。次のように使用できます。opts "$@"ただし、caseループをカスタマイズするか、それを変数に保存して、そのセクションをに変更する必要がありeval $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

この作品はbashdashzshksh93mksh...よく、私はその時点でしようとして終了します。すべてのシェルで、それも取得$[Rtf]flagしました$targ。ポイントは、getopts処理したくない引数の数がすべて残っていることです。

オプションのスタイルを変更しても違いはありませんでした。-Rf -t/mybackupまたはのように動作しました-R -f -t /mybackup。リストの中央、リストの最後、またはリストの先頭で機能しました...

それでも、最良の方法--、引数リストにfor end of options を貼り付けて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

長いオプションの変換では、非オプションも(cp -t --file fooまたはとしてcp -- --file foo)変換され、省略形(--fi...)で入力されたオプションや--target=/dest構文には対応しません。
ステファンChazelas

@StéphaneChazelas-長い変換は単なる例であり、明らかに十分に構築されていません。getopts単純な関数に置き換えました。
mikeserv 2014

@StéphaneChazelas-もう一度見てください。長いオプションのコメントは正当化されたままですが、最初の-とそれに付随する反対投票-は、私が思うようにもはやそうではありません。また、単一のループで機能する-各引数に一度だけ触れて-確実に(私の知る限り)、移植可能で、単一のサブシェルなしで、opts()あなた自身の答えに何らかの関係があると思います。opts()
mikeserv 2014

OPTINDのリセットは、別のgetoptsループの開始と呼んでいます。実際には、それはオプションのいくつかのコマンドライン/セットを解析しています(-R-t /mybackup-f)。まだ難読化$aされており、初期化されていないため、多くのシェル(bashを含む)で返された後、未設定のargs= opts...ままargs(または以前の設定に設定)になる可能性があるため、今は反対票を投じますopts
ステファンChazelas

@StéphaneChazelas-含まれていますbash-嫌いです。私の意見では、関数が現在のシェル関数である場合、保持する必要があります。私はこれらの問題に取り組んでいます-現在の関数は、より多くのテストにより、すべてのケースを処理するように堅牢に作成でき、その前のオプションで引数にフラグを付けることさえできますが、難読化されていることに同意しません。書かれているように、それは私が考えることができるそのタスクを達成するための最も単純最も直接的な手段です。失敗した場合に失敗した場合にtesting then shiftingはほとんど意味shiftがありませんreturn。それ以外のことを行うことは意図されていません。
mikeserv 2014
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.