Comment trouver l'index d'un mot dans une chaîne dans bash?

9

Dans le script bash,

J'ai une chaîne qui contient plusieurs mots séparés par un ou plusieurs espaces. c'est à dire:

Name   Age Sex  ID         Address

Si je veux trouver n'importe quel mot, par exemple je veux trouver l'index du mot "Age", comment puis-je le faire?

Existe-t-il une commande qui renvoie directement le numéro d’index du mot que je veux?

Merci.

GJ.
la source
La solution doit-elle être strictement en bash? Ou peut-on utiliser awk, grep, etc.
Jftuga

Réponses:

11

Bash effectue le fractionnement des mots dans les chaînes tout seul - en fait, le plus souvent, éviter que ce soit un problème, et la raison pour laquelle citant est si important. Il est facile de tirer parti de cela dans votre cas: placez simplement votre chaîne dans un tableau sans la citer: bash utilisera le fractionnement de mots pour séparer les éléments individuels. En supposant que votre chaîne est stockée dans la variable $str,

ar=($str) # no quotes!

retournera un tableau de 5 éléments. Votre index de tableau est votre index de mots (comptant à partir de 0, comme dans la plupart des langages de script et de programmation), c’est-à-dire que vous accédez à «Age» à l’aide de

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

ou, si vous avez besoin de trouver l'index d'élément par contenu, passez en boucle sur le tableau, c.-à-d.

function el_index {
    cnt=0; for el in "${ar[@]}"; do
        [[ $el == "$1" ]] && echo $cnt && break
        ((++cnt))
    done
}
el_index "Age" # => 1
Kopischke
la source
wow ... je ne savais pas que sans les guillemets, ce serait un tableau. Merci!
GJ.
4
$ export FOO="Name   Age Sex  ID         Address"

Remplacez * Age avec Age - cela supprimera tout ce qui précède "Age":

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

Obtenir quelque chose avant "Age"

$ echo ${FOO/Age*/}
Name

Obtenez la longueur de cette chaîne (qui est l'index de "Age"):

$ BEGIN=${FOO/Age*/}
$ echo ${#BEGIN}
7
utilisateur1034081
la source
Ne répond pas à la question, mais waouh! Astuce Slick. Cela fonctionne même dans les cendres et avec les variables incorporées: export L='debug info warn error'; export GTE='warn'; echo ${L/*${GTE}/${GTE}}print 'warn error'
Steve Tarver
0

Si vous n'avez pas à utiliser strictement bash, mais pouvez utiliser d'autres programmes couramment trouvés sur les systèmes dotés de bash, vous pouvez utiliser quelque chose comme ceci:

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

Python commence son indexation de chaîne à zéro, donc j'ai ajouté +1 à la fin de la commande.

Jftuga
la source
0

Vous pouvez utiliser l'expression rationnelle native de 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

Sortie

  2 Name
  9 Age
 13 Sex
 20 ID
 29 Address
Peter.O
la source
0

Remarque : Supposons ici que vous voulez dire par index que vous voulez savoir de quel mot il s’agit (à partir de 0) et non du caractère de la chaîne sur lequel le mot commence. D'autres réponses abordent ce dernier.

Pas que je sache, mais vous pouvez en faire un. Deux astuces:

  1. Utilisez les capacités innées de la construction for pour fractionner une entrée non citée par des espaces.
  2. Traitez le cas où vous ne trouvez pas la colonne souhaitée. Dans ce cas, j'ai choisi d'envoyer l'index trouvé à stout et de laisser le code d'état indiquer si la recherche a abouti. Il y a d'autres possibilités.

Code:

#!/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.
la source
0

Essayez l’insertion de javascript suivante dans un shell (utilisez shell javascript):

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

Ou avec un ici-doc:

js <<EOF
x = 'Name   Age Sex  ID         Address';
print(x.indexOf('Age'));
EOF
Gilles Quenot
la source
0

J'ai trouvé une solution qui fonctionne bien.

$ string = 'c'est l'heure'
$ buf = le $ {string # * le}
$ echo $ buf
sortie: l'heure
$ index = $ (($ {# string} - $ {# buf} + 1))
$ echo $ index
output: 8 -> index du premier mot "the"

Il fonctionne de la même manière que la fonction indexOf () en Java, qui renvoie la première occurrence d’une chaîne d’entrée.

Trouvé cette solution ici http://www.linuxquestions.org/questions/linux-newbie-8/bash-string-manipulation-help-670627/ (dernier post). Ce mec a sauvé ma journée. Crédit à lui.

Manière plus rapide si vous voulez faire une sous-chaîne à partir du premier indexof.

$ a = "une longue chaîne"
$ b = "ri"
$ echo $ {a / * $ b / $ b}
anneau
$ echo $ {a / $ b * / $ b}
une longue frappe

https://stackoverflow.com/questions/10349102/shell-script-substring-from-first-indexof-substring

Linh Lino
la source
0

Si des coreutils sont disponibles, vous pouvez le faire de la manière suivante:

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

À la demande de MariusMatutiae, j'ajoute une explication sur le fonctionnement de cette opération en 3 étapes:

echo $ {str / Age //} 1. remplace la chaîne qui est recherchée pour un caractère unique (dans mon cas /)

cut -d / -f1 2. coupe la partie entière de la chaîne après un caractère unique

wc -w 3. compter et imprimer les mots qui restent cela nous donnera un numéro d'index

Pour les références s'il vous plaît vérifier:

http://www.tldp.org/LDP/abs/html/parameter-substitution.html (voir: "Extension variable / Remplacement de sous-chaîne")
http://www.gnu.org/software/coreutils/manual/coreutils .html (allez à: "La commande cut" et "invocation de wc"

PiotrO
la source
Même si cela résout le problème, ces réponses laconiques sont mal vues dans ces sites. Il serait plus utile de passer quelques mots pour expliquer pourquoi cela fonctionne. S'il-vous-plaît faites ainsi.
MariusMatutiae
0

Un mélange de deux réponses données précédemment, utilisant des tableaux de bash purs et un remplacement de sous-chaîne.

L'idée est d'obtenir une chaîne de tous les mots avant celle que vous voulez, puis de compter le nombre de mots de cette sous-chaîne en la transformant en un tableau.

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

Bien sûr, l'âge peut être stocké dans une autre variable needle, puis utilisé ${haystack%$needle*}. Attendez-vous à des problèmes si le mot que vous recherchez est un sous-ensemble d'un autre mot, auquel cas la réponse de Kopischke fonctionne toujours.

Cimbali
la source