¿Cómo dividir una cadena en varias 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 que pueda hacer un bucle a través de ellas?
La cadena se pasa como argumento. Por ejemplo, ${2} == "cat cat file"
. ¿Cómo puedo recorrerlo?
Además, ¿cómo puedo comprobar si una cadena contiene espacios?
7 answers
¿Intentaste simplemente pasar la variable string a un bucle 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.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-24 05:13:46
Me gusta la conversión a un array, para poder acceder a elementos individuales:
sentence="this is a story"
stringarray=($sentence)
Ahora puede acceder directamente a elementos individuales (comienza con 0):
echo ${stringarray[0]}
O convertir de nuevo a cadena para hacer un bucle:
for i in "${stringarray[@]}"
do
:
# do whatever on $i
done
Por supuesto, el bucle a través de la cadena directamente se respondió 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
Véase también Referencia del Array Bash
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-11-15 17:24:39
Simplemente usa los shells "set" integrados. Por ejemplo,
set $text
Después de eso, las palabras individuales en text text estarán en $1, 2 2, etc 3, etc. Para la robustez, uno generalmente hace
set -- junk $text shift
Para manejar el caso donde text text está vacío o comienza con un guión. Por ejemplo:
text="This is a test" set -- junk $text shift for word; do echo "[$word]" done
Esto imprime
[This] [is] [a] [test]
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-25 16:15:31
La forma probablemente más fácil y segura en 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 más que solo la primera línea:
var="string to split"
read -ra arr -d '' <<<"$var"
(tenga en cuenta el espacio en -d ''
, no se puede dejar de lado), pero esto podría darle una nueva línea inesperada de <<<"$var"
(ya que esto agrega implícitamente un LF al final).
Ejemplo:
touch NOPE
var="* a *"
read -ra arr <<<"$var"
for a in "${arr[@]}"; do echo "[$a]"; done
Produce la esperado
[*]
[a]
[*]
Ya que esta solución (en contraste con todas las soluciones anteriores aquí) no es propensa a un desprendimiento inesperado y a menudo incontrolable de la cáscara.
También esto te da todo el poder de IFS como probablemente quieras: {[28]]}
Ejemplo:
IFS=: read -ra arr < <(grep "^$USER:" /etc/passwd)
for a in "${arr[@]}"; do echo "[$a]"; done
Produce algo como:
[tino]
[x]
[1000]
[1000]
[Valentin Hilbig]
[/home/tino]
[/bin/bash]
Como puedes ver, los espacios también se pueden conservar de esta manera:{[28]]}
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 en sí mismo, así que haga sus pruebas, algunos temas interesantes sobre esto:
-
unset IFS
: Ignora las ejecuciones de SPC, TAB, NL y on line comienza y termina -
IFS=''
: No hay separación de campo, solo lee todo -
IFS=' '
: Corridas de SPC (y solo SPC)
Algún ú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 que
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 usted no está acostumbrado a
$'ANSI-ESCAPED-STRING'
acostumbrarse a ella, es un ahorrador de tiempo.Si no incluye
-r
(como enread -a arr <<<"$var"
) entonces read hace escapes de barra invertida. Se deja como ejercicio para el lector.
Para la segunda pregunta:
Para probar algo en una cadena, generalmente me atengo a case
, ya que esto puede verificar varios casos a la vez (nota: case solo ejecuta la primera coincidencia, si necesita usar sentencias multiplce case
), y esta necesidad es a menudo el caso (juego de palabras previsto):
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
Así que puede establecer el valor devuelto para comprobar el SPC de esta manera:
case "$var" in (*' '*) true;; (*) false;; esac
¿Por qué case
? Debido a que por lo general 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.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-03-14 13:49:30
$ echo "This is a sentence." | tr -s " " "\012"
This
is
a
sentence.
Para comprobar los espacios, utilice grep:
$ echo "This is a sentence." | grep " " > /dev/null
$ echo $?
0
$ echo "Thisisasentence." | grep " " > /dev/null
$ echo $?
1
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-24 20:02:39
(A) Para dividir una oración en sus palabras (separadas por espacio) simplemente puede usar el IFS predeterminado usando
array=( $string )
Ejemplo ejecutando 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
Producirá
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 mob, pero de esta manera se almacena el array para cualquier necesidad posterior. Si solo necesitas un solo bucle, puedes usar su respuesta, que es una línea más corta:)
-- por favor refiérase a esta pregunta para métodos alternativos para dividir una cadena basada en delimitador.
(B) Para comprobar si hay un carácter en una cadena, también puede usar una expresión regular match.
Ejemplo para comprobar la presencia de un carácter de espacio se puede utilizar:
regex='\s{1,}'
if [[ "$sentence" =~ $regex ]]
then
echo "Space here!";
fi
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:26
Para comprobar espacios solo con bash:
[[ "$str" = "${str% *}" ]] && echo "no spaces" || echo "has spaces"
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-25 03:14:12