Выдаліць элемент з масіва абалонкі

Мне трэба выдаліць элемент з масіва ў Баш абалонкі. Наогул я б проста зрабіць:

array=("${(@)array:#}")

На жаль, элемент Я хачу выдаліць гэта зменная, так што я не магу выкарыстоўваць папярэднюю каманду. Там вось прыклад:

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

Любая ідэя?

57
Якая абалонка? Ваш прыклад выглядае ЗШ .
дададзена аўтар chepner, крыніца
<�Код> масіў = ($ {масіў [@]/$ выдаліць}) працуе, як чакалася ў Bash. Вы проста прапусцілі = ?
дададзена аўтар Ken Sharp, крыніца

16 адказы

Наступныя працы, як вы хацелі б у баш і ЗШ :

$ 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

<�Моцны> Caveat

Гэты метад фактычна выдаляе прэфіксы адпаведнасць $ </выдаліць код> з элементаў, не абавязкова цэлых элементаў.

<�Моцны> Абнаўленне

Каб сапраўды выдаліць дакладны дэталь, вам неабходна прайсці праз масіў, параўноўваючы мэта кожнага элемента, і выкарыстоўваючы скід , каб выдаліць дакладнае адпаведнасць.

array=(pluto pippo bob)
delete=(pippo)
for target in "${delete[@]}"; do
  for i in "${!array[@]}"; do
    if [[ ${array[i]} = "${delete[0]}" ]]; 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
94
дададзена
@BenjaminW. Я лічу, што толькі $ {масіў [@]/$ дэль} выдаліць нічога прыстаўкай. Для таго, каб раней цытуемы прыклад, калі $ масіў = (ВС flowersun) $ дэль = (ВС) , затым $ {масіў [@]/$ дэль} прывядзе flowersun .
дададзена аўтар Izy-, крыніца
Заўвага: гэта можа ўсталяваць адпаведнае значэнне ні да чаго, але элемент усё яшчэ будзе знаходзіцца ў масіве.
дададзена аўтар Blauhirn, крыніца
Так, Бернштэйн паказваў, што з раней.
дададзена аўтар chepner, крыніца
Добрае пытанне. Прынамсі, у ЗШ , $ {масіў [@]/# $% выдаленне} працуе, каб абмежаваць матч да поўных слоў.
дададзена аўтар chepner, крыніца
Што тычыцца нюанс: гэта выдаленне $ дэль , дзе ён адпавядае, не абавязкова толькі прэфіксы. Выдаленне прэфіксаў будзе $ {масіў [@]/# $ дэль} (прынамсі, у Bash).
дададзена аўтар Benjamin W., крыніца
@Izy Не, гэта прыводзіць кветка . Для flowersun , вы павінны выкарыстоўваць $ {масіў [@]/# $ дэль} . Зноў жа, не кажучы ўжо для Zsh, проста Bash.
дададзена аўтар Benjamin W., крыніца
Звярніце ўвагу, што гэта на самай справе робіць замену, таму калі масіў нешта накшталт (pluto1 pluto2 Піпі) , то вы будзеце ў канчатковым выніку з (1 2 Піпі) .
дададзена аўтар haridsv, крыніца
Толькі будзьце асцярожныя, выкарыстоўваючы гэта ў цыкле, таму што вы ў канчатковым выніку з пустой элемент, дзе выдалены элемент быў. Для здаровага сэнсу вы маглі б зрабіць нешта накшталт для элемента ў «$ {масіў [@]}» рабіць, калі [[$ элемент]]; то рэха $ {элемент} ц зроблена
дададзена аўтар Joel B, крыніца
Не працуе, калі выдаленне = (Плутон Піпі)
дададзена аўтар tommy.carstensen, крыніца
@ Tommy.carstensen, я выкарыстоўваю для цыклу, каб апрацаваць выпадак, калі выдаленне = (Плутон Піпі). Калі хто-то ёсць ідэя лепей, калі ласка даюць зрабіць акцыю
дададзена аўтар zhihong, крыніца
Так як выдаліць толькі супадаючыя элементы?
дададзена аўтар UmaN, крыніца
проста ведаю, што: $ масіў = (ВС сланечніка) $ выдаліць = (ВС) $ рэха $ {масіў [@]/$ выдаліць} вынікі ў кветка
дададзена аўтар bernstein, крыніца
Дзякуй, гэта выдатна.
дададзена аўтар Alex, крыніца

Вы можаце стварыць новы масіў без непажаданага элемента, а затым прызначыць яго назад у стары масіў. Гэта працуе ў баш :

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
15
дададзена

Для таго, каб пашырыць на вышэйзгаданых адказаў, наступнае можа быць выкарыстаны для выдалення некалькіх элементаў з масіва, без частковага адпаведнасці:

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

Гэта прывядзе да масіва, які змяшчае: (Два OneTwo тры threefour «адзін шэсць»)

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
2
дададзена
Вы можаце дадаць некаторыя каментары ці апісанне, каб паведаміць нам аб сваім адказе?
дададзена аўтар Michael, крыніца

Гэта самы прамы шлях, каб скінуць значэнне, калі вы ведаеце, што гэта становішча.

$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2
2
дададзена
Паспрабуйце рэха $ {масіў [1]} , вы атрымаеце пусты радок. А каб атрымаць тры вам трэба зрабіць рэха $ {масіў [2]} . Так скід не з'яўляецца правільным механізмам для выдалення элемента ў масіве Баш.
дададзена аўтар rashok, крыніца

POSIX сцэнар абалонкі не мае масівы.

Так што, хутчэй за ўсё, вы карыстаецеся спецыфічны дыялект, напрыклад, баш , Korn абалонак або ЗШ .

Такім чынам, ваш пытанне цяпер не можа адказаць.

Можа быць, гэта працуе для вас:

unset array[$delete]
1
дададзена
Прывітанне, Я выкарыстоўваю Баш абалонкі атм. А «$ выдаліць» гэта не пазіцыя элемента, а сама радок. Так што я не думаю, што «знятая з аховы» будзе працаваць
дададзена аўтар Alex, крыніца

Гэта хуткае і бруднае рашэнне, якое будзе працаваць у простых выпадках, але зламаецца, калі (а) ёсць рэгулярныя выразы спецыяльных сімвалаў $ выдаліць , або (б) ёсць прабелу наогул у любым пункты. Пачынаючы з:

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

Выдаліць усе запісы дакладна адпаведныя $ выдаліць :

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

resulting in echo $array -> pippo, and making sure it's an array: echo $array[1] -> pippo

fmt is a little obscure: fmt -1 wraps at the first column (to put each item on its own line. That's where the problem arises with items in spaces.) fmt -999999 unwraps it back to one line, putting back the spaces between items. There are other ways to do that, such as xargs.

Дадатак: Калі вы хочаце выдаліць толькі першы матч, выкарыстоўвайце СЕПГ, як апісана тут :

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

Вось адна лінія з рашэннем файле праекта:

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

прыклад:

$ 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
дададзена
Звярніце ўвагу, што гэта не атрымоўваецца з палямі, якія ўтрымліваюць сімвалы новага радка.
дададзена аўтар gniourf_gniourf, крыніца
Калі ласка, выкарыстоўвайце Printf '% s \ п' "$ {масіў [@]}" замест гэтага выродлівага IFS / рэха рэчы.
дададзена аўтар gniourf_gniourf, крыніца
Я рэдагаваў адказ, каб вырашыць пераклады радкоў у палях.
дададзена аўтар Niklas Holm, крыніца

Што я раблю:

array="$(echo $array | tr ' ' '\n' | sed "/itemtodelete/d")"

BAM, што элемент будзе выдалены.

1
дададзена
Гэта парушае для масіва = ( «першага пункта» «другога пункта») .
дададзена аўтар Benjamin W., крыніца

Вось (верагодна, вельмі Баш-спецыфічны) маленькая функцыя з удзелам Баша зменных вакольным і адключанага ; гэта агульнае рашэнне, якое не ўключае ў сябе замену тэксту або адкідаючы пустыя элементы і не мае ніякіх праблем з працытаваць/прабелы і г.д.

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 ЭЛЕМЕНТ ИмяМассива без якіх-небудзь $ сигилы. Пераключыце == $ слова для == $ слова * для прэфікса запалак; выкарыстоўваць $ {elmt ,} == $ {слова ,} для регистронезависимых матчаў; і г.д., незалежна ад баш [[ падтрымлівае.

Ён працуе шляхам вызначэння паказчыкаў уваходнага масіва і ітэрацыі над імі ў адваротным кірунку (так выдаленне элементаў не закручваць парадак ітэрацый). Для таго, каб атрымаць паказчыкі, неабходныя для атрымання доступу да масіву ўводу па імені, якое можа быць зроблена з дапамогай Баш зменнай Ускосна х = 1; VarName = х; Рэха $ {! Пераменная} # друкуе "1" .

Вы не можаце атрымаць доступ да масіваў па імені, як aryname = а; рэха «$ {$ aryname [@]} , гэта дае вам памылку, Вы не можаце зрабіць aryname = а ;. рэха" $ ", гэта {aryname [@]!} дае індэксы зменнай aryname (хоць гэта не масіў) Што праца aryref = "а [@]"; "{! aryref} $" рэха , які будзе друкаваць элементы масіва а , захаванне абалонкі-слова ў двукоссі і прабелы гэтак жа, як рэха «$ {а [@]}» . Але гэта толькі працуе для друку элементаў масіва, а не для друку яго даўжыні або індэксаў ( aryref = "! а [@]" або aryref = "# а [@]" або "$ {!! aryref}" або "$ {#! aryref}" , усе яны церпяць няўдачу).

Таму я капіяваць пачатковы масіў па яго імя праз Баш Ускосна і атрымаць індэксы з копіі. Для таго, каб перабіраць індэксы ў зваротным напрамку я выкарыстоўваю C-стыль для цыклу. Я мог бы таксама зрабіць гэта шляхам доступу да індэксах праз $ {! Arycopy [@]} і реверсирования іх Tac , які з'яўляецца кошка , што абгортваецца парадку уваходнай лініі.

Функцыя рашэнні без зменнай Ускосна, верагодна, прыйдзецца ўключаць Eval , які можа ці не можа быць бяспечным для выкарыстання ў гэтай сітуацыі (я не магу сказаць).

1
дададзена
Гэта амаль працуе добра, аднак гэта не переобъявить зыходны масіў перадаецца ў функцыю, таму ў той час як першапачатковы масіў мае значэнне адсутнічае, яно таксама мае свае паказчыкі пераблыталіся. Што гэта азначае, што наступны выклік вы ўносіце delete_ary_elmt на тым жа масіве не будзе працаваць (ці выдаліць няправільныя рэчы). Напрыклад, пасля таго, як вы ўставілі, паспрабуйце запусціць delete_ary_elmt «D» масіў , а затым паўторна друк масіва. Вы ўбачыце, што няправільны элемент атрымлівае выдалены. Выдаленне апошняга элемента будзе таксама тое не працуе.
дададзена аўтар Scott, крыніца

Як пра што-то накшталт:

array=(one two three)
array_t=" ${array[@]} "
delete=one
array=(${array_t// $delete/})
unset array_t
1
дададзена

<�Моцны> Выкарыстанне скід

Каб выдаліць элемент у прыватнасці, індэкс, мы можам выкарыстоўваць адключанае , а затым зрабіць копію ў іншы масіў. Толькі што скід не патрабуецца ў дадзеным выпадку. Дзякуючы зададзена не выдаляе элемент ён проста ставіць пусты радок канкрэтнага індэкса ў масіве.

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

Using :

We can remove some set of elements using : also. For example if we want to remove 1st element we can use :1 as mentioned below.

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
дададзена

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

<�Р> $ {параметрамі # PATTERN} # выдаліць з пачатку      <�Р> $ {параметрамі ## PATTERN} # выдаліць з самага пачатку, прагны матч      <�Р> $ {параметрамі% ОБРАЗЕЦ} # выдаліць з канца </р>      <�Р> $ {параметрамі %% ОБРАЗЕЦ} # выдаліць з канца, прагны матч </р>

Для таго, каб зрабіць поўны элемент выдаліць, вы павінны зрабіць адключаную каманду з, калі заявай. Калі вы не клапоціцеся аб выдаленні прэфіксаў ад іншых зменных або аб падтрымцы прабелаў у масіве, то вы можаце проста апусціць двукоссі і забыцца пра для завес.

Глядзіце прыклад ніжэй на некалькі розных спосабаў ачысткі ад масіва.

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
дададзена

толькі частковы адказ

Для таго, каб выдаліць першы элемент масіва

unset array[0]

Для выдалення апошняга элемента ў масіве

unset array[-1]
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

Звярніце ўвагу на тое, як мы пабудавалі масіў, выкарыстоўваючы ў Bash х + =) сінтаксіс (?

Вы маглі б на самай справе дадаць больш аднаго элемента з гэтым, змест цэлага іншага масіва адразу.

0
дададзена

У ZSH гэта мёртвы просты (звярніце ўвагу, гэта выкарыстоўвае больш Баш сінтаксіс, сумяшчальны, чым гэта неабходна, дзе гэта магчыма для прастаты разумення):

# 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
0
дададзена