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
これを行うと、1つ以上の要素が削除されると、インデックスは整数の連続したシーケンスではなくなることに注意してください。
$ 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を使用した1行のソリューションは次のとおりです。
$ 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コマンドを変更/交換することで柔軟性を高め、配列に空の文字列を残しません。
上記の答えを拡張するには、次を使用して、部分一致なしで配列から複数の要素を削除できます。
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 3 threefour "one six")
次に、bash変数の間接指定とunset
;を含む(おそらく非常にbash固有の)小さな関数を示します。テキストの置換や空の要素の破棄を伴わない一般的なソリューションであり、引用符や空白などの問題はありません。
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 [[
がサポートするものは何でも。
入力配列のインデックスを決定し、それらを逆方向に反復処理することで機能します(したがって、要素を削除しても反復順序が乱れることはありません)。インデックスを取得するには、入力配列に名前でアクセスする必要があります。これは、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間接指定を介して元の配列を名前でコピーし、コピーからインデックスを取得します。逆にインデックスを反復処理するには、Cスタイルのforループを使用します。 ${!arycopy[@]}
を介してインデックスにアクセスし、tac
を使用してインデックスを逆にすることでもできます。これは、入力行の順序を反転するcat
です。
変数間接指定のない関数ソリューションには、おそらくeval
が含まれる必要があります。これは、その状況で安全に使用できる場合とそうでない場合があります(わかりません)。
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
を使用できます。
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
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
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
POSIXシェルスクリプトには配列がありません。
そのため、おそらくbash
、kornシェル、またはzsh
などの特定の方言を使用しています。
したがって、今のあなたの質問には答えられません。
たぶんこれはあなたのために働く:
unset array[$delete]
配列の最初のアイテムを削除するには
unset 'array[0]'
配列の最後のアイテムを削除するには
unset 'array[-1]'
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?
それが役に立てば幸いです。
実際、シェル構文には、問題で提起されたようにアイテムを削除する必要があるときに配列を簡単に再構築できるようにする動作が組み込まれていることに気づきました。
# 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+=()
構文を使用して配列を作成した方法に注目してください。
実際には、複数のアイテムを追加できます。つまり、他の配列全体のコンテンツを一度に追加できます。
この構文もあります。 2番目の要素を削除する場合:
array=("${array[@]:0:1}" "${array[@]:2}")
実際には、2つのタブが連結されています。最初はインデックス0からインデックス1(排他的)まで、2番目はインデックス2から最後まで。