getopts
のヘルプが必要です。
実行すると次のようなBashスクリプトを作成しました。
$ foo.sh -i env -dディレクトリ-sサブディレクトリ-fファイル
各フラグから1つの引数を処理するときに正しく機能します。しかし、各フラグから複数の引数を呼び出すと、getopts
の変数から複数の変数情報を引き出す方法がわかりません。
while getopts ":i:d:s:f:" opt
do
case $opt in
i ) initial=$OPTARG;;
d ) dir=$OPTARG;;
s ) sub=$OPTARG;;
f ) files=$OPTARG;;
esac
done
オプションを取得した後、変数からディレクトリ構造を構築したい
foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3
次に、ディレクトリ構造は
/test/directory/subdirectory/file1
/test/directory/subdirectory/file2
/test/directory/subdirectory/file3
/test/directory/subdirectory2/file1
/test/directory/subdirectory2/file2
/test/directory/subdirectory2/file3
何か案は?
同じオプションを複数回使用し、すべての値を配列に追加。
ここで非常に具体的な元の質問については、ライアンのmkdir -p
解決策が明らかに最適です。
ただし、getoptsを使用して同じオプションから複数の値を取得するのより一般的な質問については、次のとおりです。
#!/bin/bash
while getopts "m:" opt; do
case $opt in
m) multi+=("$OPTARG");;
#...
esac
done
shift $((OPTIND -1))
echo "The first value of the array 'multi' is '$multi'"
echo "The whole list of values is '${multi[@]}'"
echo "Or:"
for val in "${multi[@]}"; do
echo " - $val"
done
出力は次のようになります。
$ /tmp/t
The first value of the array 'multi' is ''
The whole list of values is ''
Or:
$ /tmp/t -m "one arg with spaces"
The first value of the array 'multi' is 'one arg with spaces'
The whole list of values is 'one arg with spaces'
Or:
- one arg with spaces
$ /tmp/t -m one -m "second argument" -m three
The first value of the array 'multi' is 'one'
The whole list of values is 'one second argument three'
Or:
- one
- second argument
- three
私はこの質問が古いことを知っていますが、誰かが答えを探しに来た場合に備えて、ここにこの答えを投げたいと思いました。
BASHのようなシェルは、すでにこのようにディレクトリを再帰的に作成することをサポートしているため、スクリプトは実際には必要ありません。たとえば、元のポスターは次のようなものを望んでいました。
$ foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3
/test/directory/subdirectory/file1
/test/directory/subdirectory/file2
/test/directory/subdirectory/file3
/test/directory/subdirectory2/file1
/test/directory/subdirectory2/file2
/test/directory/subdirectory2/file3
これは、次のコマンドラインを使用して簡単に実行できます。
pong:~/tmp
[10] rmclean$ mkdir -pv test/directory/{subdirectory,subdirectory2}/{file1,file2,file3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory’
mkdir: created directory ‘test/directory/subdirectory/file1’
mkdir: created directory ‘test/directory/subdirectory/file2’
mkdir: created directory ‘test/directory/subdirectory/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’
またはさらに少し:
pong:~/tmp
[12] rmclean$ mkdir -pv test/directory/{subdirectory,subdirectory2}/file{1,2,3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory’
mkdir: created directory ‘test/directory/subdirectory/file1’
mkdir: created directory ‘test/directory/subdirectory/file2’
mkdir: created directory ‘test/directory/subdirectory/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’
より短い、より適合性の高いもの:
pong:~/tmp
[14] rmclean$ mkdir -pv test/directory/subdirectory{1,2}/file{1,2,3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory1’
mkdir: created directory ‘test/directory/subdirectory1/file1’
mkdir: created directory ‘test/directory/subdirectory1/file2’
mkdir: created directory ‘test/directory/subdirectory1/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’
または最後に、シーケンスを使用して:
pong:~/tmp
[16] rmclean$ mkdir -pv test/directory/subdirectory{1..2}/file{1..3}
mkdir: created directory ‘test’
mkdir: created directory ‘test/directory’
mkdir: created directory ‘test/directory/subdirectory1’
mkdir: created directory ‘test/directory/subdirectory1/file1’
mkdir: created directory ‘test/directory/subdirectory1/file2’
mkdir: created directory ‘test/directory/subdirectory1/file3’
mkdir: created directory ‘test/directory/subdirectory2’
mkdir: created directory ‘test/directory/subdirectory2/file1’
mkdir: created directory ‘test/directory/subdirectory2/file2’
mkdir: created directory ‘test/directory/subdirectory2/file3’
getoptsオプションは、0個または1個の引数のみを取ることができます。インターフェイスを変更して-fオプションを削除し、残りの非オプション引数を反復処理することができます。
usage: foo.sh -i end -d dir -s subdir file [...]
そう、
while getopts ":i:d:s:" opt; do
case "$opt" in
i) initial=$OPTARG ;;
d) dir=$OPTARG ;;
s) sub=$OPTARG ;;
esac
done
shift $(( OPTIND - 1 ))
path="/$initial/$dir/$sub"
mkdir -p "$path"
for file in "$@"; do
touch "$path/$file"
done
私はあなたがこのように持っていた同じ問題を修正しました:
の代わりに:
foo.sh -i test -d directory -s subdirectory -s subdirectory2 -f file1 file2 file3
これを行う:
foo.sh -i test -d directory -s "subdirectory subdirectory2" -f "file1 file2 file3"
スペース区切り文字を使用すると、基本的なループを使用して実行できます。コードは次のとおりです。
while getopts ":i:d:s:f:" opt
do
case $opt in
i ) initial=$OPTARG;;
d ) dir=$OPTARG;;
s ) sub=$OPTARG;;
f ) files=$OPTARG;;
esac
done
for subdir in $sub;do
for file in $files;do
echo $subdir/$file
done
done
出力例を次に示します。
$ ./getopts.sh -s "testdir1 testdir2" -f "file1 file2 file3"
testdir1/file1
testdir1/file2
testdir1/file3
testdir2/file1
testdir2/file2
testdir2/file3
オプションに任意の数の値を指定する場合は、単純なループを使用してそれらを見つけ、配列に詰め込むことができます。たとえば、OPの例を変更して、任意の数の-sパラメーターを許可します。
unset -v sub
while getopts ":i:d:s:f:" opt
do
case $opt in
i ) initial=$OPTARG;;
d ) dir=$OPTARG;;
s ) sub=("$OPTARG")
until [[ $(eval "echo \${$OPTIND}") =~ ^-.* ]] || [ -z $(eval "echo \${$OPTIND}") ]; do
sub+=($(eval "echo \${$OPTIND}"))
OPTIND=$((OPTIND + 1))
done
;;
f ) files=$OPTARG;;
esac
done
これは最初の引数($ OPTARG)を取り、配列$ subに入れます。その後、別の破線のパラメーターにヒットするまで、残りのパラメーターの検索を続けますOR評価する引数がなくなる。破線のパラメーターではないパラメーターがさらに見つかった場合、それを追加します$ sub配列に追加し、$ OPTIND変数を増やします。
そのため、OPの例では、次を実行できます。
foo.sh -i test -d directory -s subdirectory1 subdirectory2 -f file1
これらの行をスクリプトに追加して実証する場合:
echo ${sub[@]}
echo ${sub[1]}
echo $files
出力は次のようになります。
subdirectory1 subdirectory2
subdirectory2
file1
実際には、getopts
を使用して複数の引数を取得する方法がありますが、getopts
'OPTIND
変数を使用して手動でハッキングする必要があります。
次のスクリプト(下記を参照)を参照してください: https://Gist.github.com/achalddave/290f7fcad89a0d7c3719 。おそらくもっと簡単な方法がありますが、これは私が見つけることができる最も迅速な方法でした。
#!/bin/sh
usage() {
cat << EOF
$0 -a <a1> <a2> <a3> [-b] <b1> [-c]
-a First flag; takes in 3 arguments
-b Second flag; takes in 1 argument
-c Third flag; takes in no arguments
EOF
}
is_flag() {
# Check if $1 is a flag; e.g. "-b"
[[ "$1" =~ -.* ]] && return 0 || return 1
}
# Note:
# For a, we fool getopts into thinking a doesn't take in an argument
# For b, we can just use getopts normal behavior to take in an argument
while getopts "ab:c" opt ; do
case "${opt}" in
a)
# This is the tricky part.
# $OPTIND has the index of the _next_ parameter; so "\${$((OPTIND))}"
# will give us, e.g., ${2}. Use eval to get the value in ${2}.
# The {} are needed in general for the possible case of multiple digits.
eval "a1=\${$((OPTIND))}"
eval "a2=\${$((OPTIND+1))}"
eval "a3=\${$((OPTIND+2))}"
# Note: We need to check that we're still in bounds, and that
# a1,a2,a3 aren't flags. e.g.
# ./getopts-multiple.sh -a 1 2 -b
# should error, and not set a3 to be -b.
if [ $((OPTIND+2)) -gt $# ] || is_flag "$a1" || is_flag "$a2" || is_flag "$a3"
then
usage
echo
echo "-a requires 3 arguments!"
exit
fi
echo "-a has arguments $a1, $a2, $a3"
# "shift" getopts' index
OPTIND=$((OPTIND+3))
;;
b)
# Can get the argument from getopts directly
echo "-b has argument $OPTARG"
;;
c)
# No arguments, life goes on
echo "-c"
;;
esac
done
元の質問はgetoptsを扱っていますが、getoptsなしでより柔軟な機能を提供する別のソリューションがあります(これはおそらくもう少し冗長ですが、はるかに柔軟なコマンドラインインターフェイスを提供します)。以下に例を示します。
while [[ $# > 0 ]]
do
key="$1"
case $key in
-f|--foo)
nextArg="$2"
while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
case $nextArg in
bar)
echo "--foo bar found!"
;;
baz)
echo "--foo baz found!"
;;
*)
echo "$key $nextArg found!"
;;
esac
if ! [[ "$2" =~ -.* ]]; then
shift
nextArg="$2"
else
shift
break
fi
done
;;
-b|--bar)
nextArg="$2"
while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
case $nextArg in
foo)
echo "--bar foo found!"
;;
baz)
echo "--bar baz found!"
;;
*)
echo "$key $nextArg found!"
;;
esac
if ! [[ "$2" =~ -.* ]]; then
shift
nextArg="$2"
else
shift
break
fi
done
;;
-z|--baz)
nextArg="$2"
while ! [[ "$nextArg" =~ -.* ]] && [[ $# > 1 ]]; do
echo "Doing some random task with $key $nextArg"
if ! [[ "$2" =~ -.* ]]; then
shift
nextArg="$2"
else
shift
break
fi
done
;;
*)
echo "Unknown flag $key"
;;
esac
shift
done
この例では、すべてのコマンドラインオプションをループして、受け入れられたコマンドラインフラグ(-fや--fooなど)に一致するパラメーターを探しています。フラグを見つけたら、パラメーターがなくなるか、別のフラグに遭遇するまで、すべてのパラメーターをループします。これにより、フラグのみを処理する外側のループに戻ります。
このセットアップでは、次のコマンドは同等です。
script -f foo bar baz
script -f foo -f bar -f baz
script --foo foo -f bar baz
script --foo foo bar -f baz
次のような信じられないほど整理されたパラメーターセットを解析することもできます。
script -f baz derp --baz herp -z derp -b foo --foo bar -q llama --bar fight
出力を取得するには:
--foo baz found!
-f derp found!
Doing some random task with --baz herp
Doing some random task with -z derp
--bar foo found!
--foo bar found!
Unknown flag -q
Unknown flag llama
--bar fight found!
#!/bin/bash
myname=$(basename "$0")
# help function
help () { cat <<EOP
$myname: -c cluster [...] -a action [...] -i instance [...]
EOP
}
# parse sub options
get_opts () {
rs='' && rc=0 # return string and return code
while [[ $# -gt 0 ]]; do
shift
[[ "$1" =~ -.* ]] && break || rs="$rs $1" && rc=$((rc + 1))
done
echo "$rs"
}
#parse entire command-line
while [[ $# -gt 0 ]]; do
case $1 in
"-a") ACTS="$(get_opts $@)"
;;
"-i") INSTS=$(get_opts $@)
;;
"-c") CLUSTERS=$(get_opts $@)
;;
"-h") help
;;
?) echo "sorry, I dont do $1"
exit
;;
esac
shift
done
リストの作成方法を示していないため
/test/directory/subdirectory/file1
. . .
test/directory/subdirectory2/file3
続行する方法は少しわかりませんが、基本的には適切な変数に新しい値を追加し続ける必要があります。
case $opt in
d ) dirList="${dirList} $OPTARG" ;;
esac
最初のパスではdirが空になり、${dirList}
の最終値のfromから始まるスペースで終わることに注意してください。 (前面または背面に余分なスペースを含まないコードが本当に必要な場合は、表示できるコマンドがありますが、理解するのは難しく、ここでは必要ないようです、教えてください)
その後、リスト変数をforループでラップして、すべての値を出力できます。
for dir in ${dirList} do
for f in ${fileList} ; do
echo $dir/$f
done
done
最後に、caseステートメントへの不明な入力を「トラップ」することをお勧めします。
case $opt in
i ) initial=$OPTARG;;
d ) dir=$OPTARG;;
s ) sub=$OPTARG;;
f ) files=$OPTARG;;
* )
printf "unknown flag supplied "${OPTARG}\nUsageMessageGoesHere\n"
exit 1
;;
esac
これがお役に立てば幸いです。