ผ่านอาร์เรย์เป็นพารามิเตอร์ในทุบตี


188

ฉันจะส่งผ่านอาร์เรย์เป็นพารามิเตอร์ไปยังฟังก์ชัน bash ได้อย่างไร

หมายเหตุ:หลังจากไม่พบคำตอบที่นี่ใน Stack Overflow ฉันโพสต์คำตอบที่ค่อนข้างหยาบของฉันเอง อนุญาตให้ส่งผ่านได้เพียงหนึ่งอาเรย์เท่านั้นและเป็นองค์ประกอบสุดท้ายของรายการพารามิเตอร์ ที่จริงแล้วมันไม่ได้ผ่านอาร์เรย์เลย แต่รายชื่อขององค์ประกอบของมันซึ่งถูกประกอบเข้าอีกครั้งในอาร์เรย์โดยเรียกว่า _function () แต่มันได้ผลสำหรับฉัน หากใครรู้วิธีที่ดีกว่าอย่าลังเลที่จะเพิ่มที่นี่


1
ที่นี่คุณมีการอ้างอิงที่ดีและตัวอย่างมากมาย
Artem Barger

16
ข้อผิดพลาด ... สาม downvotes สำหรับคำถามห้าปีภายในนาทีเดียวกัน?
DevSolar

คำตอบ:


220

คุณสามารถส่งหลายอาร์เรย์เป็นอาร์กิวเมนต์โดยใช้สิ่งนี้:

takes_ary_as_arg()
{
    declare -a argAry1=("${!1}")
    echo "${argAry1[@]}"

    declare -a argAry2=("${!2}")
    echo "${argAry2[@]}"
}
try_with_local_arys()
{
    # array variables could have local scope
    local descTable=(
        "sli4-iread"
        "sli4-iwrite"
        "sli3-iread"
        "sli3-iwrite"
    )
    local optsTable=(
        "--msix  --iread"
        "--msix  --iwrite"
        "--msi   --iread"
        "--msi   --iwrite"
    )
    takes_ary_as_arg descTable[@] optsTable[@]
}
try_with_local_arys

จะสะท้อน:

sli4-iread sli4-iwrite sli3-iread sli3-iwrite  
--msix  --iread --msix  --iwrite --msi   --iread --msi   --iwrite

แก้ไข / หมายเหตุ: (จากความคิดเห็นด้านล่าง)

  • descTableและoptsTableถูกส่งเป็นชื่อและถูกขยายในฟังก์ชัน ดังนั้นไม่$จำเป็นเมื่อกำหนดเป็นพารามิเตอร์
  • โปรดทราบว่าสิ่งนี้ยังคงใช้ได้แม้กับdescTableฯลฯ ที่ถูกกำหนดด้วยlocalเพราะคนในท้องถิ่นสามารถมองเห็นได้จากฟังก์ชั่นที่พวกเขาเรียก
  • !ใน${!1}การขยายตัวของตัวแปรหาเรื่อง 1
  • declare -a เพียงแค่ทำให้อาร์เรย์ที่มีการจัดทำดัชนีชัดเจนไม่จำเป็นอย่างเคร่งครัด

14
สิ่งหนึ่งที่ควรทราบก็คือถ้าอาร์เรย์เดิมมีเบาบางอาร์เรย์ในฟังก์ชันการรับจะไม่มีดัชนีเดียวกัน
หยุดชั่วคราวจนกว่าจะมีการแจ้งให้ทราบต่อไป

13
นี่เป็นสิ่งที่ยอดเยี่ยม แต่เคนหรือใครบางคนสามารถอธิบายบางสิ่งที่ทำให้ฉันเข้าใจว่าทำไมมันถึงใช้งานได้: 1 - ฉันคิดว่า descTable และ optsTable น่าจะต้องมีคำนำหน้าด้วย $ เมื่อผ่านเป็นอาร์กิวเมนต์ของฟังก์ชัน 2 - ในบรรทัดแรกของ "take ... " เหตุใดจึงต้องมีการประกาศอาร์เรย์ที่ชัดเจน 3 - แล้วอะไรล่ะ! หมายถึงในการแสดงออก $ {! 1} และทำไม [@] ไม่จำเป็นหรือแม้กระทั่งได้รับอนุญาตที่นั่น? - ใช้งานได้และรายละเอียดทั้งหมดเหล่านี้ดูเหมือนจะต้องการจากการทดสอบของฉัน แต่ฉันอยากจะเข้าใจว่าทำไม!
Jan Hettich

8
1: descTable และ optsTable เพิ่งผ่านเป็นชื่อดังนั้นไม่มี $ พวกเขาจะถูกขยายเฉพาะในฟังก์ชันที่เรียก 2: ไม่แน่ใจทั้งหมด แต่ฉันคิดว่ามันไม่จำเป็นจริงๆ 3: the! ถูกใช้เนื่องจากพารามิเตอร์ที่ส่งไปยังฟังก์ชันจำเป็นต้องขยายสองครั้ง: $ 1 ขยายเป็น "descTable [@]" และควรขยายเป็น "$ {descTable [@]}" ไวยากรณ์ $ {! 1} ทำสิ่งนี้ได้
Elmar Zander

8
ฉันไม่คิดว่าจำเป็นต้องมีส่วน "ประกาศ -a" การมีอยู่ของวงเล็บกำหนด LHS ของการกำหนดเป็นอาร์เรย์แล้ว
Erik Aronesty

3
คำตอบนี้ช่วยฉันแก้ปัญหาได้ในตอนนี้ อย่างไรก็ตามฉันต้องการชี้ให้เห็นว่าในเครื่องของฉัน (โดยใช้ bash 4.3.42) "$ {! 1}" และ "$ {! 2}" จำเป็นต้องลบเครื่องหมายคำพูดออก หากคุณไม่อ่านค่าของอาร์เรย์ดั้งเดิมจะอ่านเป็นหนึ่งสตริงและกำหนดให้กับ argAry1 [0] และ argAry2 [0] ตามลำดับโดยทั่วไปหมายถึงโครงสร้างอาร์เรย์จะสูญหายไป
มิตร

85

หมายเหตุ:นี่เป็นคำตอบที่ค่อนข้างหยาบที่ฉันโพสต์ตัวเองหลังจากไม่พบคำตอบที่นี่ใน Stack Overflow อนุญาตให้ส่งผ่านได้เพียงหนึ่งอาเรย์เท่านั้นและเป็นองค์ประกอบสุดท้ายของรายการพารามิเตอร์ ที่จริงแล้วมันไม่ได้ผ่านอาร์เรย์เลย แต่รายชื่อขององค์ประกอบของมันซึ่งถูกประกอบเข้าอีกครั้งในอาร์เรย์โดยเรียกว่า _function () แต่มันได้ผลสำหรับฉัน ไม่นานเคนก็โพสต์คำตอบของเขา แต่ฉันเก็บไว้ที่นี่เพื่ออ้างอิง "ประวัติศาสตร์"

calling_function()
{
    variable="a"
    array=( "x", "y", "z" )
    called_function "${variable}" "${array[@]}"
}

called_function()
{
    local_variable="${1}"
    shift
    local_array=("${@}")
}

ปรับปรุงโดย TheBbean ขอบคุณ


19
สามปีหลังจากข้อเท็จจริงคำตอบนี้เก็บไว้เพื่อเหตุผลทางประวัติศาสตร์เท่านั้น - ได้รับการโหวตสองครั้งภายในสองสามวัน เป็นเรื่องเศร้าตามปกติโดยไม่มีการบันทึกว่าทำไมผู้คนถึงคิดว่าสิ่งนี้ได้รับการรับประกัน โปรดทราบว่าคำตอบนี้มาก่อนคนอื่น ๆ ทั้งหมดและฉันยอมรับคำตอบของเคนว่าเป็นทางออกที่ดีที่สุด ฉันรู้อย่างสมบูรณ์แบบว่ามันไม่มีที่ไหนใกล้สมบูรณ์แบบ แต่เป็นเวลาสี่เดือนที่ดีที่สุดสำหรับ SO ทำไมมันควรจะลดลงสองปีหลังจากที่มันเกิดขึ้นที่สองในการแก้ปัญหาที่สมบูรณ์แบบของเคนคือเกินฉัน
DevSolar

@geirha: ฉันจะขอให้คุณตรวจสอบว่ามีใครโพสต์คำถามใครโพสต์คำตอบนี้และผู้ที่อาจยอมรับคำตอบที่คุณเรียกว่า "ไม่ดี" ;-) คุณอาจต้องการตรวจสอบหมายเหตุในคำถามซึ่งชี้ให้เห็นว่าทำไมโซลูชันนี้จึงด้อยกว่าของ Ken
DevSolar

2
ฉันรู้ว่าคุณถามคำถามคุณเขียนคำตอบนี้และคุณยอมรับคำตอบที่ไม่ดี นั่นเป็นเหตุผลที่ฉันพูดอย่างนั้น เหตุผลที่คำตอบที่ยอมรับนั้นไม่ดีก็เพราะมันพยายามส่งอาเรย์โดยอ้างอิงซึ่งเป็นสิ่งที่คุณควรหลีกเลี่ยง นอกจากนี้ตัวอย่าง mash หลายอาร์กิวเมนต์ในสตริงเดียว หากคุณจำเป็นต้องผ่านอาร์เรย์โดยอ้างอิง bash เป็นภาษาที่ผิดเริ่มต้นด้วย แม้ว่าจะมีตัวแปร nameref ใหม่ของ bash 4.3 คุณไม่สามารถหลีกเลี่ยงการชนชื่อได้อย่างปลอดภัย (การอ้างอิงแบบวงกลม)
geirha

4
คุณสามารถผ่านหลายอาร์เรย์ได้ถ้าคุณใส่จำนวนองค์ประกอบของแต่ละอาร์เรย์ called_function "${#array[@]}" "${array[@]}" "${#array2[@]}" "${array2[@]}"ฯลฯ ... ยังมีข้อ จำกัด ที่ชัดเจน แต่จริงๆแล้วดีกว่าที่จะแก้ปัญหาในแบบที่ภาษารองรับแทนที่จะพยายามทำให้ภาษางอเป็นวิธีการทำงานที่คุณคุ้นเคยกับภาษาอื่น
geirha

1
@geirha: ดีฉันเดาว่าเราจะต้องยอมรับว่าเราไม่เห็นด้วยและคุณจะต้องให้ฉันเป็นผู้ตัดสินซึ่งคำตอบที่ตอบคำถามของฉันดีที่สุด โดยส่วนตัวแล้วฉันชอบอาร์เรย์ผ่านมากขึ้นโดยอ้างอิงอย่างไรก็ตาม (ไม่ว่าจะเป็นภาษาใดในการบันทึกการคัดลอกข้อมูล); มากขึ้นดังนั้นเมื่อทางเลือกคือการโค้งงอไปข้างหลังและผ่านขนาดอาร์เรย์เป็นพารามิเตอร์เพิ่มเติม ...
DevSolar

38

แสดงความคิดเห็นในโซลูชัน Ken Bertelson และตอบ Jan Hettich:

มันทำงานอย่างไร

takes_ary_as_arg descTable[@] optsTable[@]บรรทัดในtry_with_local_arys()ฟังก์ชั่นส่ง:

  1. นี่คือการสร้างสำเนาของdescTableและoptsTableอาร์เรย์ซึ่งสามารถเข้าถึงtakes_ary_as_argฟังก์ชันได้
  2. takes_ary_as_arg()ฟังก์ชั่นได้รับdescTable[@]และoptsTable[@]เป็นสตริงหมายความว่าและ$1 == descTable[@]$2 == optsTable[@]
  3. ในการเริ่มต้นของtakes_ary_as_arg()ฟังก์ชั่นการใช้${!parameter}ไวยากรณ์ซึ่งเรียกว่าการอ้างอิงทางอ้อมหรือบางครั้งอ้างถึงสองครั้งวิธีนี้แทนการใช้$1's คุ้มค่าเราจะใช้ค่าของการขยายมูลค่าของ$1ตัวอย่าง:

    baba=booba
    variable=baba
    echo ${variable} # baba
    echo ${!variable} # booba

    $2เช่นเดียวกันสำหรับ

  4. วางสิ่งนี้ในการargAry1=("${!1}")สร้างargAry1เป็นอาร์เรย์ (วงเล็บตาม=) ด้วยการขยายdescTable[@]เช่นเดียวกับการเขียนargAry1=("${descTable[@]}")ตรงนั้น declareมีไม่จำเป็นต้องใช้

หมายเหตุ:มันเป็นมูลค่าการกล่าวขวัญว่าเริ่มต้น array โดยใช้แบบฟอร์มวงเล็บนี้เริ่มต้นแถวใหม่ตามIFSหรือภายในสนามแยกซึ่งเป็นค่าเริ่มต้นแท็บ , การขึ้นบรรทัดใหม่และพื้นที่ ในกรณีนั้นเนื่องจากมันใช้[@]สัญกรณ์แต่ละองค์ประกอบจะถูกมองด้วยตัวเองราวกับว่าเขาถูกยกมา (ตรงกันข้าม[*])

ฉันจองกับมัน

ในBASHขอบเขตตัวแปรท้องถิ่นเป็นฟังก์ชั่นปัจจุบันและฟังก์ชั่นเด็กทุกคนเรียกว่ามันtakes_ary_as_arg()ฟังก์ชั่นนี้ "เห็น" ฟังก์ชั่น "เห็น" เหล่านั้นdescTable[@]และoptsTable[@]อาร์เรย์จึงทำงาน (ดูคำอธิบายข้างต้น)

ถ้าเป็นอย่างนั้นทำไมไม่ลองดูตัวแปรเหล่านั้นโดยตรงล่ะ? มันเหมือนกับการเขียนที่นั่น:

argAry1=("${descTable[@]}")

ดูคำอธิบายข้างต้นซึ่งเป็นเพียงสำเนาค่าอาร์เรย์ตามปัจจุบันdescTable[@]IFS

สรุป

นี่คือการผ่านในสาระสำคัญไม่มีอะไรตามค่า - ตามปกติ

ฉันยังต้องการเน้นความคิดเห็นของ Dennis Williamson ด้านบน: sparse arrays (อาร์เรย์ที่ไม่มีคีย์ทั้งหมดกำหนด - ด้วย "hole" ในนั้น) จะไม่ทำงานตามที่คาดไว้ - เราจะคลายคีย์และ "ย่อ" อาร์เรย์

ที่ถูกกล่าวว่าฉันจะเห็นค่าสำหรับลักษณะทั่วไปฟังก์ชั่นจึงสามารถรับอาร์เรย์ (หรือคัดลอก) โดยไม่ทราบชื่อ

  • สำหรับ ~ "copy": เทคนิคนี้ดีพอเพียงต้องระวังให้ดีว่าดัชนี (กุญแจ) หายไปแล้ว
  • สำหรับสำเนาจริง: เราสามารถใช้ eval สำหรับกุญแจได้ตัวอย่างเช่น:

    eval local keys=(\${!$1})

จากนั้นวนซ้ำใช้เพื่อสร้างสำเนา หมายเหตุ: ที่นี่!ไม่ได้ใช้เป็นการประเมินผลทางอ้อม / คู่ก่อนหน้านี้ แต่ในบริบทของอาร์เรย์จะส่งคืนดัชนีอาร์เรย์ (คีย์)

  • และแน่นอนถ้าเราจะผ่านdescTableและoptsTableสตริง (ไม่[@]) เราสามารถใช้อาร์เรย์ของตัวเอง (ในขณะที่โดยอ้างอิง) evalด้วย สำหรับฟังก์ชั่นทั่วไปที่ยอมรับอาร์เรย์

2
คำอธิบายที่ดีของกลไกหลังคำอธิบายของ Ken Bertelson สำหรับคำถามที่ว่า "ในกรณีนี้ทำไมไม่ลองดูตัวแปรเหล่านั้นด้วยตัวเองโดยตรง" ฉันจะตอบว่า: เพียงใช้ฟังก์ชั่นนี้ซ้ำ สมมติว่าฉันต้องเรียกใช้ฟังก์ชันด้วยArray1หลังจากนั้นด้วยการArray2ส่งชื่ออาร์เรย์ให้เป็นประโยชน์
gfrigon

คำตอบที่ดีเราต้องการคำอธิบายเพิ่มเติมเช่นนี้!
Édouard Lopez

22

ปัญหาพื้นฐานที่นี่คือผู้พัฒนาทุบตี (s) ที่ออกแบบ / ใช้งานอาร์เรย์จริง ๆ เมาสุนัข พวกเขาตัดสินใจว่านั่น${array}เป็นแค่มือสั้น ๆ${array[0]}ซึ่งเป็นความผิดพลาดที่ไม่ดี โดยเฉพาะอย่างยิ่งเมื่อคุณพิจารณาว่า${array[0]}ไม่มีความหมายและประเมินเป็นสตริงว่างถ้าประเภทอาเรย์เป็นแบบเชื่อมโยงกัน

การกำหนดอาร์เรย์จะใช้รูปแบบarray=(value1 ... valueN)ที่ค่ามีไวยากรณ์[subscript]=stringจึงกำหนดค่าโดยตรงไปยังดัชนีเฉพาะในอาร์เรย์ สิ่งนี้ทำให้สามารถมีสองประเภทของอาร์เรย์ดัชนีและ hash ดัชนี (เรียกว่าเชื่อมโยงอาร์เรย์ใน bash parlance) นอกจากนี้ยังทำให้คุณสามารถสร้างอาร์เรย์ที่จัดทำดัชนีเป็นตัวเลขได้ การออกจาก[subscript]=ส่วนนั้นเป็นเพียงช่วงสั้น ๆ สำหรับอาเรย์ที่ทำดัชนีเป็นตัวเลขเริ่มต้นด้วยดัชนีลำดับที่ 0 และเพิ่มขึ้นด้วยค่าใหม่แต่ละค่าในคำสั่งการกำหนด

ดังนั้น${array}ควรประเมินทั้งอาร์เรย์ดัชนีและทั้งหมด มันควรจะประเมินค่าผกผันของคำสั่งที่ได้รับมอบหมาย CS ที่สำคัญในปีที่สามควรรู้สิ่งนั้น ในกรณีนี้รหัสนี้จะทำงานอย่างที่คุณคาดหวัง:

declare -A foo bar
foo=${bar}

จากนั้นการส่งผ่านอาร์เรย์ตามค่าให้กับฟังก์ชั่นและการกำหนดหนึ่งอาเรย์ให้กับอีกอันหนึ่งจะใช้งานได้เมื่อเชลล์ที่เหลือของไวยากรณ์เชลล์สั่งการ แต่เนื่องจากพวกเขาทำสิ่งนี้ไม่ถูกต้องผู้ดำเนินการที่ได้รับมอบหมาย=จึงไม่สามารถทำงานให้กับอาร์เรย์ได้และไม่สามารถส่งผ่านค่าอาร์เรย์ไปยังฟังก์ชันหรือไปยัง subshells หรือเอาต์พุตโดยทั่วไป ( echo ${array}) โดยไม่ต้องใช้รหัสเพื่อให้เคี้ยวทั้งหมด

ดังนั้นถ้ามันถูกต้องแล้วตัวอย่างต่อไปนี้จะแสดงให้เห็นว่าการใช้ประโยชน์ของอาร์เรย์ใน bash นั้นดีกว่ามาก:

simple=(first=one second=2 third=3)
echo ${simple}

ผลลัพธ์ที่ได้ควรเป็น:

(first=one second=2 third=3)

จากนั้นอาร์เรย์สามารถใช้ตัวดำเนินการกำหนดค่าและส่งผ่านค่าไปยังฟังก์ชันและแม้แต่เชลล์สคริปต์อื่น ๆ จัดเก็บได้อย่างง่ายดายโดยการส่งออกไปยังไฟล์และโหลดจากไฟล์ลงในสคริปต์ได้อย่างง่ายดาย

declare -A foo
read foo <file

อนิจจาเราได้รับการลดลงโดยทีมพัฒนาทุบตีสุดยอดเป็นอย่างอื่น

ดังนั้นการส่งอาเรย์ไปยังฟังก์ชั่นจึงมีเพียงตัวเลือกเดียวเท่านั้นและนั่นคือการใช้คุณสมบัติ nameref:

function funky() {
    local -n ARR

    ARR=$1
    echo "indexes: ${!ARR[@]}"
    echo "values: ${ARR[@]}"
}

declare -A HASH

HASH=([foo]=bar [zoom]=fast)
funky HASH # notice that I'm just passing the word 'HASH' to the function

จะส่งผลให้ผลลัพธ์ต่อไปนี้:

indexes: foo zoom
values: bar fast

เนื่องจากสิ่งนี้ผ่านการอ้างอิงคุณยังสามารถกำหนดให้กับอาร์เรย์ในฟังก์ชันได้ ใช่อาร์เรย์ที่ถูกอ้างอิงจะต้องมีขอบเขตทั่วโลก แต่นั่นไม่ควรมีข้อตกลงที่ใหญ่เกินไปเมื่อพิจารณาว่านี่เป็นเชลล์สคริปต์ ในการส่งอาเรย์แบบดัชนีที่เชื่อมโยงหรือกระจัดกระจายโดยค่าไปยังฟังก์ชันต้องใช้การโยนดัชนีทั้งหมดและค่าลงในรายการอาร์กิวเมนต์ (ไม่มีประโยชน์มากเกินไปหากเป็นอาร์เรย์ขนาดใหญ่) เป็นสตริงเดี่ยวดังนี้

funky "${!array[*]}" "${array[*]}"

จากนั้นเขียนโค้ดจำนวนมากไว้ในฟังก์ชั่นเพื่อประกอบอาร์เรย์อีกครั้ง


1
วิธีแก้ปัญหาการใช้local -nดีกว่าและทันสมัยกว่าคำตอบที่ยอมรับ วิธีนี้จะทำงานกับตัวแปรประเภทใดก็ได้ local -n ARR=${1}ตัวอย่างที่ระบุไว้ในคำตอบนี้สามารถลงไป อย่างไรก็ตาม-nตัวเลือกสำหรับlocal/ declareมีให้เฉพาะใน Bash เวอร์ชัน 4.3 ขึ้นไป
richardjsimkins

นี่เป็นสิ่งที่ดี! gotcha ขนาดเล็ก: ถ้าคุณส่งผ่านตัวแปรที่มีชื่อเดียวกับข้อโต้แย้งในท้องถิ่นฟังก์ชั่นของคุณ (เช่นfunky ARR) เปลือกจะให้คำเตือนเพราะพื้นฟังก์ชันจะพยายามที่จะทำcircular name reference การอภิปรายที่local -n ARR=ARRดีเกี่ยวกับหัวข้อนี้
Gene Pavlovsky

5

คำตอบของ DevSolar มีอยู่ช่วงหนึ่งที่ฉันไม่เข้าใจ (บางทีเขาอาจมีเหตุผลเฉพาะที่จะทำเช่นนั้น แต่ฉันไม่สามารถคิดได้): เขาตั้งค่าอาร์เรย์จากองค์ประกอบพารามิเตอร์ตำแหน่งตามองค์ประกอบซ้ำ

approuch ง่ายขึ้นจะเป็น

called_function()
{
  ...
  # do everything like shown by DevSolar
  ...

  # now get a copy of the positional parameters
  local_array=("$@")
  ...
}

1
เหตุผลของฉันสำหรับการไม่ทำเช่นนั้นคือฉันไม่ได้เล่นกับอาร์เรย์ bash เลยจนกระทั่งสองสามวันที่ผ่านมา ก่อนหน้านี้ฉันจะเปลี่ยนเป็น Perl ถ้ามันซับซ้อนตัวเลือกที่ฉันไม่มีในงานปัจจุบันของฉัน ขอบคุณสำหรับคำใบ้!
DevSolar


3

วิธีง่ายๆในการผ่านหลายอาร์เรย์เนื่องจากพารามิเตอร์คือการใช้สตริงที่คั่นด้วยอักขระ คุณสามารถเรียกสคริปต์ของคุณเช่นนี้:

./myScript.sh "value1;value2;value3" "somethingElse" "value4;value5" "anotherOne"

จากนั้นคุณสามารถแยกมันในรหัสของคุณดังนี้:

myArray=$1
IFS=';' read -a myArray <<< "$myArray"

myOtherArray=$3
IFS=';' read -a myOtherArray <<< "$myOtherArray"

ด้วยวิธีนี้คุณสามารถผ่านหลาย ๆ อาร์เรย์เป็นพารามิเตอร์ได้และไม่จำเป็นต้องเป็นพารามิเตอร์สุดท้าย


1

อันนี้ใช้ได้แม้กับช่องว่าง:

format="\t%2s - %s\n"

function doAction
{
  local_array=("$@")
  for (( i = 0 ; i < ${#local_array[@]} ; i++ ))
    do
      printf "${format}" $i "${local_array[$i]}"
  done
  echo -n "Choose: "
  option=""
  read -n1 option
  echo ${local_array[option]}
  return
}

#the call:
doAction "${tools[@]}"

2
ฉันสงสัยว่าจุดนี้อยู่ที่ใด นี่เป็นเพียงการโต้เถียงแบบปกติ ไวยากรณ์ "$ @" ถูกสร้างมาเพื่อใช้กับช่องว่าง: "$ @" เทียบเท่ากับ "$ 1" "$ 2" ...
Andreas Spindler

ฉันสามารถส่ง 2 อาร์เรย์ไปยังฟังก์ชันได้หรือไม่
pihentagy

1

ด้วยเทคนิคเล็กน้อยคุณสามารถส่งพารามิเตอร์ที่มีชื่อไปยังฟังก์ชั่นพร้อมกับอาร์เรย์ได้

วิธีที่ฉันพัฒนาให้คุณเข้าถึงพารามิเตอร์ที่ส่งผ่านไปยังฟังก์ชันดังนี้:

testPassingParams() {

    @var hello
    l=4 @array anArrayWithFourElements
    l=2 @array anotherArrayWithTwo
    @var anotherSingle
    @reference table   # references only work in bash >=4.3
    @params anArrayOfVariedSize

    test "$hello" = "$1" && echo correct
    #
    test "${anArrayWithFourElements[0]}" = "$2" && echo correct
    test "${anArrayWithFourElements[1]}" = "$3" && echo correct
    test "${anArrayWithFourElements[2]}" = "$4" && echo correct
    # etc...
    #
    test "${anotherArrayWithTwo[0]}" = "$6" && echo correct
    test "${anotherArrayWithTwo[1]}" = "$7" && echo correct
    #
    test "$anotherSingle" = "$8" && echo correct
    #
    test "${table[test]}" = "works"
    table[inside]="adding a new value"
    #
    # I'm using * just in this example:
    test "${anArrayOfVariedSize[*]}" = "${*:10}" && echo correct
}

fourElements=( a1 a2 "a3 with spaces" a4 )
twoElements=( b1 b2 )
declare -A assocArray
assocArray[test]="works"

testPassingParams "first" "${fourElements[@]}" "${twoElements[@]}" "single with spaces" assocArray "and more... " "even more..."

test "${assocArray[inside]}" = "adding a new value"

กล่าวอีกนัยหนึ่งไม่เพียง แต่คุณสามารถเรียกพารามิเตอร์ของคุณตามชื่อของพวกเขา (ซึ่งประกอบไปด้วยแกนกลางที่อ่านได้มากขึ้น) คุณสามารถส่งผ่านอาร์เรย์ได้ (และการอ้างอิงถึงตัวแปร - คุณลักษณะนี้ใช้ได้เฉพาะในทุบตี 4.3)! นอกจากนี้ตัวแปรที่แมปนั้นอยู่ในขอบเขตของท้องถิ่นเช่นเดียวกับ $ 1 (และอื่น ๆ )

รหัสที่ทำให้งานนี้ค่อนข้างเบาและใช้ได้ทั้งใน bash 3 และ bash 4 (นี่เป็นรุ่นเดียวที่ฉันทดสอบด้วย) หากคุณสนใจเทคนิคเพิ่มเติมเช่นนี้ที่พัฒนาด้วย bash ที่ดีกว่าและง่ายกว่าคุณสามารถดูBash Infinity Frameworkของฉันได้รหัสด้านล่างนี้ได้รับการพัฒนาเพื่อจุดประสงค์นั้น

Function.AssignParamLocally() {
    local commandWithArgs=( $1 )
    local command="${commandWithArgs[0]}"

    shift

    if [[ "$command" == "trap" || "$command" == "l="* || "$command" == "_type="* ]]
    then
        paramNo+=-1
        return 0
    fi

    if [[ "$command" != "local" ]]
    then
        assignNormalCodeStarted=true
    fi

    local varDeclaration="${commandWithArgs[1]}"
    if [[ $varDeclaration == '-n' ]]
    then
        varDeclaration="${commandWithArgs[2]}"
    fi
    local varName="${varDeclaration%%=*}"

    # var value is only important if making an object later on from it
    local varValue="${varDeclaration#*=}"

    if [[ ! -z $assignVarType ]]
    then
        local previousParamNo=$(expr $paramNo - 1)

        if [[ "$assignVarType" == "array" ]]
        then
            # passing array:
            execute="$assignVarName=( \"\${@:$previousParamNo:$assignArrLength}\" )"
            eval "$execute"
            paramNo+=$(expr $assignArrLength - 1)

            unset assignArrLength
        elif [[ "$assignVarType" == "params" ]]
        then
            execute="$assignVarName=( \"\${@:$previousParamNo}\" )"
            eval "$execute"
        elif [[ "$assignVarType" == "reference" ]]
        then
            execute="$assignVarName=\"\$$previousParamNo\""
            eval "$execute"
        elif [[ ! -z "${!previousParamNo}" ]]
        then
            execute="$assignVarName=\"\$$previousParamNo\""
            eval "$execute"
        fi
    fi

    assignVarType="$__capture_type"
    assignVarName="$varName"
    assignArrLength="$__capture_arrLength"
}

Function.CaptureParams() {
    __capture_type="$_type"
    __capture_arrLength="$l"
}

alias @trapAssign='Function.CaptureParams; trap "declare -i \"paramNo+=1\"; Function.AssignParamLocally \"\$BASH_COMMAND\" \"\$@\"; [[ \$assignNormalCodeStarted = true ]] && trap - DEBUG && unset assignVarType && unset assignVarName && unset assignNormalCodeStarted && unset paramNo" DEBUG; '
alias @param='@trapAssign local'
alias @reference='_type=reference @trapAssign local -n'
alias @var='_type=var @param'
alias @params='_type=params @param'
alias @array='_type=array @param'

1

เพียงเพิ่มคำตอบที่ยอมรับเพราะฉันพบว่ามันไม่ได้ผลถ้าเนื้อหาอาเรย์เป็นที่ชอบ:

RUN_COMMANDS=(
  "command1 param1... paramN"
  "command2 param1... paramN"
)

ในกรณีนี้สมาชิกแต่ละคนของอาเรย์จะได้รับการแยกดังนั้นอาเรย์ที่ฟังก์ชั่นเห็นนั้นเทียบเท่ากับ

RUN_COMMANDS=(
    "command1"
    "param1"
     ...
    "command2"
    ...
)

เพื่อให้กรณีนี้ทำงานได้อย่างที่ฉันพบคือการส่งชื่อตัวแปรไปยังฟังก์ชันแล้วใช้ eval:

function () {
    eval 'COMMANDS=( "${'"$1"'[@]}" )'
    for COMMAND in "${COMMANDS[@]}"; do
        echo $COMMAND
    done
}

function RUN_COMMANDS

แค่ 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

ฉันแน่ใจว่าคนที่สามารถเกิดขึ้นกับการดำเนินงานของ clearner ความคิด แต่ฉันได้พบนี้จะเป็นทางออกที่ดีกว่าที่ผ่านอาร์เรย์เป็นแล้วการเข้าถึงภายในโดยใช้"{array[@]"} array_inside=("$@")สิ่งนี้จะซับซ้อนเมื่อมีตำแหน่ง / getoptsพารามิเตอร์อื่น ๆ ในกรณีเหล่านี้ฉันต้องพิจารณาก่อนแล้วจึงลบพารามิเตอร์ที่ไม่เกี่ยวข้องกับอาร์เรย์โดยใช้การรวมshiftและการกำจัดองค์ประกอบอาร์เรย์

มุมมองที่พิถีพิถันน่าจะมองว่าวิธีนี้เป็นการละเมิดภาษา แต่ในทางปฏิบัติวิธีการนี้ช่วยให้ฉันเศร้าใจมาก ในหัวข้อที่เกี่ยวข้องฉันยังใช้evalเพื่อกำหนดอาร์เรย์ที่สร้างขึ้นภายในให้กับตัวแปรที่ตั้งชื่อตามพารามิเตอร์ที่target_varnameฉันส่งไปยังฟังก์ชัน:

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

หวังว่านี่จะช่วยใครซักคน


0

สิ่งที่ต้องการ : ฟังก์ชั่นเพื่อค้นหาสตริงในอาร์เรย์
นี่เป็นวิธีแก้ปัญหาที่ง่ายขึ้นเล็กน้อยของ DevSolar โดยใช้อาร์กิวเมนต์ที่ส่งผ่านแทนที่จะคัดลอก

myarray=('foobar' 'foxbat')

function isInArray() {
  local item=$1
  shift
  for one in $@; do
    if [ $one = $item ]; then
      return 0   # found
    fi
  done
  return 1       # not found
}

var='foobar'
if isInArray $var ${myarray[@]}; then
  echo "$var found in array"
else
  echo "$var not found in array"
fi 

0

คำตอบสั้น ๆ ของฉันคือ:

function display_two_array {
    local arr1=$1
    local arr2=$2
    for i in $arr1
    do
       "arrary1: $i"
    done
    
    for i in $arr2
    do
       "arrary2: $i"
    done
}

test_array=(1 2 3 4 5)
test_array2=(7 8 9 10 11)

display_two_array "${test_array[*]}" "${test_array2[*]}"
ควรสังเกตว่า${test_array[*]}และ${test_array2[*]}ควรล้อมรอบด้วย "" มิฉะนั้นคุณจะล้มเหลว


ตัวอย่างของคุณไม่ถูกต้องเพราะไม่สมบูรณ์ กรุณาให้รหัสเต็มของสคริปต์
Dennis VR
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.