少し行き詰まっています。私の仕事は、スクリプトの引数を3番目と4番目を除いて逆の順序で出力することです。
私が持っているのはこのコードです:
#!/bin/bash
i=$#
for arg in "$@"
do
case $i
in
3) ;;
4) ;;
*) eval echo "$i. Parameter: \$$i";;
esac
i=`expr $i - 1`
done
私はeval(PHPへの挨拶)が嫌いなので、それなしで解決策を探していますが、それを見つけることができません。
引数の位置を動的に定義するにはどうすればよいですか?
PS:いいえ、宿題ではありません。私は試験のためにシェルを学んでいるので、古い試験を解こうとしています。
eval
は、動的に選択された位置によって位置パラメータにアクセスする唯一の移植可能な方法です。値ではなく(使用していない)インデックスを明示的にループすると、スクリプトがより明確になります。アンティークのBourneシェルでスクリプトを実行したい場合を除き、expr
は必要ありません。 $((…))
演算はPOSIXにあります。 eval
の使用を可能な限り最小のフラグメントに制限してください。たとえば、eval echo
を使用せずに、一時変数に値を割り当てます。
i=$#
while [ "$i" -gt 0 ]; do
if [ "$i" -ne 3 ] && [ "$i" -ne 2 ]; then
eval "value=\${$i}"
echo "Parameter $i is $value"
fi
i=$((i-1))
done
Bashでは、${!i}
を使用して、名前が$i
のパラメーターの値を意味できます。これは、$i
が名前付きパラメーターまたは数値(位置パラメーターを表す)の場合に機能します。その間、他のbashの便利な機能を利用できます。
for ((i=$#; i>0; i--)); do
if ((i != 3 && i != 4)); then
echo "Parameter $i is ${!i}"
fi
done
私は自分のパスにこれを実行するスクリプトreverse
を保持しています。
#!/bin/sh
if [ "$#" -gt 0 ]; then
arg=$1
shift
reverse "$@"
printf '%s\n' "$arg"
fi
使用例:
$ reverse a b c '*' '-n'
-n
*
c
b
a
専用スクリプトの代わりに関数を使用することもできます。
定位置パラメーターに改行文字が含まれていないと想定します。
[ "$#" -gt 0 ] && printf '%s\n' "$@" | #print in order
sed '3,4 d' | #skip 3 and 4
tac #reverse (try tail -r on
#non-GNU systems).
テスト:
set 1 2 3 4 5 6
printf '%s\n' "$@" |
sed '3,4 d' |
tac
テスト出力:
6
5
2
1
これは、evalの正しい使用であり、危険ではありません。 eval
ingするコンテンツを完全に制御します。
それでも悪い感じがする場合は、移植性を気にしないのであれば、Bashの${!i}
間接構文を使用できます。
zsh
の場合:
$ set a 'foo bar' c '' '*'
$ printf '<%s>\n' "${(Oa)@}"
<*>
<>
<c>
<foo bar>
<a>
Oa
は、拡張時に配列要素をreverse配列インデックスでソートするためのパラメーター拡張フラグです。
3と4を除外するには:
$ printf '<%s>\n' "${(Oa)@[5,-1]}" "${(Oa)@[1,2]}"
<*>
<foo bar>
<a>
基本的にどんなシェルでも:
printf '{ PS4=\${$(($#-$x))}; } 2>&3; 2>&1\n%.0s' |
x=LINENO+1 sh -sx "$@" 3>/dev/null
そして、サブシェルを使用するためにneedを使用しないでください。例えば:
set -x a b c
{ last= PS4=\${last:=\${$#}}; set +x; } 2>/dev/null
echo "$last"
...プリント...
c
そして、ここにShell alias
を設定できるShell関数があり、引数を前方または後方に出力します。
tofro() case $1 in (*[!0-9]*|'') ! :;;(*) set "$1"
until [ "$1" -eq "$(($#-1))" ] &&
shift && alias args=":; printf \
\"%.\$((\$??\${#*}:0))s%.\$((!\$??\${#*}:0))s\n\" $* "
do [ "$#" -gt 1 ] &&
set "$@ \"\${$#}\" " '"${'"$((1+$1-$#))"'}"' ||
set "$1" '"$1" "${'"$1"'}"'
done; esac
引数のリテラル値を格納するのではなく、args
alias
に次のような文字列を入れます。
:;printf "%.$(($??${#*}:0))s%.$((!$??${#*}:0))s\n" \
"$1" "${3}" "${2}" "${2}" "${3}" "${1}"
...したがって、パラメータへの参照のみを逆方向および順方向に格納します。引数として与えられたカウントまで保存します。そして上記のalias
は次のように生成されました:
tofro 3
printf
の動作は、前のコマンドの戻り値に基づいて影響を受けます。これは常に:
nullコマンドなので、通常はtrueです。 printf
は、出力するたびに引数の半分をスキップします。デフォルトでは、引数は番号の小さいものから大きいものの順に出力されます。ただし、次のようにした場合:
! args
...それらを逆に印刷します。
エイリアスはリテラル値を格納しないので、実際の引数が変更される可能性がある間、その値は静的なままですが、それでも、可能な限り多くを参照します。例えば:
set one two three
tofro 3
args; ! args
shift; args; ! args
...印刷する...
one
two
three
three
two
one
two
three
three
two
ただし、エイリアスのリセットは次のように実行できます。
tofro 2
args; ! args
...それで印刷されます...
two
three
three
two