私は何かがbash
好きです
myArray=('red' 'orange' 'green')
そして、私は次のようなことをしたいです
echo ${myArray['green']}
この場合、これはを出力します2
。これは達成可能ですか?
これはそれを行います:
#!/bin/bash
my_array=(red orange green)
value='green'
for i in "${!my_array[@]}"; do
if [[ "${my_array[$i]}" = "${value}" ]]; then
echo "${i}";
fi
done
明らかに、これを関数(get_index()など)に変換すると、汎用的にすることができます
で使用する前に配列を宣言する必要があります
declare -A myArray
myArray=([red]=1 [orange]=2 [green]=3)
echo ${myArray['orange']}
いいえ。インデックスを作成できるのは、整数の単純な配列のみですbash
。連想配列(bash
4で導入)は、文字列でインデックスを付けることができます。ただし、特別に構築された連想配列がなければ、要求しているタイプの逆ルックアップには対応していません。
$ declare -A myArray
$ myArray=([red]=0 [orange]=1 [green]=2)
$ echo ${myArray[green]}
2
トリッキーな方法も1つあります。
echo ${myArray[@]/green//} | cut -d/ -f1 | wc -w | tr -d ' '
そして、あなたは2を取得します ここに参照があります
もう少し簡潔で、Bash3.xで機能します。
my_array=(red orange green)
value='green'
for i in "${!my_array[@]}"; do
[[ "${my_array[$i]}" = "${value}" ]] && break
done
echo $i
もう1つのトリッキーなワンライナー:
index=$((-1 + 10#0$(IFS=$'\n' echo "${my_array[*]}" | grep --line-number --fixed-strings -- "$value" | cut -f1 -d:)))
特徴:
-1
見つからない場合は戻ります警告:
value
空でない必要があります実行順に分解して説明します。
IFS=$'\n' echo "${my_array[*]}"
配列展開区切り文字(IFS
)を改行文字に設定し、配列を展開します
grep --line-number --fixed-strings -- "$value"
一致するgrep:
--line-number
または-n
)--fixed-strings
または-F
;正規表現を無効にする)-
(--
)で始まる要素を許可する
カット-f1-d:
行番号のみを抽出します(形式は<line_num>:<matched line>
)
$((-1 + 10#0$(...)))
行番号は1インデックスであり、配列は0インデックスであるため、1を減算します。
$(...)
一致しない場合:
0
が使用されます(10#0
)$(...)
一致
する場合:10#0
;が付いています。すなわち10#02
、、、など10#09
_10#014
10#
接頭辞は、8進数ではなく基数10/10進数を強制します、&bash演算awk
の代わりに使用:grep
cut
IFS=$'\n'; awk "\$0 == \"${value//\"/\\\"}\" {print NR-1}" <<< "${my_array[*]}"
特徴:
警告:
見つからない場合は戻ります実行順に分解して説明します。
IFS=$'\n' [...] <<< "${my_array[*]}"
配列展開区切り文字(IFS
)を改行文字に設定し、配列を展開します
awk "\$0 == \"${value//\"/\\\"}\" {print NR-1}"
行全体を一致させ、0インデックスの行番号を出力します
${value//\"/\\\"}
$value
の二重引用符をエスケープされたバージョンに置き換えますこれはアレイに対してのみ機能する可能性がありますが、
my_array=(red orange green)
echo "$(printf "%s\n" "${my_array[@]}")" | grep -n '^orange$' | sed 's/:orange//'
出力:
2
tsvファイルでヘッダーインデックスを検索する場合は、
head -n 1 tsv_filename | sed 's/\t/\n/g' | grep -n '^header_name$' | sed 's/:header_name//g'
私はその解決策が好きです:
let "n=(`echo ${myArray[@]} | tr -s " " "\n" | grep -n "green" | cut -d":" -f 1`)-1"
変数nには結果が含まれます!
これは、chepnerが示したように、連想配列を初期化するもう1つの方法です。declare
属性を持つ連想配列を明示的にまたはタイプセットする必要があることを忘れないでください-A
。
i=0; declare -A myArray=( [red]=$((i++)) [orange]=$((i++)) [green]=$((i++)) )
echo ${myArray[green]}
2
これにより、値をハードコーディングする必要がなくなり、重複する可能性が低くなります。
追加する値がたくさんある場合は、それらを別々の行に配置すると役立つ場合があります。
i=0; declare -A myArray;
myArray+=( [red]=$((i++)) )
myArray+=( [orange]=$((i++)) )
myArray+=( [green]=$((i++)) )
echo ${myArray[green]}
2
数字と小文字の配列が必要だとします(例:メニュー選択の場合)。このようなこともできます。
declare -a mKeys_1=( {{0..9},{a..z}} );
i=0; declare -A mKeys_1_Lookup; eval mKeys_1_Lookup[{{0..9},{a..z}}]="$((i++))";
その後実行すると
echo "${mKeys_1[15]}"
f
echo "${mKeys_1_Lookup[f]}"
15
zshでは次のことができます
xs=( foo bar qux )
echo ${xs[(ie)bar]}
zshparam(1)サブセクションの添え字フラグを参照してください。
これにより、クエリの0ベースの配列インデックス(ここでは「オレンジ」)が出力されます。
echo $(( $(printf "%s\n" "${myArray[@]}" | sed -n '/^orange$/{=;q}') - 1 ))
クエリが配列で発生しない場合は、上記の出力が表示されます-1
。
クエリが配列内で複数回発生する場合、上記はクエリの最初の発生のインデックスを出力します。
このソリューションはsedを呼び出すため、このスレッドの純粋なbashソリューションのいくつかと効率的に競合できるとは思えません。
これは、配列メンバーのインデックスを返すためのいくつかのメソッドを示しています。配列は、最初と最後のインデックスに適用できない値を使用して、1から始まるインデックスを提供し、制限を提供します。
whileループは、配列値のインデックスを生成する目的で、カットオフを使用した反復の興味深い方法です。ループの本体には、null操作のコロンのみが含まれます。重要な部分は、一致するまで、または可能な一致を過ぎてiを繰り返すことです。
関数indexof()は、テキスト値をインデックスに変換します。値が一致しない場合、関数はエラー処理を実行するためのテストで使用できるエラーコードを返します。配列に一致しない入力値は、範囲制限(-gt, -lt)
テストを超えます。
良い/悪い値をループするテスト(メインコード)があり、最初の3行はコメント化されていますが、興味深い結果を確認するためにいくつかのバリエーションを試してください(lines 1,3 or 2,3 or 4
)。エラー状態を考慮したコードをいくつか含めました。これは便利な場合があるためです。
コードの最後の行は、インデックス値をエコーする既知の適切な値「緑」で関数indexofを呼び出します。
indexof(){
local s i;
# 0 1 2 3 4
s=( @@@ red green blue @o@ )
while [ ${s[i++]} != $1 ] && [ $i -lt ${#s[@]} ]; do :; done
[ $i -gt 1 ] && [ $i -lt ${#s[@]} ] || return
let i--
echo $i
};# end function indexof
# --- main code ---
echo -e \\033c
echo 'Testing good and bad variables:'
for x in @@@ red pot green blue frog bob @o@;
do
#v=$(indexof $x) || break
#v=$(indexof $x) || continue
#echo $v
v=$(indexof $x) && echo -e "$x:\t ok" || echo -e "$x:\t unmatched"
done
echo -e '\nShow the index of array member green:'
indexof green
myArray=('red' 'orange' 'green')
echo ${myArray[@]}
arrayElementToBeRemoved='orange'
echo "removing element: $arrayElementToBeRemoved"
# Find index of the array element (to be kept or preserved)
let "index=(`echo ${myArray[@]} | tr -s " " "\n" | grep -n "$arrayElementToBeRemoved" | cut -d":" -f 1`)-1"
unset "myArray[$index]"
echo ${myArray[@]}
私は自分に似たものが欲しかったので、ループを避けて、思いついた...
myArray=('red' 'orange' 'green')
declare -p myArray | sed -n "s,.*\[\([^]]*\)\]=\"green\".*,\1,p"
...要素が見つからない場合にstdoutを汚さないままにします...
$ myArray=('red' 'orange' 'green')
$ declare -p myArray | sed -n "s,.*\[\([^]]*\)\]=\"green\".*,\1,p"
2
$ declare -p myArray | sed -n "s,.*\[\([^]]*\)\]=\"gren\".*,\1,p"
$
その後、私はグーグルで検索し、この質問を見つけて、共有したいと思いました;)
これは、キャラクター「モリー」の1ベースのNEUROMANCERインデックスを出力します;)
get_index() {
declare -n dummy_array="$1"
# alternative: split read -ra array <<< "${dummy_array[@]}"
local array=( "${dummy_array[@]}" )
# alternative: local value; value="$( for dummy_value; do true; done; echo "$dummy_value" )"
local value=$2
local length="${#array[@]}"
local i=0
while (( i < length ))
do
if [ "${array[$i]}" = "$value" ]
then echo $(( i + 1 )); return 0
fi; (( i++ ))
done
echo "$2 not found beneath $1"
exit 1
}
NEUROMANCER=(Case Molly Riviera)
get_index NEUROMANCER Molly
get_index NEUROMANCER 'John Doe'
次に実行する場合:
$ bash script.sh
2
John Doe not found beneath NEUROMANCER
function array_indexof() {
[ $# -lt 2 ] && return 1
local a=("$@")
local v="${a[-1]}"
unset a[-1]
local i
for i in ${!a[@]}; do
if [ "${a[$i]}" = "$v" ]; then
echo $i
return 0 # stop after first match
fi
done
return 1
}
a=(a b c d)
i=$(array_indexof "${a[@]}" d)
echo $i # 3
簡単な解決策:
my_array=(red orange green)
echo ${my_array[*]} | tr ' ' '\n' | awk '/green/ {print NR-1}'