Bash 배열에서 요소 제거
bash 셸의 배열에서 요소를 제거해야합니다. 일반적으로 간단히 다음을 수행합니다.
array=("${(@)array:#<element to remove>}")
불행히도 제거하려는 요소는 변수이므로 이전 명령을 사용할 수 없습니다. 여기에 예가 있습니다.
array+=(pluto)
array+=(pippo)
delete=(pluto)
array( ${array[@]/$delete} ) -> but clearly doesn't work because of {}
어떤 생각?
다음은 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
원하지 않는 요소없이 새 배열을 만든 다음 이전 배열에 다시 할당 할 수 있습니다. 이것은 다음에서 작동합니다 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
이것이 위치를 안다면 값을 설정 해제하는 가장 직접적인 방법입니다.
$ array=(one two three)
$ echo ${#array[@]}
3
$ unset 'array[1]'
$ echo ${array[@]}
one three
$ echo ${#array[@]}
2
다음은 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 명령을 수정 / 교환하여 큰 유연성을 허용하며 배열에 빈 문자열을 남기지 않습니다.
다음은 bash 변수 간접과 관련된 (아마도 매우 bash에 특정한) 작은 함수입니다 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*
위해 for 를 전환하십시오 . 사용 ${elmt,,} == ${word,,}
대소 문자를 구분하지 일치하는; 등, bash가 [[
지원하는 모든 것.
입력 배열의 인덱스를 결정하고 역순으로 반복하여 작동합니다 (따라서 요소를 삭제해도 반복 순서가 망가지지 않습니다). 인덱스를 얻으려면 bash 변수 indirection을 통해 수행 할 수있는 이름으로 입력 배열에 액세스해야합니다 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 간접 지정을 통해 원래 배열을 이름으로 복사하고 복사본에서 색인을 얻습니다. 인덱스를 반대로 반복하기 위해 C 스타일 for 루프를 사용합니다. 또한를 통해 인덱스에 액세스하고을 사용하여 ${!arycopy[@]}
반전하여 수행 할 수 있습니다 . 이는 입력 라인 순서를 뒤집는 tac
a cat
입니다.
변수 간접이없는 함수 솔루션은 아마도를 포함해야 할 것입니다 eval
. 이는 해당 상황에서 사용하는 것이 안전 할 수도 있고 안전하지 않을 수도 있습니다 (알 수 없습니다).
위의 답변을 확장하기 위해 다음을 사용하여 부분 일치없이 배열에서 여러 요소를 제거 할 수 있습니다.
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")
사용 unset
특정 인덱스에서 요소를 제거하기 위해 사용 unset
하고 다른 배열로 복사 할 수 있습니다 . unset
이 경우 에만 필요하지 않습니다. unset
요소를 제거하지 않기 때문에 배열의 특정 인덱스에 null 문자열을 설정합니다.
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>
also를 사용하여 일부 요소 세트를 제거 할 수 있습니다. 예를 들어 첫 번째 요소를 제거하려면 :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
POSIX 쉘 스크립트에는 배열이 없습니다.
따라서 bash
, korn shells 또는 zsh
.
따라서 현재 귀하의 질문에 답할 수 없습니다.
아마도 이것이 당신을 위해 일할 것입니다.
unset array[$delete]
실제로 쉘 구문에는 항목이 제거되어야 할 때 배열을 쉽게 재구성 할 수있는 동작이 내장되어 있음을 알았습니다.
# 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의 x+=()
구문을 사용하여 배열을 어떻게 구성했는지 주목 하세요?
실제로 한 번에 전체 다른 배열의 내용을 포함하여 둘 이상의 항목을 추가 할 수 있습니다.
http://wiki.bash-hackers.org/syntax/pe#substring_removal
$ {PARAMETER # PATTERN} # 처음부터 제거
$ {PARAMETER ## PATTERN} # 처음부터 제거, 탐욕스러운 일치
$ {PARAMETER % PATTERN} # 끝에서 제거
$ {PARAMETER %% PATTERN} # 욕심 많은 경기 끝에서 제거
전체 제거 요소를 수행하려면 if 문으로 unset 명령을 수행해야합니다. 다른 변수에서 접두사를 제거하거나 배열에서 공백을 지원하는 데 관심이 없다면 따옴표를 삭제하고 for 루프를 잊어 버릴 수 있습니다.
어레이를 정리하는 몇 가지 다른 방법은 아래 예를 참조하십시오.
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?
도움이 되었기를 바랍니다.
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
이 구문도 있습니다. 예를 들어 두 번째 요소를 삭제하려는 경우 :
array=("${array[@]:0:1}" "${array[@]:2}")
이것은 사실 2 개의 탭을 연결 한 것입니다. 첫 번째는 인덱스 0에서 인덱스 1 (배타적)까지, 두 번째는 인덱스 2에서 끝까지입니다.
부분 답변 만
배열의 첫 번째 항목을 삭제하려면
unset 'array[0]'
배열의 마지막 항목을 삭제하려면
unset 'array[-1]'
To avoid conflicts with array index using unset
- see https://stackoverflow.com/a/49626928/3223785 and https://stackoverflow.com/a/47798640/3223785 for more information - reassign the array to itself: 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
[Ref.: https://tecadmin.net/working-with-array-bash-script/ ]
What I do is:
array="$(echo $array | tr ' ' '\n' | sed "/itemtodelete/d")"
BAM, that item is removed.
This is a quick-and-dirty solution that will work in simple cases but will break if (a) there are regex special characters in $delete
, or (b) there are any spaces at all in any items. Starting with:
array+=(pluto)
array+=(pippo)
delete=(pluto)
Delete all entries exactly matching $delete
:
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
.
Addendum: If you want to delete just the first match, use sed, as described here:
array=(`echo $array | fmt -1 | sed "0,/^${delete}$/{//d;}" | fmt -999999`)
How about something like:
array=(one two three)
array_t=" ${array[@]} "
delete=one
array=(${array_t// $delete / })
unset array_t
#/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
참고URL : https://stackoverflow.com/questions/16860877/remove-an-element-from-a-bash-array
'programing' 카테고리의 다른 글
두 번째 매개 변수를 기준으로 튜플 정렬 (0) | 2020.09.21 |
---|---|
대문자없이 IntelliJ IDEA 12 코드 완성 (0) | 2020.09.21 |
OS X 터미널 바로 가기 : 줄의 시작 / 끝으로 이동 (0) | 2020.09.21 |
NumPy 배열에 숫자가 아닌 값이 하나 이상 포함되어 있는지 감지 하시겠습니까? (0) | 2020.09.21 |
IFRAME에서 현재 URL 가져 오기 (0) | 2020.09.21 |