วิธีการส่งผ่านอาร์เรย์เป็นอาร์กิวเมนต์ฟังก์ชั่น?


57

ดิ้นรนซักพักหนึ่งขณะผ่านอาร์เรย์เป็นอาร์กิวเมนต์ แต่มันไม่ทำงาน ฉันได้ลองด้านล่าง:

#! /bin/bash

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

}

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

copyFiles $array

คำตอบที่มีคำอธิบายจะดี

แก้ไข: โดยทั่วไปฉันจะเรียกใช้ฟังก์ชันจากไฟล์สคริปต์อื่นในที่สุด โปรดอธิบายข้อ จำกัด หากเป็นไปได้

คำตอบ:


84
  • การขยายอาร์เรย์โดยไม่มีดัชนีจะให้เพียงองค์ประกอบแรกเท่านั้น

    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 {…} ไม่ใช่ไวยากรณ์ที่ถูกต้องใช่ไหม แม้ว่าฉันจะเป็น bie ใหม่ฉันสามารถรันโปรแกรมบางอย่างได้ด้วยไวยากรณ์
Ahsanul Haque

สายพันธุ์ที่ถูกต้องคือcopyFiles {…}และcopyFiles(){…}และแต่ไม่copyFiles() {…} copyFiles{…}สังเกตที่ว่างในชุดตัวเลือกโดยไม่ต้อง()
AB

19

หากคุณต้องการส่งอาร์กิวเมนต์หนึ่งตัวขึ้นไปและอาร์เรย์ฉันขอเสนอการเปลี่ยนแปลงนี้กับสคริปต์ของ @AB
Array ควรเป็นอาร์กิวเมนต์สุดท้ายและสามารถส่งผ่านได้เพียงอาร์เรย์เดียวเท่านั้น

#!/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
ขอบคุณสำหรับการshiftใช้งาน
Itachi

นอกจากนี้ยังมีประโยชน์ในการใช้อาร์กิวเมนต์กะบางครั้งดังนั้นหากคุณมี 6 shift 6ข้อโต้แย้งก่อนอาร์เรย์คุณสามารถใช้
spinup

คุณสามารถแปลง "ส่วนที่เหลือของการขัดแย้ง" arrใน เป็นไปได้ไหมที่จะมีพารามิเตอร์อาร์เรย์อยู่ตรงกลาง หรือแม้กระทั่งหลายพารามิเตอร์อาร์เรย์? function copyAndMove() { msg1=$1 ; arr1=...?... ; msg2=? ; arr2=...?... ; msg3=? ; ... }. def copyAndMove(msg1="foo", cpFiles=[], msg2="bar", mvFiles=[], msg3="baz"): ...เหมือนที่ผมจะกำหนดมันในหลาม: ไม่เป็นไรฉันพบstackoverflow.com/a/4017175/472245
towi

18

คุณสามารถส่งผ่านอาร์เรย์เป็นข้อมูลอ้างอิงได้ เช่น:

#!/bin/bash

function copyFiles {
   local -n arr=$1

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

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

copyFiles array

แต่โปรดทราบว่าการแก้ไขใด ๆ ที่จะทำกับอาร์เรย์


1
แม้ว่ามันไม่ได้เป็นอย่างที่ฉันต้องการ แต่มันก็ยังดีที่ได้รู้ว่าการส่งผ่านโดยอ้างอิงในงานทุบตี +1 :)
Ahsanul Haque

3
ต้องทุบตี 4.3+
dtmland

8

มีปัญหาสองสามข้อ นี่คือรูปแบบการทำงาน:

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

}

array=("one" "two" "three")
copyFiles "${array[@]}"
  • อย่างน้อยต้องมีช่องว่างระหว่างการประกาศฟังก์ชันและ {

  • คุณไม่สามารถใช้$arrayเช่นเดียวกับarrayอาร์เรย์ไม่ใช่ตัวแปร หากคุณต้องการได้รับค่าทั้งหมดของการใช้อาร์เรย์"${array[@]}"

  • ในการประกาศฟังก์ชั่นหลักที่คุณต้องการarr="$@"เช่นเดียวกับที่"${array[@]}"จะขยายไปเป็นค่าดัชนีที่คั่นด้วยช่องว่างถ้าคุณใช้$1คุณจะได้รับเฉพาะค่าแรก arr="$arr[@]}"ที่จะได้รับค่าทั้งหมดใช้


คุณต้องการarr=("$@")
AB

เพื่อให้เห็นความแตกต่างเพิ่มด้านล่างbreak echo "$i"ในเวอร์ชั่นของคุณคุณจะยังคงเห็นองค์ประกอบทั้งหมด อย่างไรก็ตามควรเป็นสามบรรทัด
AB

@heemayl: พิมพ์ผิดขนาดเล็ก - {ในชุดกระสุนนัดที่สองของคุณหายไป ... "$ {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 ... พารามิเตอร์ตำแหน่ง โดยที่ index 0 -> 1, 1 -> 2, ... หากต้องการย้ำการเข้าถึงคุณควรใช้ $ 1 เสมอและหลัง Shift ไม่ต้องการอะไรเพิ่มเติม คุณสามารถส่งผ่านข้อโต้แย้งโดยไม่มีอาเรย์เช่นนี้:

show_passed_array one two three four five

bash media จะสร้างอาเรย์โดยอัตโนมัติจากอากิวเมนต์ที่ส่งผ่านเพื่อให้ทำงานและจากนั้นคุณจะมีอากิวเมนต์ตำแหน่ง นอกจากนี้เมื่อคุณเขียน $ {array [2]} คุณเขียนอาร์กิวเมนต์ที่ตามมาจริง ๆ หนึ่งสองสามสี่และส่งผ่านไปยังฟังก์ชัน ดังนั้นการโทรเหล่านั้นจึงเทียบเท่ากัน


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[@]})"

นั่นน่าเกลียดและไม่จำเป็น ถ้าคุณต้องการส่งผ่านชื่ออาร์เรย์ให้ทำarray_internallyนามแฝงของมัน: declare -n array_internally=$1. และส่วนที่เหลือของมันเกี่ยวกับ "มีความซับซ้อน" และ "ตรวจสอบแล้วเอาออก ... " นำไปใช้โดยไม่คำนึงถึงวิธีการที่คุณผ่านอาร์เรย์ดังนั้นฉันไม่เห็นสิ่งที่เป็นจุดของ และevalการจัดเรียงอาร์เรย์ที่อาจมีตัวอักษรพิเศษกำลังรอให้ความเศร้าโศกเกิดขึ้นในภายหลัง
muru
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.