Usuwanie elementu z tablicy Bash
Muszę usunąć element z tablicy w powłoce bash. Generalnie po prostu bym zrobił:
array=("${(@)array:#<element to remove>}")
Niestety element, który chcę usunąć jest zmienną, więc nie mogę użyć poprzedniego polecenia. Poniżej przykład:
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
Jakiś pomysł? 16 answers
Następujące prace jak chcesz w bash
i zsh
:
$ array=(pluto pippo)
$ delete=(pluto)
$ echo ${array[@]/$delete}
pippo
$ array=( "${array[@]/$delete}" ) #Quotes when working with strings
Jeśli trzeba usunąć więcej niż jeden element:
...
$ delete=(pluto pippo)
for del in ${delete[@]}
do
array=("${array[@]/$del}") #Quotes when working with strings
done
Zastrzeżenie
Ta technika faktycznie usuwa przedrostki pasujące $delete
z elementów, niekoniecznie całych elementów.
Update
Aby naprawdę usunąć dokładny element, musisz przejść przez tablicę, porównując obiekt docelowy z każdym elementem i używając unset
do usunięcia dokładnego dopasowania.
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
Uwaga jeśli to zrobisz, a jeden lub więcej elementów zostanie usuniętych, indeksy nie będą już ciągiem liczb całkowitych.
$ declare -p array
declare -a array=([0]="pluto" [2]="bob")
Faktem jest, że tablice nie zostały zaprojektowane do wykorzystania jako zmienne struktury danych. Są one używane przede wszystkim do przechowywania list elementów w pojedynczej zmiennej bez konieczności marnowania znaków jako ogranicznika(np. do przechowywania listy łańcuchów, które mogą zawierać białe znaki).
Jeśli luki są problemem, musisz odbudować tablicę, aby wypełnić luki:
for i in "${!array[@]}"; do
new_array+=( "${array[i]}" )
done
array=("${new_array[@]}")
unset new_array
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-13 10:39:42
Można zbudować nową tablicę bez niepożądanego elementu, a następnie przypisać ją z powrotem do starej tablicy. To działa w bash
:
array=(pluto pippo)
new_array=()
for value in "${array[@]}"
do
[[ $value != pluto ]] && new_array+=($value)
done
array=("${new_array[@]}")
unset new_array
Daje to:
echo "${array[@]}"
pippo
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-29 18:21:41
Aby rozwinąć powyższe odpowiedzi, można użyć następujących elementów do usunięcia wielu elementów z tablicy, bez częściowego dopasowania:
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
Spowoduje to powstanie tablicy zawierającej: (two onetwo three threefour "one six")
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-16 09:48:06
Jest to najbardziej bezpośredni sposób na wyłączenie wartości, jeśli znasz jej pozycję.
$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-13 17:05:58
Skrypt powłoki POSIX nie posiada tablic.
Więc najprawdopodobniej używasz określonego dialektu, takiego jak bash
, Korn czy zsh
.
W związku z tym, na twoje pytanie od teraz nie można odpowiedzieć.
Może to działa dla Ciebie:
unset array[$delete]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-31 15:38:45
Oto (prawdopodobnie bardzo specyficzna dla Basha) mała funkcja z indirection zmiennej bash i unset
; jest to ogólne rozwiązanie, które nie wymaga zastępowania tekstu lub odrzucania pustych elementów i nie ma problemów z cytowaniem / białymi spacjami itp.
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
Użyj go jak delete_ary_elmt ELEMENT ARRAYNAME
bez sigila $
. Przełącz == $word
dla == $word*
dla dopasowań prefiksów; użyj {[6] } dla dopasowań bez rozróżniania wielkości liter; itd., cokolwiek wspiera bash [[
.
Działa poprzez wyznaczenie wskaźników input array i iteracja nad nimi do tyłu (więc usuwanie elementów nie psuje kolejności iteracji). Aby uzyskać indeksy, musisz uzyskać dostęp do tablicy wejściowej według nazwy, co można zrobić za pomocą zmiennej Bash indirection x=1; varname=x; echo ${!varname} # prints "1"
.
Nie możesz uzyskać dostępu do tablic po nazwie, jak aryname=a; echo "${$aryname[@]}
, to oznacza błąd. Nie możesz zrobić aryname=a; echo "${!aryname[@]}"
, to daje Ci indeksy zmiennej aryname
(chociaż nie jest to tablica). Działa aryref="a[@]"; echo "${!aryref}"
, który wyświetli elementy tablicy a
, zachowując powłoka-cytowanie słów i spacji dokładnie tak jak echo "${a[@]}"
. Ale to działa tylko do drukowania elementów tablicy, a nie do drukowania jej długości lub indeksów (aryref="!a[@]"
lub aryref="#a[@]"
lub "${!!aryref}"
lub "${#!aryref}"
, wszystkie one zawodzą).
Więc kopiuję oryginalną tablicę według jej nazwy poprzez Bash indirection i pobieram indeksy z kopii. Do iteracji indeksów w odwrotnej kolejności używam pętli w stylu C. Mogę to zrobić również poprzez dostęp do indeksów za pomocą ${!arycopy[@]}
i odwrócenie ich za pomocą tac
, czyli cat
, który odwraca wokół kolejności linii wejściowych.
Rozwiązanie funkcji bez zmiennej indrection prawdopodobnie musiałoby obejmować eval
, które mogą, ale nie muszą być bezpieczne w takiej sytuacji(nie mogę powiedzieć).
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-15 18:17:54
Oto jednoliniowe rozwiązanie z mapfile:
$ mapfile -d $'\0' -t arr < <(printf '%s\0' "${arr[@]}" | grep -Pzv "<regexp>")
Przykład:
$ 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
Ta metoda pozwala na dużą elastyczność poprzez modyfikację / wymianę polecenia grep i nie pozostawia żadnych pustych łańcuchów w tablicy.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-23 08:26:14
Właściwie, zauważyłem, że składnia powłoki ma wbudowane zachowanie, które pozwala na łatwą rekonstrukcję tablicy, gdy, jak postawiono w pytaniu, element powinien zostać usunięty.
# 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
Zauważ jak skonstruowaliśmy tablicę używając składni x+=()
Basha?
Możesz dodać więcej niż jeden element z tym, zawartość całej innej tablicy naraz.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-21 07:25:39
Http://wiki.bash-hackers.org/syntax/pe#substring_removal
${parametr # wzorzec} # usuń z początku
${parametr# # wzorzec} # usuń z początku, dopasuj
${parametr % wzorzec} # usuń z końca
${parametr % % wzorzec} # remove from the end, greedy match
Aby wykonać pełny remove element, musisz wykonać polecenie unset z instrukcją if. Jeśli nie zależy ci na usunięciu prefiksy z innych zmiennych lub o obsługę białych znaków w tablicy, wtedy możesz po prostu porzucić cudzysłowy i zapomnieć o pętlach for.
Zobacz przykład poniżej dla kilku różnych sposobów czyszczenia tablicy.
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
Wyjście
Choose an option:
1) foobar
2) foo bar
3) s
4) Quit
Option?
Mam nadzieję, że to pomoże.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-15 19:05:32
Za pomocą unset
Aby usunąć element z określonego indeksu, możemy użyć unset
, a następnie skopiować do innej tablicy. Tylko unset
nie jest wymagane w tym przypadku. Ponieważ unset
nie usuwa elementu, ustawia null string do określonego indeksu w tablicy.
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]}"
Wyjście to
aa cc dd ee
1st val is , 2nd val is cc
aa cc dd ee
1st val is cc, 2nd val is dd
Za pomocą :<idx>
Możemy również usunąć niektóre elementy używając :<idx>
. Na przykład jeśli chcemy usunąć 1. element możemy użyć :1
jako wymienione poniżej.
declare -a arr=('aa' 'bb' 'cc' 'dd' 'ee')
arr2=("${arr[@]:1}")
echo ${arr2[@]}
echo "1st val is ${arr2[1]}, 2nd val is ${arr2[2]}"
Wyjście to
bb cc dd ee
1st val is cc, 2nd val is dd
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-03 09:43:17
W ZSH jest to bardzo proste (zauważ, że używa składni zgodnej z bash, która jest bardziej niż to konieczne, jeśli jest to możliwe dla ułatwienia zrozumienia):
# 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[@]}"
Wyniki:
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-11 08:22:03
Tylko częściowa odpowiedź
Aby usunąć pierwszą pozycję w tablicy
unset array[0]
Aby usunąć ostatnią pozycję w tablicy
unset array[-1]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-08-19 09:17:00
To co robię to:
array="$(echo $array | tr ' ' '\n' | sed "/itemtodelete/d")"
BAM, ten element jest usuwany.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-24 17:34:13
Jest to szybkie i brudne rozwiązanie, które będzie działać w prostych przypadkach, ale pęknie, jeśli (a) w $delete
są znaki specjalne regex lub (b) w dowolnych elementach są spacje. Zaczynając od:
array+=(pluto)
array+=(pippo)
delete=(pluto)
Usuń wszystkie wpisy dokładnie pasujące $delete
:
array=(`echo $array | fmt -1 | grep -v "^${delete}$" | fmt -999999`)
W wyniku
echo $array
- > pippo, i upewniając się, że to tablica:
echo $array[1]
- > pippo
fmt
jest trochę niejasne: fmt -1
owija się w pierwszej kolumnie (aby umieścić każdy element w swojej własnej linii. I tu problem powstaje z przedmiotów w przestrzeniach.) fmt -999999
rozpakowuje go z powrotem do jednej linii, przywracając odstępy między przedmiotami. Są na to inne sposoby, np. xargs
.
Dodatek: jeśli chcesz usunąć tylko pierwsze dopasowanie, Użyj sed, zgodnie z opisem tutaj :
array=(`echo $array | fmt -1 | sed "0,/^${delete}$/{//d;}" | fmt -999999`)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-01 14:44:00
A może coś w stylu:
array=(one two three)
array_t=" ${array[@]} "
delete=one
array=(${array_t// $delete / })
unset array_t
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-28 00:11:50
#/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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-17 19:16:11