
getopts
複数回使用できるように、いくつかのロジックを関数に移動して、ユーザーに指定されたパラメータの順序でより柔軟性を提供したいと思います。
print-usage() {
echo "myssh [options] <host> [options] [-- ssh-options...]" >&2
exit 1
}
extra_args=()
parse-args() {
while getopts ":hvV:" opt; do
case ${opt} in
(h) print-usage ;;
(v) extra_args+=('-L 5900:localhost:5900') ;;
(V) extra_args+=("-L $OPTARG:localhost:5900") ;;
(\?) echo "Invalid option: -$OPTARG" >&2 ;;
(:) echo "Invalid option: -$OPTARG requires an argument" >&2 ;;
esac
done
echo $((OPTIND -1))
}
shift $(parse-args $@)
host=$1
shift
shift $(parse-args $@)
ssh $host $extra_args $@
私の問題は、これがparse-args() { ... extra_args+=(...) }
グローバル変数に影響を与えないことですextra_args
。私はサブシェルが親範囲変数に書き込むことができず、通常はstdoutを使用する必要があることを知っていますが、stdoutを移動した整数として使用しました。
この問題は通常どのように解決されますか?
答え1
(タグ付き)使用しているので、bash
配列変数を使用できます。できないことは、サブシェルのshift $(parse_args "$@")
thisのような呼び出しを介してグローバル変数に影響を与えることです。parse_args
以下は、関数の外部に値の配列を渡す代替ソリューションです。
#!/bin/bash
#
extra_args1=()
extra_args2=()
usage() {
echo "myssh [options] <host> [options] [-- ssh-options...]" >&2
exit 1
}
parse_args() {
local OPT OPTIND=1 OPTARG args=()
while getopts ":hvV:" OPT
do
case "$OPT" in
(h) usage ;;
(v) args+=('-L' '5900:localhost:5900') ;;
(V) args+=('-L' "$OPTARG:localhost:5900") ;;
(\?) echo "Invalid option: -$OPTARG" >&2 ;;
(:) echo "Invalid option: -$OPTARG requires an argument" >&2 ;;
esac
done
echo $((OPTIND -1)) "${args[@]}"
}
# Get number to shift plus set of arguments
extra_args1=($(parse_args "$@"))
shift ${extra_args1[0]}
unset extra_args1[0]
# Position dependent value
host="$1"
shift
# Get number to shift plus set of arguments
extra_args2=($(parse_args "$@"))
unset extra_args2[0]
# "${x[@]}" values are interpolated as quoted and vanish if fully empty
ssh "$host" "${extra_args1[@]}" "${extra_args2[@]}" "$@"
二重引用符を使用すると、"$@"
個々の値自体が二重引用符として扱われるだけでなく、拡張する値がないと拡張全体が消える魔法を使用できます。同じ機能を使用して"${extra_args1[@]}"
拡張を処理できます。"${extra_args2[@]}"
この方法の潜在的な欠点の1つは、それa=($(function))
自体がワイルドカードの対象となるため、function
ワイルドカードが返されると配列割り当ての一部に拡張されることですa
。この問題に対する良い解決策はありません。
答え2
パイプを使用して可能な解決策ですFIFO
。関数で定義するのではなく、関数を使用してextra_args
パイプに書き込み、関数を呼び出しextra_args
てから読み込みます。
#!/bin/bash
trap "rm -rf extraargpipe" ERR EXIT
mkfifo extraargpipe
parseargs() {
echo 1 > extraargpipe &
echo 2 > extraargpipe &
}
parseargs
extra_args=$(cat extraargpipe)
echo ${extra_args}
FIFOロジック(または正しく覚えている場合は実際に競合状態)が原因で返されますが、2 1
誤った順序は定義ssh
したオプションに問題はありません。似たようなものを作りたいなら、-o opt1,opt2,opt3
それはすべてです。
一時ファイルを使用してほぼ同じ操作を実行できます。