ลบองค์ประกอบออกจากอาร์เรย์ Bash


116

ฉันต้องการลบองค์ประกอบออกจากอาร์เรย์ใน bash shell โดยทั่วไปฉันเพียงแค่ทำ:

array=("${(@)array:#<element to remove>}")

น่าเสียดายที่องค์ประกอบที่ฉันต้องการลบเป็นตัวแปรดังนั้นฉันจึงไม่สามารถใช้คำสั่งก่อนหน้านี้ได้ ตัวอย่างด้านล่าง:

array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}

ความคิดใด ๆ ?


เปลือกไหน? zshตัวอย่างของคุณมีลักษณะเหมือน
chepner

array=( ${array[@]/$delete} )ทำงานตามที่คาดไว้ใน Bash คุณพลาดเพียงแค่=?
Ken Sharp

1
@Ken นั่นไม่ใช่สิ่งที่ต้องการ - มันจะลบการจับคู่ใด ๆ ออกจากแต่ละสตริงและปล่อยสตริงว่างไว้ในอาร์เรย์ซึ่งตรงกับสตริงทั้งหมด
Toby Speight

คำตอบ:


165

ต่อไปนี้ทำงานได้ตามที่คุณต้องการbashและzsh:

$ array=(pluto pippo)
$ delete=pluto
$ echo ${array[@]/$delete}
pippo
$ array=( "${array[@]/$delete}" ) #Quotes when working with strings

หากจำเป็นต้องลบมากกว่าหนึ่งองค์ประกอบ:

...
$ delete=(pluto pippo)
for del in ${delete[@]}
do
   array=("${array[@]/$del}") #Quotes when working with strings
done

ข้อแม้

เทคนิคนี้จะลบคำนำหน้าการจับคู่$deleteออกจากองค์ประกอบจริง ๆ ไม่จำเป็นต้องเป็นองค์ประกอบทั้งหมด

ปรับปรุง

ในการลบรายการที่แน่นอนจริงๆคุณต้องเดินผ่านอาร์เรย์เปรียบเทียบเป้าหมายกับแต่ละองค์ประกอบและใช้unsetเพื่อลบรายการที่ตรงกันทั้งหมด

array=(pluto pippo bob)
delete=(pippo)
for target in "${delete[@]}"; do
  for i in "${!array[@]}"; do
    if [[ ${array[i]} = $target ]]; then
      unset 'array[i]'
    fi
  done
done

โปรดทราบว่าหากคุณทำเช่นนี้และองค์ประกอบอย่างน้อยหนึ่งรายการถูกลบออกดัชนีจะไม่เป็นลำดับของจำนวนเต็มต่อเนื่องอีกต่อไป

$ declare -p array
declare -a array=([0]="pluto" [2]="bob")

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

หากช่องว่างเป็นปัญหาคุณต้องสร้างอาร์เรย์ใหม่เพื่อเติมเต็มช่องว่าง:

for i in "${!array[@]}"; do
    new_array+=( "${array[i]}" )
done
array=("${new_array[@]}")
unset new_array

43
เพิ่งรู้ว่า: $ array=(sun sunflower) $ delete=(sun) $ echo ${array[@]/$delete}ผลลัพธ์ในflower
bernstein

12
ทราบว่านี้เป็นจริงทำทดแทนดังนั้นหากอาร์เรย์เป็นสิ่งที่ต้องการแล้วคุณจะจบลงด้วย(pluto1 pluto2 pippo) (1 2 pippo)
haridsv

5
โปรดใช้ความระมัดระวังในการใช้สิ่งนี้ในการวนซ้ำเพราะคุณจะจบลงด้วยองค์ประกอบว่างที่องค์ประกอบที่ถูกลบไปนั้น เพื่อความมีสติคุณสามารถทำสิ่งต่างๆเช่นfor element in "${array[@]}" do if [[ $element ]]; then echo ${element} fi done
Joel B

2
ดังนั้นจะลบเฉพาะองค์ประกอบที่ตรงกันได้อย่างไร?
UmaN

4
หมายเหตุ: อาจตั้งค่าตามลำดับเป็นค่าอะไรก็ได้ แต่องค์ประกอบจะยังคงอยู่ในอาร์เรย์
phil294

29

คุณสามารถสร้างอาร์เรย์ใหม่โดยไม่มีองค์ประกอบที่ไม่ต้องการจากนั้นกำหนดกลับไปยังอาร์เรย์เก่า สิ่งนี้ใช้ได้ในbash:

array=(pluto pippo)
new_array=()
for value in "${array[@]}"
do
    [[ $value != pluto ]] && new_array+=($value)
done
array=("${new_array[@]}")
unset new_array

สิ่งนี้ให้ผล:

echo "${array[@]}"
pippo

14

นี่เป็นวิธีที่ตรงที่สุดในการยกเลิกการตั้งค่าหากคุณรู้ว่าเป็นตำแหน่ง

$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2

3
ลองecho ${array[1]}คุณจะได้รับสตริงว่าง และเพื่อให้ได้ที่คุณต้องทำthree echo ${array[2]}ดังนั้นจึงunsetไม่ใช่กลไกที่ถูกต้องในการลบองค์ประกอบใน bash array
rashok

@rashok ไม่${array[1]+x}เป็นสตริงว่างจึงarray[1]ไม่ได้ตั้งค่า unsetไม่เปลี่ยนดัชนีขององค์ประกอบที่เหลือ ไม่จำเป็นต้องอ้างถึงอาร์กิวเมนต์สำหรับ unset วิธีที่จะทำลายองค์ประกอบอาร์เรย์อธิบายไว้ในคู่มือการทุบตี
jarno

@rashok ฉันไม่เห็นว่าทำไมไม่ คุณไม่สามารถสรุปได้ว่า${array[1]}มีอยู่เพียงเพราะขนาดเป็น 2 ${!array[@]}ถ้าคุณต้องการที่ดัชนีการตรวจสอบ
Daniel C. Sobral

4

นี่คือโซลูชันบรรทัดเดียวพร้อม mapfile:

$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "<regexp>")

ตัวอย่าง:

$ arr=("Adam" "Bob" "Claire"$'\n'"Smith" "David" "Eve" "Fred")

$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"

Size: 6 Contents: Adam Bob Claire
Smith David Eve Fred

$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "^Claire\nSmith$")

$ echo "Size: ${#arr[*]} Contents: ${arr[*]}"

Size: 5 Contents: Adam Bob David Eve Fred

วิธีนี้ช่วยให้เกิดความยืดหยุ่นอย่างมากโดยการแก้ไข / แลกเปลี่ยนคำสั่ง grep และไม่ปล่อยให้สตริงว่างในอาร์เรย์


1
โปรดใช้printf '%s\n' "${array[@]}"แทนสิ่งที่น่าเกลียดIFS/ echoสิ่งนั้น
gniourf_gniourf

โปรดทราบว่าสิ่งนี้ล้มเหลวกับฟิลด์ที่มีการขึ้นบรรทัดใหม่
gniourf_gniourf

@Socowi คุณไม่ถูกต้องอย่างน้อยก็ใน bash 4.4.19 -d $'\0'ทำงานได้ดีอย่างสมบูรณ์ในขณะที่-dไม่มีการโต้แย้งไม่ได้
Niklas Holm

อ่าใช่ฉันผสมมัน ขอโทษ สิ่งที่ผมหมายถึงคือ: -d $'\0'เป็นเช่นเดียวกับหรือเพียงแค่-d $'\0 something' -d ''
Socowi

ไม่เจ็บที่จะใช้$'\0'เพื่อความชัดเจน
Niklas Holm

4

คำตอบนี้ใช้เฉพาะในกรณีของการลบหลายค่าจากอาร์เรย์ขนาดใหญ่ซึ่งประสิทธิภาพเป็นสิ่งสำคัญ

โซลูชันที่ได้รับการโหวตมากที่สุดคือ (1) การทดแทนรูปแบบบนอาร์เรย์หรือ (2) การทำซ้ำบนองค์ประกอบอาร์เรย์ อย่างแรกรวดเร็ว แต่สามารถจัดการได้เฉพาะองค์ประกอบที่มีคำนำหน้าแตกต่างกันอย่างที่สองมี O (n * k), n = ขนาดอาร์เรย์, k = องค์ประกอบที่จะลบ แอสโซซิเอทีฟอาร์เรย์เป็นคุณสมบัติใหม่ที่สัมพันธ์กันและอาจไม่เหมือนกันเมื่อโพสต์คำถาม

สำหรับกรณีการจับคู่แบบตรงทั้งหมดที่มีขนาดใหญ่ n และ k เป็นไปได้ที่จะปรับปรุงประสิทธิภาพจาก O (n k) ถึง O (n + k log (k)) ในทางปฏิบัติ O (n) สมมติว่า k ต่ำกว่า n มาก ความเร็วส่วนใหญ่ขึ้นอยู่กับการใช้ Associative array เพื่อระบุรายการที่จะลบออก

ประสิทธิภาพ (ขนาด n-array, ค่า k ที่จะลบ) ประสิทธิภาพวัดวินาทีของเวลาของผู้ใช้

   N     K     New(seconds) Current(seconds)  Speedup
 1000   10     0.005        0.033             6X
10000   10     0.070        0.348             5X
10000   20     0.070        0.656             9X
10000    1     0.043        0.050             -7%

ตามที่คาดไว้การcurrentแก้ปัญหาเป็นเส้นตรงถึง N * K และfastวิธีการแก้ปัญหานั้นเป็นเส้นตรงถึง K โดยมีค่าคงที่ต่ำกว่ามาก การfastแก้ปัญหาช้ากว่าเล็กน้อยcurrentเมื่อเทียบกับโซลูชันเมื่อ k = 1 เนื่องจากการตั้งค่าเพิ่มเติม

โซลูชัน 'รวดเร็ว': array = list of input, delete = list of values ​​to remove.

        declare -A delk
        for del in "${delete[@]}" ; do delk[$del]=1 ; done
                # Tag items to remove, based on
        for k in "${!array[@]}" ; do
                [ "${delk[${array[$k]}]-}" ] && unset 'array[k]'
        done
                # Compaction
        array=("${array[@]}")

เปรียบเทียบกับ currentโซลูชันจากคำตอบที่ได้รับการโหวตมากที่สุด

    for target in "${delete[@]}"; do
        for i in "${!array[@]}"; do
            if [[ ${array[i]} = $target ]]; then
                unset 'array[i]'
            fi
        done
    done
    array=("${array[@]}")

3

นี่คือฟังก์ชั่นเล็ก ๆ น้อย ๆ (อาจเป็นเฉพาะการทุบตี) ที่เกี่ยวข้องกับตัวแปร bash indirection และunset; เป็นวิธีแก้ปัญหาทั่วไปที่ไม่เกี่ยวข้องกับการแทนที่ข้อความหรือการละทิ้งองค์ประกอบที่ว่างเปล่าและไม่มีปัญหากับการอ้าง / เว้นวรรค ฯลฯ

delete_ary_elmt() {
  local word=$1      # the element to search for & delete
  local aryref="$2[@]" # a necessary step since '${!$2[@]}' is a syntax error
  local arycopy=("${!aryref}") # create a copy of the input array
  local status=1
  for (( i = ${#arycopy[@]} - 1; i >= 0; i-- )); do # iterate over indices backwards
    elmt=${arycopy[$i]}
    [[ $elmt == $word ]] && unset "$2[$i]" && status=0 # unset matching elmts in orig. ary
  done
  return $status # return 0 if something was deleted; 1 if not
}

array=(a 0 0 b 0 0 0 c 0 d e 0 0 0)
delete_ary_elmt 0 array
for e in "${array[@]}"; do
  echo "$e"
done

# prints "a" "b" "c" "d" in lines

ใช้มันเหมือนdelete_ary_elmt ELEMENT ARRAYNAMEไม่มี$เครื่องหมายใด ๆ สลับ== $wordสำหรับ== $word*สำหรับคำนำหน้าที่ตรงกัน ใช้${elmt,,} == ${word,,}สำหรับการจับคู่แบบไม่คำนึงถึงตัวพิมพ์เล็กและใหญ่ ฯลฯ สิ่งที่[[สนับสนุนbash

ทำงานโดยการกำหนดดัชนีของอาร์เรย์อินพุตและวนซ้ำไปข้างหลัง (ดังนั้นการลบองค์ประกอบจะไม่ทำให้ลำดับการวนซ้ำ) x=1; varname=x; echo ${!varname} # prints "1"ที่จะได้รับดัชนีที่คุณจำเป็นต้องเข้าถึงอาร์เรย์การป้อนข้อมูลโดยใช้ชื่อซึ่งสามารถทำได้ผ่านทางตัวแปรทุบตีร้าย

คุณไม่สามารถเข้าถึงอาร์เรย์ตามชื่อเช่นaryname=a; echo "${$aryname[@]}นี้ได้ซึ่งจะทำให้คุณมีข้อผิดพลาด คุณไม่สามารถทำได้สิ่งaryname=a; echo "${!aryname[@]}"นี้จะให้ดัชนีของตัวแปรaryname(แม้ว่าจะไม่ใช่อาร์เรย์) สิ่งที่ทำงานได้คืออะไรaryref="a[@]"; echo "${!aryref}"ซึ่งจะพิมพ์องค์ประกอบของอาร์เรย์aโดยเก็บรักษาคำพูดเชลล์และช่องว่างไว้เหมือนecho "${a[@]}"กัน แต่สิ่งนี้ใช้ได้กับการพิมพ์องค์ประกอบของอาร์เรย์เท่านั้นไม่ใช่สำหรับการพิมพ์ความยาวหรือดัชนี ( aryref="!a[@]"หรือaryref="#a[@]"หรือ"${!!aryref}"หรือ"${#!aryref}"ทั้งหมดล้มเหลว)

ดังนั้นฉันจึงคัดลอกอาร์เรย์ดั้งเดิมตามชื่อของมันผ่าน bash indirection และรับดัชนีจากสำเนา ในการวนซ้ำดัชนีในทางกลับกันฉันใช้สไตล์ C สำหรับการวนซ้ำ ฉันสามารถทำได้โดยการเข้าถึงดัชนีผ่าน${!arycopy[@]}และย้อนกลับด้วยtacซึ่งเป็นสิ่งcatที่หมุนรอบลำดับบรรทัดอินพุต

โซลูชันฟังก์ชันที่ไม่มีทิศทางตัวแปรอาจต้องเกี่ยวข้องevalซึ่งอาจจะปลอดภัยหรือไม่ที่จะใช้ในสถานการณ์นั้น (ฉันไม่สามารถบอกได้)


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

2

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

ARRAY=(one two onetwo three four threefour "one six")
TO_REMOVE=(one four)

TEMP_ARRAY=()
for pkg in "${ARRAY[@]}"; do
    for remove in "${TO_REMOVE[@]}"; do
        KEEP=true
        if [[ ${pkg} == ${remove} ]]; then
            KEEP=false
            break
        fi
    done
    if ${KEEP}; then
        TEMP_ARRAY+=(${pkg})
    fi
done
ARRAY=("${TEMP_ARRAY[@]}")
unset TEMP_ARRAY

สิ่งนี้จะส่งผลให้อาร์เรย์ประกอบด้วย: (two onetwo three threefour "one six")


2

หากใครพบว่าตัวเองอยู่ในตำแหน่งที่ต้องจำค่า set -e หรือ set -x และสามารถกู้คืนได้โปรดดูส่วนสำคัญนี้ซึ่งใช้โซลูชันการลบอาร์เรย์แรกเพื่อจัดการสแต็กของตัวเอง:

https://gist.github.com/kigster/94799325e39d2a227ef89676eed44cc6


1

คำตอบบางส่วนเท่านั้น

เพื่อลบรายการแรกในอาร์เรย์

unset 'array[0]'

เพื่อลบรายการสุดท้ายในอาร์เรย์

unset 'array[-1]'

@gniourf_gniourf unsetไม่มีความจำเป็นต้องใช้คำพูดสำหรับอาร์กิวเมนต์ของ
jarno

2
@jarno: ต้องใช้เครื่องหมายคำพูดเหล่านี้: หากคุณมีไฟล์ที่ตั้งชื่อarray0อยู่ในไดเร็กทอรีปัจจุบันเนื่องจากarray[0]เป็น glob ก่อนอื่นจะขยายไปยังarray0คำสั่ง unset
gniourf_gniourf

@gniourf_gniourf คุณถูกต้อง สิ่งนี้ควรได้รับการแก้ไขในคู่มืออ้างอิง Bashที่ในปัจจุบันระบุว่า "unset name [subscript] ทำลายองค์ประกอบอาร์เรย์ที่ดัชนีห้อย"
jarno

1

การใช้ unset

ในการลบองค์ประกอบที่ดัชนีเฉพาะเราสามารถใช้unsetแล้วคัดลอกไปยังอาร์เรย์อื่น เพียงแค่unsetไม่จำเป็นต้องใช้ในกรณีนี้ เนื่องจากunsetไม่ได้ลบองค์ประกอบเพียงแค่ตั้งค่าสตริงว่างให้กับดัชนีเฉพาะในอาร์เรย์

declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
unset 'arr[1]'
declare -a arr2=()
i=0
for element in "${arr[@]}"
do
    arr2[$i]=$element
    ((++i))
done
echo "${arr[@]}"
echo "1st val is ${arr[1]}, 2nd val is ${arr[2]}"
echo "${arr2[@]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"

เอาต์พุตคือ

aa cc dd ee
1st val is , 2nd val is cc
aa cc dd ee
1st val is cc, 2nd val is dd

การใช้ :<idx>

เราสามารถลบชุดขององค์ประกอบบางอย่างได้โดยใช้:<idx>ด้วย ตัวอย่างเช่นหากเราต้องการลบองค์ประกอบที่ 1 เราสามารถใช้:1ดังที่กล่าวไว้ด้านล่าง

declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
arr2=("${arr[@]:1}")
echo "${arr2[@]}"
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"

เอาต์พุตคือ

bb cc dd ee
1st val is cc, 2nd val is dd

0

POSIX เชลล์สคริปต์ไม่มีอาร์เรย์

ดังนั้นส่วนใหญ่อาจคุณกำลังใช้ภาษาที่เฉพาะเจาะจงเช่นbashหอย Korn zshหรือ

ดังนั้นคำถามของคุณ ณ ตอนนี้จึงไม่สามารถตอบได้

อาจจะเหมาะกับคุณ:

unset array[$delete]

2
สวัสดีฉันใช้ bash shell atm และ "$ delete" ไม่ใช่ตำแหน่งขององค์ประกอบ แต่เป็นสตริงเอง ดังนั้นฉันไม่คิดว่า "unset" จะได้ผล
Alex

0

อันที่จริงฉันเพิ่งสังเกตว่าไวยากรณ์ของเชลล์มีลักษณะการทำงานในตัวที่ช่วยให้สามารถสร้างอาร์เรย์ใหม่ได้ง่ายเมื่อตามที่ระบุไว้ในคำถามควรลบรายการออก

# let's set up an array of items to consume:
x=()
for (( i=0; i<10; i++ )); do
    x+=("$i")
done

# here, we consume that array:
while (( ${#x[@]} )); do
    i=$(( $RANDOM % ${#x[@]} ))
    echo "${x[i]} / ${x[@]}"
    x=("${x[@]:0:i}" "${x[@]:i+1}")
done

สังเกตว่าเราสร้างอาร์เรย์โดยใช้x+=()ไวยากรณ์ของ bash ได้อย่างไร?

คุณสามารถเพิ่มได้มากกว่าหนึ่งรายการโดยเนื้อหาของอาร์เรย์อื่น ๆ ทั้งหมดพร้อมกัน


0

http://wiki.bash-hackers.org/syntax/pe#substring_removal

$ {PARAMETER # PATTERN} # ลบออกจากจุดเริ่มต้น

$ {PARAMETER ## PATTERN} # ลบออกจากจุดเริ่มต้นการจับคู่แบบโลภ

$ {PARAMETER% PATTERN} # ลบออกจากท้าย

$ {PARAMETER %% PATTERN} # ลบออกจากตอนท้ายการจับคู่แบบโลภ

ในการดำเนินการลบองค์ประกอบแบบเต็มคุณต้องทำคำสั่ง unset ด้วยคำสั่ง if หากคุณไม่สนใจที่จะลบคำนำหน้าออกจากตัวแปรอื่น ๆ หรือเกี่ยวกับการรองรับช่องว่างในอาร์เรย์คุณก็สามารถวางเครื่องหมายคำพูดและลืมเกี่ยวกับการวนซ้ำได้

ดูตัวอย่างด้านล่างสำหรับวิธีต่างๆในการล้างอาร์เรย์

options=("foo" "bar" "foo" "foobar" "foo bar" "bars" "bar")

# remove bar from the start of each element
options=("${options[@]/#"bar"}")
# options=("foo" "" "foo" "foobar" "foo bar" "s" "")

# remove the complete string "foo" in a for loop
count=${#options[@]}
for ((i = 0; i < count; i++)); do
   if [ "${options[i]}" = "foo" ] ; then
      unset 'options[i]'
   fi
done
# options=(  ""   "foobar" "foo bar" "s" "")

# remove empty options
# note the count variable can't be recalculated easily on a sparse array
for ((i = 0; i < count; i++)); do
   # echo "Element $i: '${options[i]}'"
   if [ -z "${options[i]}" ] ; then
      unset 'options[i]'
   fi
done
# options=("foobar" "foo bar" "s")

# list them with select
echo "Choose an option:"
PS3='Option? '
select i in "${options[@]}" Quit
 do
    case $i in 
       Quit) break ;;
       *) echo "You selected \"$i\"" ;;
    esac
 done

เอาท์พุต

Choose an option:
1) foobar
2) foo bar
3) s
4) Quit
Option? 

หวังว่าจะช่วยได้


0

ใน ZSH สิ่งนี้ง่ายมาก (โปรดทราบว่าสิ่งนี้ใช้ไวยากรณ์ที่เข้ากันได้กับ bash มากกว่าที่จำเป็นเพื่อความง่ายในการทำความเข้าใจ):

# I always include an edge case to make sure each element
# is not being word split.
start=(one two three 'four 4' five)
work=(${(@)start})

idx=2
val=${work[idx]}

# How to remove a single element easily.
# Also works for associative arrays (at least in zsh)
work[$idx]=()

echo "Array size went down by one: "
[[ $#work -eq $(($#start - 1)) ]] && echo "OK"

echo "Array item "$val" is now gone: "
[[ -z ${work[(r)$val]} ]] && echo OK

echo "Array contents are as expected: "
wanted=("${start[@]:0:1}" "${start[@]:2}")
[[ "${(j.:.)wanted[@]}" == "${(j.:.)work[@]}" ]] && echo "OK"

echo "-- array contents: start --"
print -l -r -- "-- $#start elements" ${(@)start}
echo "-- array contents: work --"
print -l -r -- "-- $#work elements" "${work[@]}"

ผล:

Array size went down by one:
OK
Array item two is now gone:
OK
Array contents are as expected:
OK
-- array contents: start --
-- 5 elements
one
two
three
four 4
five
-- array contents: work --
-- 4 elements
one
three
four 4
five

ขออภัยเพิ่งพยายาม มันไม่ทำงานใน zsh สำหรับอาร์เรย์ assoziative
Falk

ใช้งานได้ดีฉันเพิ่งทดสอบ (อีกครั้ง) สิ่งที่ไม่ได้ผลสำหรับคุณ? โปรดอธิบายสิ่งที่ไม่ได้ผลอย่างละเอียดที่สุดเท่าที่จะทำได้ คุณใช้ ZSH เวอร์ชันอะไร
trevorj

0

นอกจากนี้ยังมีไวยากรณ์นี้เช่นหากคุณต้องการลบองค์ประกอบที่ 2:

array=("${array[@]:0:1}" "${array[@]:2}")

ซึ่งในความเป็นจริงแล้วการต่อกันของ 2 แท็บ ตัวแรกจากดัชนี 0 ถึงดัชนี 1 (เฉพาะตัว) และตัวที่ 2 จากดัชนี 2 ไปยังจุดสิ้นสุด



-1

นี่เป็นวิธีแก้ปัญหาที่รวดเร็วและสกปรกซึ่งจะใช้งานได้ในกรณีง่ายๆ แต่จะพังหาก (a) มีอักขระพิเศษ regex อยู่$deleteหรือ (b) มีช่องว่างในรายการใด ๆ เริ่มต้นด้วย:

array+=(pluto)
array+=(pippo)
delete=(pluto)

ลบรายการทั้งหมดที่ตรงกัน$delete:

array=(`echo $array | fmt -1 | grep -v "^${delete}$" | fmt -999999`)

ส่งผลให้ echo $array-> pippo และตรวจสอบให้แน่ใจว่าเป็นอาร์เรย์: echo $array[1]-> pippo

fmtคลุมเครือเล็กน้อย: fmt -1ล้อมรอบคอลัมน์แรก (เพื่อวางแต่ละรายการในบรรทัดของตัวเองนั่นคือจุดที่ปัญหาเกิดขึ้นกับรายการในช่องว่าง) fmt -999999ดึงกลับไปที่บรรทัดเดียวโดยเว้นช่องว่างระหว่างรายการ มีวิธีอื่นในการทำเช่นxargs.

ภาคผนวก: หากคุณต้องการลบเพียงคู่แรกให้ใช้ sed ตามที่อธิบายไว้ที่นี่ :

array=(`echo $array | fmt -1 | sed "0,/^${delete}$/{//d;}" | fmt -999999`)


-1

เพื่อหลีกเลี่ยงความขัดแย้งกับดัชนีอาร์เรย์ใช้unset- ดูhttps://stackoverflow.com/a/49626928/3223785และhttps://stackoverflow.com/a/47798640/3223785สำหรับข้อมูลเพิ่มเติม - ARRAY_VAR=(${ARRAY_VAR[@]})โอนอาร์เรย์กับตัวเอง:

#!/bin/bash

ARRAY_VAR=(0 1 2 3 4 5 6 7 8 9)
unset ARRAY_VAR[5]
unset ARRAY_VAR[4]
ARRAY_VAR=(${ARRAY_VAR[@]})
echo ${ARRAY_VAR[@]}
A_LENGTH=${#ARRAY_VAR[*]}
for (( i=0; i<=$(( $A_LENGTH -1 )); i++ )) ; do
    echo ""
    echo "INDEX - $i"
    echo "VALUE - ${ARRAY_VAR[$i]}"
done

exit 0

[อ้างอิง: https://tecadmin.net/working-with-array-bash-script/ ]


-2
#/bin/bash

echo "# define array with six elements"
arr=(zero one two three 'four 4' five)

echo "# unset by index: 0"
unset -v 'arr[0]'
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

arr_delete_by_content() { # value to delete
        for i in ${!arr[*]}; do
                [ "${arr[$i]}" = "$1" ] && unset -v 'arr[$i]'
        done
        }

echo "# unset in global variable where value: three"
arr_delete_by_content three
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

echo "# rearrange indices"
arr=( "${arr[@]}" )
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

delete_value() { # value arrayelements..., returns array decl.
        local e val=$1; new=(); shift
        for e in "${@}"; do [ "$val" != "$e" ] && new+=("$e"); done
        declare -p new|sed 's,^[^=]*=,,'
        }

echo "# new array without value: two"
declare -a arr="$(delete_value two "${arr[@]}")"
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

delete_values() { # arraydecl values..., returns array decl. (keeps indices)
        declare -a arr="$1"; local i v; shift
        for v in "${@}"; do 
                for i in ${!arr[*]}; do
                        [ "$v" = "${arr[$i]}" ] && unset -v 'arr[$i]'
                done
        done
        declare -p arr|sed 's,^[^=]*=,,'
        }
echo "# new array without values: one five (keep indices)"
declare -a arr="$(delete_values "$(declare -p arr|sed 's,^[^=]*=,,')" one five)"
for i in ${!arr[*]}; do echo "arr[$i]=${arr[$i]}"; done

# new array without multiple values and rearranged indices is left to the reader

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