wake-up-neo.net

Rufen Sie den Index eines Werts in einem Bash-Array ab

Ich habe etwas in bash

myArray=('red' 'orange' 'green')

Und ich würde gerne sowas machen

echo ${myArray['green']}

Was in diesem Fall 2 ausgeben würde. Ist das erreichbar?

39
user137369

Das wird es tun:

#!/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

Wenn Sie dies in eine Funktion umwandeln (z. B. get_index ()), können Sie es natürlich generisch machen

60
Steve Walsh

Sie müssen Ihr Array vor der Verwendung mit deklarieren 

declare -A myArray
myArray=([red]=1 [orange]=2 [green]=3)
echo ${myArray['orange']}
23
Olaf Dietsche

Nein. In bash können Sie nur ein einfaches Array mit einer Ganzzahl indizieren. Assoziative Arrays (eingeführt in bash 4) können durch Strings indiziert werden. Sie sind jedoch nicht für die Art der umgekehrten Suche vorgesehen, die Sie ohne ein speziell konstruiertes assoziatives Array benötigen.

$ declare -A myArray
$ myArray=([red]=0 [orange]=1 [green]=2)
$ echo ${myArray[green]}
2
10
chepner

Es gibt auch einen kniffligen Weg:

echo ${myArray[@]/green//} | cut -d/ -f1 | wc -w | tr -d ' '

Und Sie erhalten 2 Hier sind Referenzen

9
PiotrO

Ich mag diese Lösung:

let "n=(`echo ${myArray[@]} | tr -s " " "\n" | grep -n "green" | cut -d":" -f 1`)-1"

Die Variable n enthält das Ergebnis!

2
user3680055

Dies ist nur eine weitere Methode zum Initialisieren eines assoziativen Arrays, wie von chepner gezeigt wurde .. _ Vergessen Sie nicht, dass Sie declare oder typset ein assoziatives Array explizit mit dem Attribut -A angeben müssen.

i=0; declare -A myArray=( [red]=$((i++)) [orange]=$((i++)) [green]=$((i++)) )
echo ${myArray[green]}
2

Dadurch entfällt die Notwendigkeit, Werte fest zu codieren, und es ist unwahrscheinlich, dass Duplikate erhalten.

Wenn Sie viele Werte hinzufügen, kann es hilfreich sein, sie in separate Zeilen zu setzen.

i=0; declare -A myArray; 
myArray+=( [red]=$((i++)) )
myArray+=( [orange]=$((i++)) )
myArray+=( [green]=$((i++)) )
echo ${myArray[green]}
2

Angenommen, Sie möchten eine Reihe von Zahlen und Kleinbuchstaben (z. B. für eine Menüauswahl), können Sie auch so etwas tun.

declare -a mKeys_1=( {{0..9},{a..z}} );
i=0; declare -A mKeys_1_Lookup; eval mKeys_1_Lookup[{{0..9},{a..z}}]="$((i++))";

Wenn du dann rennst

echo "${mKeys_1[15]}"
f
echo "${mKeys_1_Lookup[f]}"
15
2
sbts

Dies könnte nur für Arrays funktionieren,

my_array=(red orange green)
echo "$(printf "%s\n" "${my_array[@]}")" | grep -n '^orange$' | sed 's/:orange//'

Ausgabe:

2

Wenn Sie den Header-Index in einer TSV-Datei suchen möchten,

head -n 1 tsv_filename | sed 's/\t/\n/g' | grep -n '^header_name$' | sed 's/:header_name//g'
1
Manish Sharma

Ein weiterer kniffliger One-Liner:

index=$((-1 + 10#0$(IFS=$'\n' echo "${my_array[*]}" | grep --line-number --fixed-strings -- "$value" | cut -f1 -d:)))

eigenschaften:

  • unterstützt Elemente mit Leerzeichen
  • gibt -1 zurück, wenn er nicht gefunden wird

vorbehalte:

  • erfordert, dass value nicht leer ist
  • schwierig zu lesen

Erklärungen durch Aufgliederung in Ausführungsreihenfolge:

IFS=$'\n' echo "${my_array[*]}"

setzen Sie das Array-Erweiterungs-Trennzeichen (IFS) auf eine neue Zeile, und erweitern Sie das Array

grep --line-number --fixed-strings -- "$value"

grep für ein Spiel:

  • zeilennummern anzeigen (--line-number oder -n)
  • verwenden Sie eine feste Zeichenfolge (--fixed-strings oder -F; deaktiviert Regex).
  • elemente mit einem - (--) beginnen

    schnitt -f1 -d:

extrahiere nur die Zeilennummer (Format ist <line_num>:<matched line>)

$((-1 + 10#0$(...)))

subtrahieren Sie 1, da die Zeilennummern 1 indiziert und Arrays 0 indiziert sind

  • wenn $(...) nicht übereinstimmt:

    • es wird nichts zurückgegeben und der Standard von 0 wird verwendet (10#0)
  • wenn $(...) übereinstimmt:
    • eine Zeilennummer ist vorhanden und mit 10#0 versehen. d.h. 10#02, 10#09, 10#014 usw
    • das 10#-Präfix erzwingt Basis-10/Dezimalzahlen anstelle von Oktal


Verwenden Sie awk anstelle von grep, cut & bash arithmetic:

IFS=$'\n'; awk "\$0 == \"${value//\"/\\\"}\" {print NR-1}" <<< "${my_array[*]}"

eigenschaften:

  • unterstützt Elemente mit Leerzeichen
  • unterstützt leere Elemente
  • weniger Befehle in einer Subshell geöffnet

vorbehalte:

  • gibt zurück, wenn er nicht gefunden wird

Erklärungen durch Aufgliederung in Ausführungsreihenfolge:

IFS=$'\n' [...] <<< "${my_array[*]}"

setzen Sie das Array-Erweiterungs-Trennzeichen (IFS) auf eine neue Zeile, und erweitern Sie das Array

awk "\$0 == \"${value//\"/\\\"}\" {print NR-1}"

entspricht der gesamten Zeile und druckt die 0-indizierte Zeilennummer

  • ${value//\"/\\\"} ersetzt doppelte Anführungszeichen in $value durch Escape-Versionen
  • da wir eine Variablensubstitution benötigen, kann dieses Segment mehr flüchten als gewünscht
1
srbs

Etwas prägnanter und funktioniert in Bash 3.x:

my_array=(red orange green)
value='green'

for i in "${!my_array[@]}"; do
   [[ "${my_array[$i]}" = "${value}" ]] && break
done

echo $i
0
cmcginty

In zsh kannst du das

xs=( foo bar qux )
echo ${xs[(ie)bar]}

siehe zshparam (1) -Unterstützpunkt-Flags

0
yaccz

Einfache Lösung:

my_array=(red orange green)
echo ${my_array[*]} | tr ' ' '\n' | awk '/green/ {print NR-1}'
0
Nikhil Gupta