¿Cómo encontrar el índice de una palabra en una cadena en bash?

10

En script bash,

Tengo una cadena que contiene varias palabras separadas por uno o más espacios. es decir:

Name   Age Sex  ID         Address

Si quiero encontrar alguna de las palabras, por ejemplo, quiero encontrar el índice de la palabra "Edad", ¿cómo puedo hacerlo?

¿Hay algún comando que devuelva el número de índice de la palabra que quiero directamente?

Gracias.

G3Y
fuente
¿La solución tiene que ser estrictamente bash? ¿O se puede usar awk, grep, etc.?
jftuga
Publicación relacionada: ¿Cómo imprimir ciertas columnas por nombre?
zx8754

Respuestas:

12

Bash realiza la división de palabras en cadenas por sí mismo; de hecho, la mayoría de las veces, evitando que sea un problema, y ​​la razón por la cual la cita es tan importante. Es fácil aprovechar eso en su caso: simplemente coloque su cadena en una matriz sin citarla; bash usará la división de palabras para separar los elementos individuales. Suponiendo que su cadena se almacena en la variable $str,

ar=($str) # no quotes!

devolverá una matriz de 5 elementos. Su índice de matriz es su índice de palabras (contando desde 0, como en la mayoría de los lenguajes de programación y scripts), es decir, se accede a "Edad" usando

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

o, si necesita encontrar el índice del elemento por contenido, repita la matriz, es decir

function el_index {
    cnt=0; for el in "${ar[@]}"; do
        [[ $el == "$1" ]] && echo $cnt && break
        ((++cnt))
    done
}
el_index "Age" # => 1
kopischke
fuente
wow ... no sabía que sin las comillas sería una matriz. ¡Gracias!
G3Y
4
$ export FOO="Name   Age Sex  ID         Address"

Reemplazar * Edad con Edad: esto eliminará cualquier cosa antes de "Edad":

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

Obtén cualquier cosa antes de "Age"

$ echo ${FOO/Age*/}
Name

Obtenga la longitud de esa cadena (que es el índice de "Edad"):

$ BEGIN=${FOO/Age*/}
$ echo ${#BEGIN}
7
usuario1034081
fuente
No responde la pregunta, pero ¡guau! Truco hábil. Incluso funciona en cenizas y con variables incrustadas: export L='debug info warn error'; export GTE='warn'; echo ${L/*${GTE}/${GTE}}imprime 'advertir error'
Steve Tarver
0

Si no tiene que usar estrictamente bash, pero puede usar otros programas comúnmente encontrados en sistemas con bash, entonces podría usar algo como esto:

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

Python comienza su indexación de cadenas en cero, por lo tanto, agregué +1 al final del comando.

jftuga
fuente
0

Puedes usar la expresión regular nativa 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

Salida

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

Nota : Suponiendo aquí que por índice quiere decir que quiere saber qué palabra es (a partir de 0), no en qué carácter de la cadena comienza la palabra. Otras respuestas abordan lo último.

No es que yo sepa, pero puedes hacer uno. Dos trucos:

  1. Use las habilidades innatas de la construcción for para dividir una entrada sin comillas por espacios en blanco.
  2. Maneje el caso donde no puede encontrar la columna que desea. En este caso, elegí enviar el índice encontrado a stout y dejar que el código de estado indique si la búsqueda fue exitosa. Hay otras posibilidades

Código:

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

Pruebe el siguiente jael oneliner en un shell (use javascript shell):

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

O con un documento aquí:

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

Encontré una solución que funciona bien.

$ string = 'ahora es el momento'
$ buf = the $ {string # * the}
$ echo $ buf
output: the time
$ index = $ (($ {# string} - $ {# buf} + 1))
$ echo $ index
output: 8 -> índice de la primera palabra "the"

Funciona de manera similar a la función indexOf () en Java que devuelve la primera aparición de una cadena de entrada.

Encontré esta solución aquí http://www.linuxquestions.org/questions/linux-newbie-8/bash-string-manipulation-help-670627/ (última publicación). Este chico me salvó el día. Crédito para él.

Una forma más rápida si desea hacer una subcadena desde el primer índice de.

$ a = "alguna cadena larga"
$ b = "ri"
$ echo $ {a / * $ b / $ b}
anillo
$ echo $ {a / $ b * / $ b}
alguna línea larga

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

Linh Lino
fuente
0

Si coreutils están disponibles, puede hacerlo de la siguiente manera:

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

Por solicitud de MariusMatutiae, agrego una explicación de cómo funciona esta operación de 3 pasos:

echo $ {str / Age //} 1. reemplazar la cadena que se está buscando por un carácter único (en mi caso /)

cut -d / -f1 2. corta toda la cadena que está después de un carácter único

wc -w 3. cuenta e imprime las palabras que quedan, esto nos dará un número de índice

Para referencias, consulte:

http://www.tldp.org/LDP/abs/html/parameter-substitution.html (vaya a: "Expansión variable / Reemplazo de subcadenas")
http://www.gnu.org/software/coreutils/manual/coreutils .html (vaya a: "El comando de corte" y "invocación de wc"

PiotrO
fuente
Si bien esto resuelve el problema en cuestión, estas respuestas concisas están mal vistas en estos sitios. Sería más útil pasar algunas palabras explicando exactamente por qué esto funciona. Por favor, hazlo.
MariusMatutiae
0

Una combinación de dos respuestas dadas anteriormente, utilizando matrices de bash puro y reemplazo de subcadenas.

La idea es obtener una cadena de todas las palabras antes de la que desea, luego contar el número de palabras en esa subcadena convirtiéndola en una matriz.

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

Por supuesto, la edad se puede almacenar en otra variable needle, luego usar ${haystack%$needle*}. Espere problemas si la palabra que busca es un subconjunto de otra palabra, en cuyo caso la respuesta de kopischke sigue funcionando.

Cimbali
fuente