Bash パラメーター配列の索引付けと変更 $@

Bash パラメーター配列の索引付けと変更 $@

でインデックスを参照できますか$@?どこでも使用できる参照が見つかりません。灰色猫ウィキ、しかも高度なスクリプトガイドそして他の人変更する前に他の変数に代入してください。

$ echo ${@[0]}
-bash: ${@[0]}: bad substitution

目標は乾いた:最初の引数は1つのものであり、残りは別のものです。正規化、配列のコードを繰り返す、またはそのための別々の関数を生成することを避けたいです$@(おそらくこの時点で最も簡単な方法ですが)。

説明:目的は修正することです。可変長 $@作るパスワードデバッグが簡単です。現在のバージョンは私にとっては時代遅れですが、奇妙なパスでも動作します。

$'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'

修正する:不可能そうです。パスワードこれで、コードとデータのコピーの両方を使用しますが、少なくとも機能します。

path_common()
{
    # Get the deepest common path.
    local common_path="$(echo -n "${1:-}x" | tr -s '/')"
    common_path="${common_path%x}"
    shift # $1 is obviously part of $1
    local path

    while [ -n "${1+defined}" ]
    do
        path="$(echo -n "${1}x" | tr -s '/')"
        path="${path%x}"
        if [[ "${path%/}/" = "${common_path%/}/"* ]]
        then
            shift
        else
            new_common_path="${common_path%/*}"
            [ "$new_common_path" = "$common_path" ] && return 1 # Dead end
            common_path="$new_common_path"
        fi
    done
    printf %s "$common_path"
}

この混乱から逃れる人は誰でも賞金を受け取るでしょう。重複コード繰り返されるスラッシュを折りたたむデータ複製$1コードを適切なサイズに保ち、すべてのユニットテストに正常に合格し、および/または他のパラメータを保存します。

test "$(path_common /a/b/c/d /a/b/e/f; echo x)" = /a/bx
test "$(path_common /long/names/foo /long/names/bar; echo x)" = /long/namesx
test "$(path_common / /a/b/c; echo x)" = /x
test "$(path_common a/b/c/d a/b/e/f ; echo x)" = a/bx
test "$(path_common ./a/b/c/d ./a/b/e/f; echo x)" = ./a/bx
test "$(path_common $'\n/\n/\n' $'\n/\n'; echo x)" = $'\n/\n'x
test "$(path_common --/-- --; echo x)" = '--x'
test "$(path_common '' ''; echo x)" = x
test "$(path_common /foo/bar ''; echo x)" = x
test "$(path_common /foo /fo; echo x)" = x
test "$(path_common $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n' $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'; echo x)" = $'--$`\! *@ \a\b\e\E\f\r\t\v\\\"\' \n'x
test "$(path_common /foo/bar //foo//bar//baz; echo x)" = /foo/barx
test "$(path_common foo foo; echo x)" = foox
test "$(path_common /fo /foo; echo x)" = x

答え1

POSIX

すべての引数でスラッシュを正規化するために、引数回転トリックを使用します。これをオフにして$1変換し、結果を引数リストの最後に配置します。これを引数の数だけ実行すると、すべての引数が変換され、元の順序に復元されます。

コードの2番目の部分では、あまり混乱しないようにロジックを変更しました。外部ループはパラメータを繰り返し、内部ループはパスコンポーネントを繰り返します。for x; do … done位置パラメータを繰り返すのは便利なイディオムです。 POSIX互換の方法である構文を使用して、パターンと文字列を一致させますcase

テストには、dash 0.5.5.1、pdksh 5.2.14、bash 3.2.39、bash 4.1.5、ksh 93s+、zsh 4.3.10を使用してください。

注:bash 4.1.5(3.2にはありません)にバグがあるようです。ケースモードがある場合、テストの"${common_path%/}"/*1つは失敗します。

posix_path_common () {
  for tmp; do
    tmp=$(printf %s. "$1" | tr -s "/")
    set -- "$@" "${tmp%.}"
    shift
  done
  common_path=$1; shift
  for tmp; do
    while case ${tmp%/}/ in "${common_path%/}/"*) false;; esac; do
      new_common_path=${common_path%/*}
      if [ "$new_common_path" = "$common_path" ]; then return 1; fi
      common_path=$new_common_path
    done
  done
  printf %s "$common_path"
}

バッシュ、クッシュ

bash(またはksh)を使用している場合は、配列を使用できます。なぜ位置引数に制限するのか理解できません。配列を使用するバージョンです。 POSIXバージョンより特に明確ではありませんが、最初のn ^ 2シャッフリングを防ぐことを認めなければなりません。

スラッシュ正規化部分では、ksh93コンストラクタを使用して${foo//PATTERN/REPLACEMENT}inのすべての項目を 。このパターンは、bashで1つ以上のスラッシュを一致させるために使用され、有効でなければなりません(startup bashを使用するのと同じ)。この構成は、位置引数を配列の添字リストに設定します。これは、配列要素を繰り返す便利な方法を提供します。PATTERN$fooREPLACEMENT+(\/)shopt -s extglobbash -O extglobset ${!a[@]}a

2番目の部分では、POSIXバージョンと同じループロジックを使用します。今回は[[ … ]]ここのすべてのターゲットシェルがサポートしているので使用できます。

テストにはbash 3.2.39、bash 4.1.5、ksh 93s+を使用してください。

array_path_common () {
  typeset a i tmp common_path new_common_path
  a=("$@")
  set ${!a[@]}
  for i; do
    a[$i]=${a[$i]//+(\/)//}
  done
  common_path=${a[$1]}; shift
  for tmp; do
    tmp=${a[$tmp]}
    while [[ "${tmp%/}/" != "${common_path%/}/"* ]]; do
      new_common_path="${common_path%/*}"
      if [[ $new_common_path = $common_path ]]; then return 1; fi
      common_path="$new_common_path"
    done
  done
  printf %s "$common_path"
}

扱いにくい

残念ながら、zshには${!array[@]}ksh93バージョンをそのまま実行する機能がありません。幸いなことに、zshには最初の部分を簡単に作成する2つの機能があります。配列などの位置パラメータにインデックスを付けることができるため、@中間配列を使用する必要はありません。 zshには1つあります配列反復構成"${(@)array//PATTERN/REPLACEMENT}"各配列要素に対して順番にパターン置換を実行し、結果の配列を評価します(混乱させても、結果が複数の単語の場合でも二重引用符が必要です。これは一般化です"$@")。 2番目の部分は本質的に変更されていません。

zsh_path_common () {
  setopt local_options extended_glob
  local tmp common_path new_common_path
  set -- "${(@)@//\/##//}"
  common_path=$1; shift
  for tmp; do
    while [[ "${tmp%/}/" != "${common_path%/}/"* ]]; do
      new_common_path="${common_path%/*}"
      if [[ $new_common_path = $common_path ]]; then return 1; fi
      common_path="$new_common_path"
    done
  done
  printf %s "$common_path"
}

テストケース

私の解決策は最小限にテストされ、コメントがありました。シェルなしで$'…'解析し、より便利な方法でエラーを報告できるように、テストケースの構文を変更しました。

do_test () {
  if test "$@"; then echo 0; else echo $? "$@"; failed=$(($failed+1)); fi
}

run_tests () {
  function_to_test=$1; shift
  failed=0
  do_test "$($function_to_test /a/b/c/d /a/b/e/f; echo x)" = /a/bx
  do_test "$($function_to_test /long/names/foo /long/names/bar; echo x)" = /long/namesx
  do_test "$($function_to_test / /a/b/c; echo x)" = /x
  do_test "$($function_to_test a/b/c/d a/b/e/f ; echo x)" = a/bx
  do_test "$($function_to_test ./a/b/c/d ./a/b/e/f; echo x)" = ./a/bx
  do_test "$($function_to_test '
/
/
' '
/
'; echo x)" = '
/
'x
  do_test "$($function_to_test --/-- --; echo x)" = '--x'
  do_test "$($function_to_test '' ''; echo x)" = x
  do_test "$($function_to_test /foo/bar ''; echo x)" = x
  do_test "$($function_to_test /foo /fo; echo x)" = x
  do_test "$($function_to_test '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
' '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
'; echo x)" = '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
'x
  do_test "$($function_to_test /foo/bar //foo//bar//baz; echo x)" = /foo/barx
  do_test "$($function_to_test foo foo; echo x)" = foox
  do_test "$($function_to_test /fo /foo; echo x)" = x
  if [ $failed -ne 0 ]; then echo $failed failures; return 1; fi
}

答え2

$1, $2.. $9, ${10}, ${11}.. などを使うとどうなりますか?さらに進む乾いた-何をしたいのかがもっと重要です:)

$ 間の関係の詳細数字そして$ @:

$@ は、「すべての引数を含む配列のすべての要素」の略称と考えることができます。

したがって、$ @は$ {args [@]}の略です(ここで、argsはすべての引数を含む「仮想」配列です。実際の変数ではありません)

$1 は ${args[1]}、$2 は ${args[2]} です。

[9]を押すと中括弧を使用します。 ${10} は ${args[10]}、${11} は ${args[11]} です。


コマンドラインパラメータを間接的に使用する

argnum=3  # You want to get the 3rd arg
do-something ${!argnum}  # Do something with the 3rd arg

例:

argc=$#
for (( argn=1; argn<=argc; argn++)); do
    if [[ ${!argn} == "foo" ]]; then
        echo "Argument $argn of $argc is 'foo'"
    fi
done

答え3

最初のパラメータは1つにのみ使用され、残りは他の目的に使用されます。

私の考えでは、あなたが望むものshift

$ set one two three four five
$ echo $@
one two three four five
$ echo $1
one
$ foo=$1
$ echo $foo
one
$ shift
$ echo $@
two three four five
$ shift 2
$ echo $@
four five
$ echo $1
four

答え4

ファイル名にはスペースがサポートされます。

function SplitFilePath {
    IFS=$'/' eval "${1}"=\( \${2} \)
}
function JoinFilePath {
    IFS=$'/' eval echo -n \"\${*}\"
    [ $# -eq 1 -a "${1}" = "" ] && echo -n "/"
}
function path_common {
    set -- "${@//\/\///}"       ## Replace all '//' with '/'
    local -a Path1
    local -i Cnt=0
    SplitFilePath Path1 "${1}"
    IFS=$'/' eval set -- \${2} 
    for CName in "${Path1[@]}" ; do
        [ "${CName}" != "${1}" ] && break;
        shift && (( Cnt++ ))
    done
    JoinFilePath "${Path1[@]:0:${Cnt}}"
}

空白のファイル名テストケースを追加し、先行/不足している2つのテストを修正しました。

    do_test () {

  if test "${@}"; then echo 0; else echo $? "$@"; failed=$(($failed+1)); fi
}

run_tests () {
  function_to_test=$1; shift
  failed=0
  do_test "$($function_to_test /a/b/c/d /a/b/e/f; echo x)" = /a/bx
  do_test "$($function_to_test /long/names/foo /long/names/bar; echo x)" = /long/namesx
  do_test "$($function_to_test / /a/b/c; echo x)" = /x      
  do_test "$($function_to_test a/b/c/d a/b/e/f ; echo x)" = a/bx
  do_test "$($function_to_test ./a/b/c/d ./a/b/e/f; echo x)" = ./a/bx
  do_test "$($function_to_test '
/
/
' '
/
'; echo x)" = '
/
'x
  do_test "$($function_to_test --/-- --; echo x)" = '--x'
  do_test "$($function_to_test '' ''; echo x)" = x
  do_test "$($function_to_test /foo/bar ''; echo x)" = x
  do_test "$($function_to_test /foo /fo; echo x)" = /x      ## Changed from x
  do_test "$($function_to_test '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
' '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
'; echo x)" = '--$`\! *@ \a\b\e\E\f\r\t\v\\\"'\'' 
'x
  do_test "$($function_to_test /foo/bar //foo//bar//baz; echo x)" = /foo/barx
  do_test "$($function_to_test foo foo; echo x)" = foox
  do_test "$($function_to_test /fo /foo; echo x)" = /x          ## Changed from x
  do_test "$($function_to_test "/fo d/fo" "/fo d/foo"; echo x)" = "/fo dx"

  if [ $failed -ne 0 ]; then echo $failed failures; return 1; fi
}

関連情報