Làm thế nào để vượt qua một mảng như là đối số chức năng?


57

Cố gắng một lúc để vượt qua một mảng làm đối số nhưng dù sao nó cũng không hoạt động. Tôi đã thử như dưới đây:

#! /bin/bash

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

}

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

copyFiles $array

Một câu trả lời với lời giải thích sẽ tốt đẹp.

Chỉnh sửa: Về cơ bản, cuối cùng tôi sẽ gọi hàm từ một tệp script khác. Xin vui lòng giải thích các ràng buộc nếu có thể.

Câu trả lời:


85
  • Mở rộng một mảng mà không có chỉ mục chỉ cung cấp phần tử đầu tiên, sử dụng

    copyFiles "${array[@]}"

    thay vì

    copyFiles $array
  • Sử dụng một cô ấy

    #!/bin/bash
  • Sử dụng cú pháp hàm đúng

    Các biến thể hợp lệ là

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

    thay vì

    function copyFiles{…}
  • Sử dụng cú pháp đúng để lấy tham số mảng

    arr=("$@")

    thay vì

    arr="$1"

vì thế

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

}

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

copyFiles "${array[@]}"

Đầu ra là (tập lệnh của tôi có tên foo)

$ ./foo   
one
two
three

cảm ơn, nhưng chức năng copyFiles {không phải là một cú pháp đúng? Mặc dù tôi là một bie mới nhưng tôi chạy một số chương trình thành công với cú pháp.
Ahsanul Haque

Biến hợp lệ là copyFiles {…}copyFiles(){…}copyFiles() {…}, nhưng không phải copyFiles{…}. Lưu ý không gian trong biến thể không có()
AB

19

Bạn cũng có thể vượt qua các mảng như một tài liệu tham khảo. I E:

#!/bin/bash

function copyFiles {
   local -n arr=$1

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

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

copyFiles array

nhưng lưu ý rằng bất kỳ sửa đổi nào đối với mảng sẽ được thực hiện thành mảng.


2
Mặc dù, đó không phải là chính xác những gì tôi muốn, nhưng thật tuyệt khi biết cách vượt qua công việc tham khảo trong bash. +1 :)
Ahsanul Haque

4
Yêu cầu bash 4.3+
dtmland

19

Nếu bạn muốn truyền một hoặc nhiều đối số VÀ một mảng, tôi đề xuất thay đổi này đối với tập lệnh của @AB
Array phải là đối số cuối cùngchỉ một mảng có thể được thông qua

#!/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[@]}"

Đầu ra:

$ ./foo   
Copying one
Copying two
Copying three

2
+1 để tìm hiểu về một mảng cần phải ở cuối và chỉ nên gửi một mảng
David 'gừng hói'

1
Cảm ơn đã shiftsử dụng.
Itachi

Đôi khi cũng rất hữu ích khi sử dụng đối số của shift, vì vậy nếu bạn có 6 đối số trước mảng, bạn có thể sử dụng shift 6.
spinup

Bạn chuyển đổi "phần còn lại của đối số" thành arr. Có thể có một tham số mảng ở giữa? Hoặc thậm chí một vài tham số mảng? function copyAndMove() { msg1=$1 ; arr1=...?... ; msg2=? ; arr2=...?... ; msg3=? ; ... }. Giống như tôi sẽ định nghĩa nó trong python : def copyAndMove(msg1="foo", cpFiles=[], msg2="bar", mvFiles=[], msg3="baz"): .... Đừng
bận

8

Có một vài vấn đề. Đây là hình thức làm việc:

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

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • Cần có ít nhất một khoảng trắng giữa khai báo hàm và {

  • Bạn không thể sử dụng $array, như arraylà một mảng không phải là một biến. Nếu bạn muốn nhận được tất cả các giá trị của một mảng sử dụng"${array[@]}"

  • Trong bạn khai báo hàm main bạn cần arr="$@""${array[@]}"sẽ mở rộng tới các giá trị được lập chỉ mục cách nhau bằng dấu, nếu bạn sử dụng $1bạn sẽ nhận được chỉ là giá trị đầu tiên. Để có được tất cả các giá trị sử dụng arr="$arr[@]}".


Bạn cầnarr=("$@")
AB

Để thấy sự khác biệt, thêm một breakdưới đây echo "$i". Trong phiên bản của bạn, bạn vẫn sẽ thấy tất cả các yếu tố. Tuy nhiên, nó phải là ba dòng.
AB

@heemayl: lỗi đánh máy nhỏ - {trong mảng đạn thứ hai của bạn bị mất ... "$ {mảng [@]}" ...
Cbhihe

3

Dưới đây là một ví dụ lớn hơn một chút. Để giải thích, xem các ý kiến ​​trong mã.

#!/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

Cách tốt nhất là vượt qua như là đối số vị trí. Không có gì khác. Bạn có thể vượt qua dưới dạng chuỗi, nhưng cách này có thể gây ra một số rắc rối. Thí dụ:

array=(one two three four five)

function show_passed_array(){
  echo $@
}

hoặc là

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

    show_passed_array ${array[@]}

đầu ra:

  one two three four five

Bạn có nghĩa là nếu giá trị mảng có ký hiệu không gian, trước tiên bạn phải trích dẫn các phần tử trước khi vượt qua để truy cập giá trị theo chỉ mục trong hàm sử dụng các tham số vị trí $ 1 $ 2 $ 3 .... Trong đó chỉ mục 0 -> 1, 1 -> 2, ... Để lặp lại quyền truy cập, tốt nhất là luôn sử dụng $ 1 và sau Shift. Không có gì bổ sung là cần thiết. Bạn có thể truyền các đối số mà không có bất kỳ mảng nào như thế này:

show_passed_array one two three four five

bash media tự động xây dựng một mảng từ các đối số được truyền qua chúng để hoạt động và sau đó bạn có các đối số vị trí. Hơn nữa, khi bạn viết $ {mảng [2]}, bạn thực sự viết đối số hệ quả một hai ba bốn và chuyển chúng cho hàm. Vì vậy, những cuộc gọi là tương đương.


1

Xấu xí như nó là, một cách giải quyết hoạt động miễn là bạn không vượt qua một mảng rõ ràng, nhưng một biến tương ứng với một mảng:

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

Tôi chắc chắn rằng ai đó có thể đưa ra một triển khai ý tưởng sạch hơn, nhưng tôi thấy đây là một giải pháp tốt hơn so với việc truyền một mảng "{array[@]"}và sau đó truy cập nội bộ bằng cách sử dụng array_inside=("$@"). Điều này trở nên phức tạp khi có các vị trí / getoptstham số khác. Trong những trường hợp này, trước tiên tôi phải xác định và sau đó loại bỏ các tham số không liên quan đến mảng bằng cách sử dụng một số kết hợp shiftvà loại bỏ phần tử mảng.

Một quan điểm thuần túy có khả năng xem cách tiếp cận này là vi phạm ngôn ngữ, nhưng thực tế mà nói, cách tiếp cận này đã giúp tôi tiết kiệm rất nhiều đau buồn. Trong một chủ đề liên quan, tôi cũng sử dụng evalđể gán một mảng được xây dựng bên trong cho một biến có tên theo một tham số target_varnametôi truyền cho hàm:

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

Đó là xấu xí và không cần thiết. Nếu bạn muốn truyền mảng theo tên, hãy đặt array_internallybí danh của nó : declare -n array_internally=$1. Và phần còn lại của nó về "trở nên phức tạp" và "xác định và sau đó loại bỏ ..." áp dụng cho bất kể bạn vượt qua mảng như thế nào, vì vậy tôi không thấy ý nghĩa của điều đó. Và evaling một mảng có khả năng chứa các ký tự đặc biệt chỉ chờ đợi sự đau buồn xảy ra vào một ngày sau đó.
muru
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.