Pregunta ¿Cómo puedo dividir una cadena en un delimitador en Bash?


Tengo esta cadena almacenada en una variable:

IN="bla@some.com;john@home.com"

Ahora me gustaría dividir las cuerdas ; delimitador para que yo tenga:

ADDR1="bla@some.com"
ADDR2="john@home.com"

No necesariamente necesito el ADDR1 y ADDR2 variables. Si son elementos de una matriz que es aún mejor.


Después de las sugerencias de las respuestas a continuación, terminé con lo siguiente, que es lo que buscaba:

#!/usr/bin/env bash

IN="bla@some.com;john@home.com"

mails=$(echo $IN | tr ";" "\n")

for addr in $mails
do
    echo "> [$addr]"
done

Salida:

> [bla@some.com]
> [john@home.com]

Había una solución que implicaba establecer Internal_field_separator (IFS) a ;. No estoy seguro de lo que pasó con esa respuesta, ¿cómo se restablece IFS volver al valor predeterminado?

RE: IFS solución, probé esto y funciona, guardo el viejo IFS y luego restaurarlo:

IN="bla@some.com;john@home.com"

OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
    echo "> [$x]"
done

IFS=$OIFS

Por cierto, cuando lo intenté

mails2=($IN)

Solo obtuve la primera cadena cuando la imprimí en loop, sin corchetes $IN funciona.


1507
2018-05-28 02:03


origen


Respuestas:


Puedes configurar el separador de campo interno (IFS) variable, y luego dejarlo analizar en una matriz. Cuando esto sucede en un comando, entonces la asignación a IFS solo tiene lugar en el entorno de ese único comando (para read ) Luego analiza la entrada de acuerdo con el IFS valor variable en una matriz, que luego podemos iterar.

IFS=';' read -ra ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
    # process "$i"
done

Analizará una línea de elementos separados por ;, empujándolo en una matriz. Cosas para procesar todo de $IN, cada vez una línea de entrada separada por ;:

 while IFS=';' read -ra ADDR; do
      for i in "${ADDR[@]}"; do
          # process "$i"
      done
 done <<< "$IN"

922
2018-05-28 02:23



Tomado de Bash shell script split array:

IN="bla@some.com;john@home.com"
arrIN=(${IN//;/ })

Explicación:

Esta construcción reemplaza todas las ocurrencias de ';' (la inicial // significa reemplazo global) en la cadena IN con ' ' (un solo espacio), luego interpreta la cadena delimitada por espacios como una matriz (eso es lo que hacen los paréntesis que la rodean).

La sintaxis utilizada dentro de las llaves para reemplazar cada ';' personaje con un ' ' personaje se llama Expansión de parámetros.

Hay algunos errores comunes:

  1. Si la cadena original tiene espacios, deberá usar IFS:
    • IFS=':'; arrIN=($IN); unset IFS;
  2. Si la cadena original tiene espacios y el delimitador es una nueva línea, puede establecer IFS con:
    • IFS=$'\n'; arrIN=($IN); unset IFS;

743
2018-03-10 09:00



Si no te importa procesarlos inmediatamente, me gusta hacer esto:

for i in $(echo $IN | tr ";" "\n")
do
  # process
done

Podría utilizar este tipo de bucle para inicializar una matriz, pero probablemente haya una manera más fácil de hacerlo. Espero que esto ayude, sin embargo.


207
2018-05-28 02:09



Respuesta compatible

Para esta pregunta SO, ya hay muchas maneras diferentes de hacer esto en . Pero bash tiene muchos especial características, llamadas bashism eso funciona bien, pero eso no funcionará en ningún otro .

En particular, matrices, matriz asociativay sustitución de patrones son puros bashisms y puede no funcionar bajo otras conchas.

En mi Debian GNU / Linux, hay un estándar shell llamado , pero conozco a mucha gente a la que le gusta usar .

Finalmente, en una situación muy pequeña, hay una herramienta especial llamada  con su propio intérprete de shell ()

Cadena solicitada

La muestra de cadena en la pregunta SO es:

IN="bla@some.com;john@home.com"

Como esto podría ser útil con espacios en blanco y como espacios en blanco podría modificar el resultado de la rutina, prefiero usar esta cadena de muestra:

 IN="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

División de cadena basada en delimitador en  (versión> = 4.2)

Debajo puro bash, podemos usar matrices y IFS:

var="bla@some.com;john@home.com;Full Name <fulnam@other.org>"

oIFS="$IFS"
IFS=";"
declare -a fields=($var)
IFS="$oIFS"
unset oIFS


121
2018-04-13 14:20



¿Qué tal este enfoque?

IN="bla@some.com;john@home.com" 
set -- "$IN" 
IFS=";"; declare -a Array=($*) 
echo "${Array[@]}" 
echo "${Array[0]}" 
echo "${Array[1]}" 

Fuente


80
2018-05-28 10:31



He visto un par de respuestas que hacen referencia al cut comando, pero todos han sido eliminados. Es un poco extraño que nadie haya explicado eso, porque creo que es uno de los comandos más útiles para hacer este tipo de cosas, especialmente para analizar archivos de registro delimitados.

En el caso de dividir este ejemplo específico en una matriz de script bash, tr es probablemente más eficiente, pero cut puede ser utilizado, y es más efectivo si desea extraer campos específicos del centro.

Ejemplo:

$ echo "bla@some.com;john@home.com" | cut -d ";" -f 1
bla@some.com
$ echo "bla@some.com;john@home.com" | cut -d ";" -f 2
john@home.com

Obviamente, puede poner eso en un bucle e iterar el parámetro -f para extraer cada campo de forma independiente.

Esto se vuelve más útil cuando tienes un archivo de registro delimitado con filas como esta:

2015-04-27|12345|some action|an attribute|meta data

cut es muy útil para poder cat este archivo y seleccione un campo particular para su posterior procesamiento.


74
2018-04-27 18:20



Esto funcionó para mí:

string="1;2"
echo $string | cut -d';' -f1 # output is 1
echo $string | cut -d';' -f2 # output is 2

65
2017-08-11 20:45



echo "bla@some.com;john@home.com" | sed -e 's/;/\n/g'
bla@some.com
john@home.com

59
2018-05-28 02:12



Esto también funciona:

IN="bla@some.com;john@home.com"
echo ADD1=`echo $IN | cut -d \; -f 1`
echo ADD2=`echo $IN | cut -d \; -f 2`

Tenga cuidado, esta solución no siempre es correcta. En caso de que pase "bla@some.com" solamente, lo asignará a ADD1 y ADD2.


57
2017-09-08 05:01