配列を関数の引数として渡す方法は?


57

引数として配列を渡すのにしばらく苦労していますが、とにかく機能していません。私は以下のように試しました:

#! /bin/bash

function copyFiles{
   arr="$1"
   for i in "${arr[@]}";
      do
          echo "$i"
      done

}

array=("one" "two" "three")

copyFiles $array

説明付きの答えがいいでしょう。

編集:基本的に、私は最終的に別のスクリプトファイルから関数を呼び出します。可能であれば、Plzは制約を説明します。

回答:


85
  • インデックスなしで配列を展開すると、最初の要素のみが使用されます。

    copyFiles "${array[@]}"

    の代わりに

    copyFiles $array
  • シバンを使う

    #!/bin/bash
  • 正しい関数構文を使用してください

    有効なバリアントは

    function copyFiles {…}
    function copyFiles(){…}
    function copyFiles() {…}
    

    の代わりに

    function copyFiles{…}
  • 正しい構文を使用して配列パラメーターを取得します

    arr=("$@")

    の代わりに

    arr="$1"

だから

#!/bin/bash
function copyFiles() {
   arr=("$@")
   for i in "${arr[@]}";
      do
          echo "$i"
      done

}

array=("one" "two" "three")

copyFiles "${array[@]}"

出力は(私のスクリプトの名前はfoo)です

$ ./foo   
one
two
three

感謝しますが、関数copyFiles {…}は正しい構文ではありませんか?私は新しい初心者ですが、構文を使用していくつかのプログラムを正常に実行します。
アサヌールハック

有効なバリアントはcopyFiles {…}and copyFiles(){…}copyFiles() {…}ですが、ではありませんcopyFiles{…}。なしバリアントのスペースに注意してください()
AB

19

参照として配列を渡すこともできます。すなわち:

#!/bin/bash

function copyFiles {
   local -n arr=$1

   for i in "${arr[@]}"
   do
      echo "$i"
   done
}

array=("one" "two" "three")

copyFiles array

ただし、arrに対する変更は配列に対して行われることに注意してください。


2
しかし、それは私が望んでいるものとはまったく異なりましたが、bashで参照渡しがどのように機能するかを知ることはまだ素晴らしいです。+1 :)
アサヌールハク

4
bash 4.3以降が必要
dtmland

19

1つ以上の引数と配列を渡す場合は、@ AB
配列のスクリプトへのこの変更を最後の引数にし、1つの配列のみを渡すことを提案します

#!/bin/bash
function copyFiles() {
   local msg="$1"   # Save first argument in a variable
   shift            # Shift all arguments to the left (original $1 gets lost)
   local arr=("$@") # Rebuild the array with rest of arguments
   for i in "${arr[@]}";
      do
          echo "$msg $i"
      done
}

array=("one" "two" "three")

copyFiles "Copying" "${array[@]}"

出力:

$ ./foo   
Copying one
Copying two
Copying three

2
配列が最後にある必要があり、1つだけを送信する必要があることを学習するために+1
David 'the bald ginger'

1
shift使用していただきありがとうございます。
イタチ

シフトの引数を時々使用することも便利です。したがって、配列の前に6つの引数がある場合は、を使用できますshift 6
スピンアップ

「残りの引数」をに変換しarrます。中央に配列パラメーターを持つことは可能ですか?または、いくつかの配列パラメーターでさえも?function copyAndMove() { msg1=$1 ; arr1=...?... ; msg2=? ; arr2=...?... ; msg3=? ; ... }。私がPythonで定義するように:def copyAndMove(msg1="foo", cpFiles=[], msg2="bar", mvFiles=[], msg3="baz"): ...。気にしないで、stackoverflow.com
a / 4017175/472245

8

問題がいくつかあります。作業フォームは次のとおりです。

#!/bin/bash
function copyFiles {
   arr=( "$@" )
   for i in "${arr[@]}";
      do
          echo "$i"
      done

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • 関数宣言と少なくとも間にスペースが必要です {

  • 配列は変数ではないため$array、使用できませんarray。配列のすべての値を取得したい場合は、"${array[@]}"

  • あなたが必要とするあなたのmain関数の宣言arr="$@"として"${array[@]}"使用する場合は、スペースで区切られたインデックス付きの値に展開されます$1あなたが最初の値のみになるだろう。すべての値を取得するには、を使用しますarr="$arr[@]}"


あなたが必要arr=("$@")
AB

違いを確認するには、break以下を追加しますecho "$i"。ご使用のバージョンでは、すべての要素が引き続き表示されます。ただし、3行にする必要があります。
AB

@heemayl:小さなタイプミス-2番目の箇条書きの配列内の{が欠落しました... "$ {array [@]}" ...
Cbhihe

3

次に、少し大きな例を示します。説明については、コード内のコメントを参照してください。

#!/bin/bash -u
# ==============================================================================
# Description
# -----------
# Show the content of an array by displaying each element separated by a
# vertical bar (|).
#
# Arg Description
# --- -----------
# 1   The array
# ==============================================================================
show_array()
{
    declare -a arr=("${@}")
    declare -i len=${#arr[@]}
    # Show passed array
    for ((n = 0; n < len; n++))
    do
        echo -en "|${arr[$n]}"
    done
    echo "|"
}

# ==============================================================================
# Description
# -----------
# This function takes two arrays as arguments together with their sizes and a
# name of an array which should be created and returned from this function.
#
# Arg Description
# --- -----------
# 1   Length of first array
# 2   First array
# 3   Length of second array
# 4   Second array
# 5   Name of returned array
# ==============================================================================
array_demo()
{
    declare -a argv=("${@}")                           # All arguments in one big array
    declare -i len_1=${argv[0]}                        # Length of first array passad
    declare -a arr_1=("${argv[@]:1:$len_1}")           # First array
    declare -i len_2=${argv[(len_1 + 1)]}              # Length of second array passad
    declare -a arr_2=("${argv[@]:(len_1 + 2):$len_2}") # Second array
    declare -i totlen=${#argv[@]}                      # Length of argv array (len_1+len_2+2)
    declare __ret_array_name=${argv[(totlen - 1)]}     # Name of array to be returned

    # Show passed arrays
    echo -en "Array 1: "; show_array "${arr_1[@]}"
    echo -en "Array 2: "; show_array "${arr_2[@]}"

    # Create array to be returned with given name (by concatenating passed arrays in opposite order)
    eval ${__ret_array_name}='("${arr_2[@]}" "${arr_1[@]}")'
}

########################
##### Demo program #####
########################
declare -a array_1=(Only 1 word @ the time)                                       # 6 elements
declare -a array_2=("Space separated words," sometimes using "string paretheses") # 4 elements
declare -a my_out # Will contain output from array_demo()

# A: Length of array_1
# B: First array, not necessary with string parentheses here
# C: Length of array_2
# D: Second array, necessary with string parentheses here
# E: Name of array that should be returned from function.
#          A              B             C              D               E
array_demo ${#array_1[@]} ${array_1[@]} ${#array_2[@]} "${array_2[@]}" my_out

# Show that array_demo really returned specified array in my_out:
echo -en "Returns: "; show_array "${my_out[@]}"

1

最良の方法は、位置引数として渡すことです。他に何もありません。文字列として渡すこともできますが、この方法では問題が発生する可能性があります。例:

array=(one two three four five)

function show_passed_array(){
  echo $@
}

または

function show_passed_array(){
  while $# -gt 0;do
    echo $1;shift
  done
}

    show_passed_array ${array[@]}

出力:

  one two three four five

配列値にスペース記号が含まれている場合、関数のインデックスで値にアクセスするために渡す前に、要素を最初に引用する必要があります$ 1 $ 2 $ 3 ...位置パラメーターを使用します。where index 0-> 1、1-> 2、...アクセスを反復するには、常に$ 1およびShiftの後に使用するのが最善です。追加の必要はありません。次のような配列なしで引数を渡すことができます。

show_passed_array one two three four five

bashメディアは、関数に渡された引数から関数に配列を自動的に構築します。その後、位置引数があります。さらに、$ {array [2]}を記述するとき、実際に結果の引数1 2 3 4を記述し、それらを関数に渡します。したがって、これらの呼び出しは同等です。


1

いですが、明示的に配列を渡すのではなく、配列に対応する変数を渡す限り動作する回避策があります。

function passarray()
{
    eval array_internally=("$(echo '${'$1'[@]}')")
    # access array now via array_internally
    echo "${array_internally[@]}"
    #...
}

array=(0 1 2 3 4 5)
passarray array # echo's (0 1 2 3 4 5) as expected

誰かがアイデアのよりクリーンな実装を思い付くことができると確信していますが、これを配列として渡し、"{array[@]"}を使用して内部的にアクセスするよりも優れたソリューションであることがわかりましたarray_inside=("$@")。他の位置/ getoptsパラメータがある場合、これは複雑になります。これらの場合、shift配列要素の削除の組み合わせを使用して、配列に関連付けられていないパラメーターを最初に決定してから削除する必要がありました。

純粋主義者の観点からは、このアプローチは言語の違反と見なされる可能性がありますが、実際的に言えば、このアプローチは非常に多くの悲しみを救いました。関連トピックではeval、内部的に構築された配列target_varnameを、関数に渡すパラメーターに従って名前が付けられた変数に割り当てるためにも使用します。

eval $target_varname=$"(${array_inside[@]})"

それはugくて不必要です。配列を名前で渡す場合はarray_internally、エイリアスを作成しますdeclare -n array_internally=$1。そして、「複雑になります」と「決定してから削除する...」についての残りの部分は、配列を渡す方法に関係なく適用されるため、その意味はわかりません。そしてeval、潜在的に特殊文字を含む配列を作成することは、悲しみが後日発生するのを待っています。
ムル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.