¿Cómo dividir una cadena en múltiples cadenas separadas por al menos un espacio en bash shell?

Tengo una cadena que contiene muchas palabras con al menos un espacio entre cada dos. ¿Cómo puedo dividir la cadena en palabras individuales para poder recorrerlas?

La cadena se pasa como un argumento. Ej. ${2} == "cat cat file" . ¿Cómo puedo recorrerlo?

Además, ¿cómo puedo verificar si una cadena contiene espacios?

¿Intentó simplemente pasar la variable de cadena a un ciclo for ? Bash, por ejemplo, se dividirá en espacios en blanco automáticamente.

 sentence="This is a sentence." for word in $sentence do echo $word done 

 This is a sentence. 

Me gusta la conversión a una matriz, para poder acceder a elementos individuales:

  sentence="this is a story" stringarray=($sentence) 

ahora puede acceder a elementos individuales directamente (comienza con 0):

  echo ${stringarray[0]} 

o convertir de nuevo a cadena para hacer un ciclo:

  for i in "${stringarray[@]}" do : # do whatever on $i done 

Por supuesto, el bucle de la cadena directamente fue respondido antes, pero esa respuesta tenía la desventaja de no realizar un seguimiento de los elementos individuales para su uso posterior:

  for i in $sentence do : # do whatever on $i done 

Consulte también Bash Array Reference

Simplemente use el “set” de proyectiles incorporado. Por ejemplo,

 establecer $ text

Después de eso, las palabras individuales en $ text estarán en $ 1, $ 2, $ 3, etc. Para la solidez, uno usualmente lo hace

 establecer - texto $ basura
 cambio

para manejar el caso donde $ text está vacío o comienza con un guión. Por ejemplo:

 text = "Esta es una prueba"
 establecer - texto $ basura
 cambio
 por palabra;  hacer
   echo "[$ word]"
 hecho

Esto imprime

 [Esta]
 [es]
 [un]
 [prueba]

La forma más fácil y segura de BASH 3 y superior es:

 var="string to split" read -ra arr < <<"$var" 

(donde arr es la matriz que toma las partes divididas de la cadena) o, si puede haber nuevas líneas en la entrada y desea algo más que la primera línea:

 var="string to split" read -ra arr -d '' < <<"$var" 

(tenga en cuenta el espacio en -d '' , no puede dejarse de lado), pero esto podría darle una nueva línea inesperada de < <<"$var" (ya que esto agrega implícitamente una LF al final).

Ejemplo:

 touch NOPE var="* a *" read -ra arr < <<"$var" for a in "${arr[@]}"; do echo "[$a]"; done 

Resultados lo esperado

 [*] [a] [*] 

ya que esta solución (a diferencia de todas las soluciones anteriores aquí) no es propensa a englobamiento de shell inesperado ya menudo incontrolable.

También esto le da el poder completo de IFS como probablemente quiera:

Ejemplo:

 IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd) for a in "${arr[@]}"; do echo "[$a]"; done 

Resultados algo como:

 [tino] [x] [1000] [1000] [Valentin Hilbig] [/home/tino] [/bin/bash] 

Como puede ver, los espacios se pueden preservar de esta manera también:

 IFS=: read -ra arr < <<' split : this ' for a in "${arr[@]}"; do echo "[$a]"; done 

salidas

 [ split ] [ this ] 

Tenga en cuenta que el manejo de IFS en BASH es un tema por sí mismo, así que haga sus pruebas, algunos temas interesantes sobre esto:

  • unset IFS : Ignora las ejecuciones de SPC, TAB, NL y en línea comienza y termina
  • IFS='' : Sin separación de campo, simplemente lee todo
  • IFS=' ' : ejecuciones de SPC (y SPC solamente)

Un último ejemplo

 var=$'\n\nthis is\n\n\na test\n\n' IFS=$'\n' read -ra arr -d '' < <<"$var" i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done 

salidas

 1 [this is] 2 [a test] 

mientras

 unset IFS var=$'\n\nthis is\n\n\na test\n\n' read -ra arr -d '' < <<"$var" i=0; for a in "${arr[@]}"; do let i++; echo "$i [$a]"; done 

salidas

 1 [this] 2 [is] 3 [a] 4 [test] 

Por cierto:

  • Si no está acostumbrado a $'ANSI-ESCAPED-STRING' , es un ahorro de tiempo.

  • Si no incluye -r (como en read -a arr < <<"$var" ), a continuación, lee se escapa la barra invertida. Esto se deja como ejercicio para el lector.


Para la segunda pregunta:

Para probar algo en una cadena, por lo general me apego a la case , ya que esto puede verificar múltiples casos a la vez (nota: el caso solo ejecuta la primera coincidencia, si necesita usar multiploce las declaraciones de case ), y esta necesidad es muy a menudo el caso (juego de palabras intencionado):

 case "$var" in '') empty_var;; # variable is empty *' '*) have_space "$var";; # have SPC *[[:space:]]*) have_whitespace "$var";; # have whitespaces like TAB *[^-+.,A-Za-z0-9]*) have_nonalnum "$var";; # non-alphanum-chars found *[-+.,]*) have_punctuation "$var";; # some punctuation chars found *) default_case "$var";; # if all above does not match esac 

Entonces puede establecer el valor de retorno para verificar SPC como este:

 case "$var" in (*' '*) true;; (*) false;; esac 

¿Por qué case ? Porque generalmente es un poco más legible que las secuencias de expresiones regulares, y gracias a los metacaracteres de Shell maneja muy bien el 99% de todas las necesidades.

 $ echo "This is a sentence." | tr -s " " "\012" This is a sentence. 

Para buscar espacios, use grep:

 $ echo "This is a sentence." | grep " " > /dev/null $ echo $? 0 $ echo "Thisisasentence." | grep " " > /dev/null $ echo $? 1 

(A) Para dividir una oración en sus palabras (espacio separado) puede simplemente usar el IFS predeterminado usando

 array=( $string ) 

Ejemplo que ejecuta el siguiente fragmento

 #!/bin/bash sentence="this is the \"sentence\" 'you' want to split" words=( $sentence ) len="${#words[@]}" echo "words counted: $len" printf "%s\n" "${words[@]}" ## print array 

saldrá

 words counted: 8 this is the "sentence" 'you' want to split 

Como puede ver, también puede usar comillas simples o dobles sin ningún problema

Notas:
– Esto es básicamente lo mismo que la respuesta de la mafia , pero de esta manera almacenas la matriz para cualquier necesidad adicional. Si solo necesita un bucle único, puede usar su respuesta, que es una línea más corta 🙂
– Consulte esta pregunta para obtener métodos alternativos para dividir una cadena en función del delimitador.

(B) Para buscar un personaje en una cadena, también puede usar una coincidencia de expresión regular.
Ejemplo para verificar la presencia de un carácter de espacio que puede usar:

 regex='\s{1,}' if [[ "$sentence" =~ $regex ]] then echo "Space here!"; fi 

Para verificar espacios solo con bash:

 [[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"