Jak znaleźć indeks słowa w ciągu w bash?

10

W skrypcie bash

Mam ciąg znaków, który zawiera kilka słów oddzielonych jednym lub więcej spacjami. to znaczy:

Name   Age Sex  ID         Address

Jeśli chcę znaleźć dowolne słowo, na przykład chcę znaleźć indeks słowa „Wiek”, jak mogę to zrobić?

Czy jest jakieś polecenie, które zwróci bezpośrednio numer indeksu słowa, którego chcę?

Dzięki.

G3Y
źródło
Czy rozwiązanie musi być wyłącznie bashowe? Czy można użyć awk, grep itp.?
jftuga

Odpowiedzi:

12

Bash samodzielnie wykonuje dzielenie wyrazów na ciągi - w rzeczywistości częściej, niż to, unikając tego, jest to problem, a powód cytowania jest tak ważny. W twoim przypadku łatwo to wykorzystać: po prostu umieść łańcuch w tablicy bez cytowania go - bash użyje podziału słów, aby oddzielić poszczególne elementy. Zakładając, że Twój ciąg jest przechowywany w zmiennej $str,

ar=($str) # no quotes!

zwróci tablicę 5 elementów. Indeks tablicy jest indeksem słów (licząc od 0, jak w większości języków skryptowych i programistycznych), tzn. Dostęp do „wieku” można uzyskać za pomocą

${ar[1]}  # 0 => Name, 1 => Age, 2 => Sex, 3 => ID, 4 => Address

lub, jeśli chcesz znaleźć indeks elementu według zawartości, zapętl się nad tablicą, tj

function el_index {
    cnt=0; for el in "${ar[@]}"; do
        [[ $el == "$1" ]] && echo $cnt && break
        ((++cnt))
    done
}
el_index "Age" # => 1
kopischke
źródło
wow ... nie wiedziałem, że bez cudzysłowów byłaby to tablica. dzięki!
G3Y
4
$ export FOO="Name   Age Sex  ID         Address"

Zamień * Age na Age - spowoduje to usunięcie czegokolwiek przed „Age”:

$ echo ${FOO/*Age/Age}
Age Sex ID Address

Zdobądź wszystko przed „Wiekiem”

$ echo ${FOO/Age*/}
Name

Uzyskaj długość tego ciągu (który jest indeksem „Wiek”):

$ BEGIN=${FOO/Age*/}
$ echo ${#BEGIN}
7
użytkownik1034081
źródło
Nie odpowiada na pytanie, ale wow! Zręczna sztuczka. Działa nawet w popiół i ze zmiennymi osadzonymi: export L='debug info warn error'; export GTE='warn'; echo ${L/*${GTE}/${GTE}}drukuje „warn error”
Steve Tarver
0

Jeśli nie musisz ściśle używać bash, ale możesz korzystać z innych programów często spotykanych w systemach z bash, możesz użyć czegoś takiego:

echo "Name   Age Sex ID  Addr" | python -c 'print(raw_input().index("Age"))+1'

Python rozpoczyna indeksowanie ciągów od zera, dlatego dodałem +1 na końcu polecenia.

jftuga
źródło
0

Możesz użyć natywnego wyrażenia regularnego bash

# a function to print the index of a field and its name
printIx() { 
  for ((l=0,i=1;i<$1;i++)) ;do 
     ((l+=${#BASH_REMATCH[i]}))
  done
  printf '%3s %s\n' $l "$2"
}

#   Using a zero based index
#   "0----+----1----+----2----+----3----+----4"
str="  Name   Age Sex  ID         Address   "

if [[ $str =~ ^(\ *)(Name)(\ +)(Age)(\ +)(Sex)(\ +()ID)(\ +)(Address)\ *$ ]] ;then
  F=(Name Age Sex ID Address)
  f=(   2   4   6  8      10)  # regex back-references
  for ((g=0;g<${#f[@]};g++)) ;do
     printIx  ${f[g]} "${F[g]}"
  done 
fi

Wynik

  2 Name
  9 Age
 13 Sex
 20 ID
 29 Address
Peter.O
źródło
0

Uwaga : Zakładając, że przez indeks rozumiesz, że chcesz wiedzieć, które to słowo (od 0), a nie jaki znak w ciągu zaczyna się od tego słowa. Inne odpowiedzi dotyczą tego drugiego.

Nie jestem tego świadomy, ale możesz to zrobić. Dwie sztuczki:

  1. Skorzystaj z wrodzonych zdolności konstruktu for, aby podzielić niecytowane dane wejściowe według białych znaków.
  2. Zajmij się przypadkiem, w którym nie możesz znaleźć kolumny, którą chcesz. W tym przypadku zdecydowałem się wysłać znaleziony indeks do stouta i pozwolić, aby kod stanu wskazywał, czy znalezienie się powiodło. Istnieją inne możliwości.

Kod:

#!/bin/bash
find_index() {
    local str=$1
    local search=$2
    let local n=0
    local retval=1 # here, 1 is failure, 0 success
    for col in $str; do # $str unquoted -> whitespace tokenization!
    if [ $col = $search ]; then
        echo $n
        retval=0
        break
    else
        ((n++))
    fi
    done
    return $retval
}

test="Name   Age Sex  ID         Address"
idx=`find_index "$test" Age`
if [ $? -ne 0 ]; then
    echo "Not found!"
else
    echo "Found: $idx"
fi
Owen S.
źródło
0

Wypróbuj następujący oneliner javascript w powłoce (użyj powłoki javascript):

$ js <<< "x = 'Name   Age Sex  ID         Address'; print(x.indexOf('Age'));"
7

Lub z dokumentem tutaj:

js <<EOF
x = 'Name   Age Sex  ID         Address';
print(x.indexOf('Age'));
EOF
Gilles Quenot
źródło
0

Znalazłem rozwiązanie, które działa dobrze.

$ string = „teraz jest czas”
$ buf = $ {string # * the}
$ echo $ buf
output: the time
$ index = $ (($ {# string} - $ {# buf} + 1))
$ echo $
wynik indeksu : 8 -> indeks pierwszego słowa „the”

Działa podobnie jak funkcja indexOf () w Javie, która zwraca pierwsze wystąpienie ciągu wejściowego.

Znaleźć to rozwiązanie tutaj http://www.linuxquestions.org/questions/linux-newbie-8/bash-string-manipulation-help-670627/ (ostatni post). Ten facet uratował mi dzień. Podziękowania dla niego.

Szybszy sposób, jeśli chcesz wykonać podciąg z pierwszego indeksu.

$ a = "jakiś długi ciąg"
$ b = "ri"
$ echo $ {a / * $ b / $ b}
pierścień
$ echo $ {a / $ b * / $ b}
jakiś długi stri

/programming/10349102/shell-script-substring-from-first-indexof-substring

Linh Lino
źródło
0

Jeśli dostępne są coreutils, możesz to zrobić w następujący sposób:

echo $ {str / Age //} | cut -d / -f1 | wc -w

Na żądanie MariusMatutiae dodaję wyjaśnienie, jak działa ta 3-etapowa operacja:

echo $ {str / Age //} 1. zamień szukany ciąg znaków na unikalny znak (w moim przypadku /)

cut -d / -f1 2. odetnij całą część łańcucha po unikalnym char

wc -w 3. policz i wydrukuj pozostałe słowa, to da nam numer indeksu

W celu uzyskania referencji sprawdź:

http://www.tldp.org/LDP/abs/html/parameter-substitution.html (przejdź do: „Zmienna rozbudowa / zamiana podciągów”)
http://www.gnu.org/software/coreutils/manual/coreutils .html (przejdź do: „Polecenie cięcia” i „Wywołanie wc”

PiotrO
źródło
Chociaż rozwiązuje to obecny problem, takie zwięzłe odpowiedzi są odrzucone na tych stronach. Przydałoby się kilka słów, wyjaśniających dokładnie, dlaczego to działa. Zrób tak, proszę.
MariusMatutiae
0

Połączenie dwóch wcześniej podanych odpowiedzi, przy użyciu czystych tablic bash i zamiany podciągów.

Chodzi o to, aby uzyskać ciąg wszystkich słów przed tym, którego chcesz, a następnie policzyć liczbę słów w tym podciągu, przekształcając je w tablicę.

$ haystack="Name   Age Sex  ID         Address"
$ words_before=( ${haystack%Age*} )     # truncate string, make array
$ echo ${#words_before[*]}              # count words in array
1

Oczywiście Wiek można zapisać w innej zmiennej needle, a następnie użyć ${haystack%$needle*}. Spodziewaj się problemów, jeśli szukane słowo jest podzbiorem innego słowa, w którym to przypadku odpowiedź kopischke nadal działa.

Cimbali
źródło
0

To 7-letnie pytanie, ale niektórzy mogą potrzebować odpowiedzi w czystym stylu.

STRING="Name   Age Sex  ID         Address"
INDEXOF_AGE=${#${STRING/Age*/}}
echo $INDEXOF_AGE
Henry Chen
źródło