Tengo esta cadena almacenada en una variable:
IN="[email protected];[email protected]"
Ahora me gustaría dividir las cadenas por ;
delimitador para que tenga:
ADDR1="[email protected]"
ADDR2="[email protected]"
No necesariamente necesito las variables ADDR1
y ADDR2
. Si son elementos de una matriz, eso es aún mejor.
Después de las sugerencias de las respuestas a continuación, terminé con lo siguiente, que es lo que estaba buscando:
#!/usr/bin/env bash
IN="[email protected];[email protected]"
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 configurar Internal_field_separator (IFS) en ;
. No estoy seguro de lo que sucedió con esa respuesta, ¿cómo restablece los IFS
valores predeterminados?
RE: IFS
solución, probé esto y funciona, conservo el viejo IFS
y luego lo restauro:
IN="[email protected];[email protected]"
OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
echo "> [$x]"
done
IFS=$OIFS
Por cierto, cuando intenté
mails2=($IN)
Solo obtuve la primera cadena cuando la imprimí en bucle, sin corchetes alrededor $IN
funciona.
local IFS=...
cuando sea posible; (b) -1 paraunset IFS
, esto no restablece exactamente IFS a su valor predeterminado, aunque creo que un IFS no configurado se comporta igual que el valor predeterminado de IFS ($ '\ t \ n'), sin embargo, parece una mala práctica supongamos ciegamente que su código nunca será invocado con IFS configurado en un valor personalizado; (c) otra idea es invocar una subshell:(IFS=$custom; ...)
cuando la subshell salga, IFS volverá a lo que era originalmente.ruby -e "puts ENV.fetch('PATH').split(':')"
. Si quieres mantenerte puro, bash no ayudará, pero usar cualquier lenguaje de script que tenga una división incorporada es más fácil.for x in $(IFS=';';echo $IN); do echo "> [$x]"; done
\n
solo por un espacio. Entonces la línea final esmails=($(echo $IN | tr ";" " "))
. Así que ahora puedo verificar los elementosmails
utilizando la notación de matrizmails[index]
o simplemente iterando en un bucleRespuestas:
Puede establecer la variable del separador de campo interno (IFS) y luego dejar que se analice en una matriz. Cuando esto sucede en un comando, la asignación a
IFS
solo tiene lugar en el entorno de ese comando único (aread
). Luego analiza la entrada de acuerdo con elIFS
valor de la variable en una matriz, que luego podemos iterar.Analizará una línea de elementos separados por
;
, empujándolo en una matriz. Cosas para procesar todo$IN
, cada vez una línea de entrada separada por;
:fuente
IFS
en la misma línea queread
sin punto y coma u otro separador, en lugar de en un comando separado, lo aplica a ese comando, por lo que siempre se "restaura"; No necesita hacer nada manualmente.$IN
ser citado. El error se corrigió enbash
4.3.Tomado de la matriz dividida de script de shell Bash :
Explicación:
Esta construcción reemplaza todas las ocurrencias de
';'
(la inicial//
significa reemplazo global) en la cadenaIN
con' '
(un solo espacio), luego interpreta la cadena delimitada por espacios como una matriz (eso es lo que hacen los paréntesis circundantes).La sintaxis utilizada dentro de las llaves para reemplazar cada
';'
carácter con un' '
carácter se llama Expansión de parámetros .Hay algunas trampas comunes:
IFS=':'; arrIN=($IN); unset IFS;
IFS=$'\n'; arrIN=($IN); unset IFS;
fuente
IN="[email protected];[email protected];*;broken apart"
. En resumen: este enfoque se romperá si sus tokens contienen espacios y / o caracteres incrustados. como*
que sucede que un token coincida con los nombres de archivo en la carpeta actual.;*;
, entonces*
se expandirá a una lista de nombres de archivo en el directorio actual. -1Si no te importa procesarlos de inmediato, me gusta hacer esto:
Podría usar este tipo de bucle para inicializar una matriz, pero probablemente haya una forma más fácil de hacerlo. Sin embargo, espero que esto ayude.
fuente
IN="[email protected];[email protected];*;broken apart"
. En resumen: este enfoque se romperá si sus tokens contienen espacios y / o caracteres incrustados. como*
que sucede que un token coincida con los nombres de archivo en la carpeta actual.Respuesta compatible
Hay muchas formas diferentes de hacer esto en golpetazo.
Sin embargo, es importante tener en cuenta que
bash
tiene muchas características especiales (los llamados bashismos ) que no funcionarán en ningún otrocáscara.En particular, las matrices , las matrices asociativas y la sustitución de patrones , que se usan en las soluciones en esta publicación, así como en otras en el hilo, son bashismos y pueden no funcionar bajo otras capas que muchas personas usan.
Por ejemplo: en mi Debian GNU / Linux , hay un shell estándar llamadoguión; Conozco a muchas personas que les gusta usar otro shell llamadoksh; y también hay una herramienta especial llamadabusybox con su propio intérprete de shell (ceniza)
Cadena solicitada
La cadena que se dividirá en la pregunta anterior es:
Usaré una versión modificada de esta cadena para asegurarme de que mi solución sea robusta para las cadenas que contienen espacios en blanco, lo que podría romper otras soluciones:
División de cadena basada en delimitador en golpetazo (versión> = 4.2)
En puro
bash
, podemos crear una matriz con elementos divididos por un valor temporal para IFS (el separador de campo de entrada ). El IFS, entre otras cosas, le dicebash
qué carácter (s) debe tratar como un delimitador entre elementos al definir una matriz:En las versiones más recientes de
bash
, el prefijo de un comando con una definición de IFS cambia el IFS para ese comando solamente y lo restablece al valor anterior inmediatamente después. Esto significa que podemos hacer lo anterior en una sola línea:Podemos ver que la cadena
IN
se ha almacenado en una matriz llamadafields
, dividida en punto y coma:(También podemos mostrar el contenido de estas variables usando
declare -p
:)Tenga en cuenta que
read
es la forma más rápida de hacer la división porque no se llaman tenedores o recursos externos.Una vez que se define la matriz, puede usar un bucle simple para procesar cada campo (o, más bien, cada elemento de la matriz que ha definido ahora):
O bien, puede eliminar cada campo de la matriz después de procesar utilizando un enfoque de desplazamiento , que me gusta:
Y si solo desea una impresión simple de la matriz, ni siquiera necesita recorrerla:
Actualización: reciente golpetazo > = 4.4
En las versiones más recientes de
bash
, también puedes jugar con el comandomapfile
:¡Esta sintaxis conserva caracteres especiales, líneas nuevas y campos vacíos!
Si no desea incluir campos vacíos, puede hacer lo siguiente:
Con
mapfile
, también puede omitir la declaración de una matriz y "bucle" implícitamente sobre los elementos delimitados, llamando a una función en cada uno:(Nota: el
\0
final de la cadena de formato es inútil si no le interesan los campos vacíos al final de la cadena o no están presentes).O podría usar
<<<
, y en el cuerpo de la función incluir algún procesamiento para eliminar la nueva línea que agrega:División de cadena basada en delimitador en cáscara
Si no puede usar
bash
, o si desea escribir algo que pueda usarse en muchos shells diferentes, a menudo no puede usar bashisms , y esto incluye los arreglos que hemos estado usando en las soluciones anteriores.Sin embargo, no necesitamos usar matrices para recorrer los "elementos" de una cadena. Hay una sintaxis utilizada en muchos shells para eliminar subcadenas de una cadena desde la primera o la última aparición de un patrón. Tenga en cuenta que
*
es un comodín que significa cero o más caracteres:(La falta de este enfoque en cualquier solución publicada hasta ahora es la razón principal por la que escribo esta respuesta;)
Según lo explicado por Score_Under :
Usando la sintaxis anterior, podemos crear un enfoque donde extraemos "elementos" de la subcadena de la cadena eliminando las subcadenas hasta o después del delimitador.
El siguiente bloque de código funciona bien en golpetazo(incluidos los Mac OS
bash
),guión, kshy busybox's ceniza:¡Que te diviertas!
fuente
#
,##
,%
, y%%
sustituciones tienen lo que es la OMI una explicación más fácil de recordar (por lo mucho que eliminar):#
y%
eliminar la cadena coincidente más corto posible, y##
y%%
eliminar lo más largos posibles.IFS=\; read -a fields <<<"$var"
falla en los saltos de línea y añadir un salto de línea final. La otra solución elimina un campo vacío final.for sep in "#" "ł" "@" ; do ... var="${var#*$sep}" ...
He visto un par de respuestas que hacen referencia al
cut
comando, pero todas han sido eliminadas. 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
probablemente sea más eficiente, perocut
se puede usar y es más efectivo si desea extraer campos específicos del medio.Ejemplo:
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 tiene un archivo de registro delimitado con filas como esta:
cut
es muy útil poder acceder acat
este archivo y seleccionar un campo en particular para su posterior procesamiento.fuente
cut
, ¡es la herramienta adecuada para el trabajo! Mucho más claro que cualquiera de esos piratas informáticos.Esto funcionó para mí:
fuente
¿Qué tal este enfoque:
Fuente
fuente
IFS";" && Array=($IN)
$'...'
:IN=$'[email protected];[email protected];bet <d@\ns* kl.com>'
. Luegoecho "${Array[2]}"
imprimirá una cadena con nueva línea.set -- "$IN"
También es necesario en este caso. Sí, para evitar la expansión global, la solución debe incluirset -f
.Creo que AWK es el comando mejor y eficiente para resolver su problema. AWK se incluye por defecto en casi todas las distribuciones de Linux.
daré
Por supuesto, puede almacenar cada dirección de correo electrónico redefiniendo el campo de impresión awk.
fuente
inode=
en;
por ejemplosed -i 's/inode\=/\;/g' your_file_to_process
, a continuación, definir-F';'
cuando se apliqueawk
, la esperanza de que pueda ayudar.fuente
IN="this is first line; this is second line" arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )
, producirá una matriz de 8 elementos en este caso (un elemento para cada espacio de palabras separado), en lugar de 2 (un elemento para cada línea separada por punto y coma)arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )
para lograr eso, y aconsejar cambiar IFSIFS=$'\n'
para aquellos que aterrizarán aquí en el futuro y necesitan dividir una cadena que contenga espacios. (y para restaurarlo luego). :)Esto también funciona:
Tenga cuidado, esta solución no siempre es correcta. En caso de que pase "[email protected]" solamente, lo asignará a ADD1 y ADD2.
fuente
Una versión diferente de la respuesta de Darron , así es como lo hago:
fuente
IFS=";"
asignación existe solo en la$(...; echo $IN)
subshell; Es por eso que algunos lectores (incluyéndome a mí) inicialmente piensan que no funcionará. Supuse que ADDR1 estaba gastando todo $ IN. Pero nickjb es correcto; funciona La razón es que elecho $IN
comando analiza sus argumentos usando el valor actual de $ IFS, pero luego los repite a stdout usando un delimitador de espacio, independientemente de la configuración de $ IFS. Por lo tanto, el efecto neto es como si uno hubiera llamadoread ADDR1 ADDR2 <<< "[email protected] [email protected]"
(tenga en cuenta que la entrada está separada por espacios, no por separado).*
en elecho $IN
con una expansión de variables sin comillas.En Bash, una forma a prueba de balas, que funcionará incluso si su variable contiene nuevas líneas:
Mira:
El truco para que esto funcione es usar la
-d
opción deread
(delimitador) con un delimitador vacío, por lo queread
se ve obligado a leer todo lo que se alimenta. Y nos alimentamosread
con exactamente el contenido de la variablein
, sin nueva línea final gracias aprintf
. Tenga en cuenta que también estamos colocando el delimitadorprintf
para garantizar que la cadena que se pasaread
tiene un delimitador final. Sin él,read
se recortarían los posibles campos vacíos finales:se conserva el campo vacío final.
Actualización para Bash≥4.4
Desde Bash 4.4, el incorporado
mapfile
(también conocido comoreadarray
) admite la-d
opción de especificar un delimitador. Por lo tanto, otra forma canónica es:fuente
\n
espacios y*
simultáneamente. Además, no hay bucles; La variable de matriz es accesible en el shell después de la ejecución (al contrario de la respuesta más votada). Tenga enin=$'...'
cuenta que no funciona con comillas dobles. Creo que necesita más votos a favor.¿Qué tal este revestimiento, si no está utilizando matrices:
fuente
read -r ...
para asegurarse de que, por ejemplo, los dos caracteres "\ t" en la entrada terminen como los mismos dos caracteres en sus variables (en lugar de una sola pestaña).echo "ADDR1 $ADDR1"\n echo "ADDR2 $ADDR2"
a su fragmento generaráADDR1 [email protected] [email protected]\nADDR2
(\ n es nueva línea)IFS
y aquí cadenas que se corrigieron enbash
4.3. Las citas$IN
deberían arreglarlo. (En teoría,$IN
no está sujeto a la división de palabras o al engrosamiento después de que se expande, lo que significa que las citas deberían ser innecesarias. Sin embargo, incluso en 4.3, queda al menos un error, informado y programado para ser corregido, por lo que las citas siguen siendo buenas idea.)Sin configurar el IFS
Si solo tiene un colon, puede hacer eso:
conseguirás:
fuente
Aquí hay un 3-liner limpio:
donde
IFS
delimita palabras basadas en el separador y()
se usa para crear una matriz . Luego[@]
se utiliza para devolver cada elemento como una palabra separada.Si tiene algún código después de eso, también necesita restaurar
$IFS
, por ejemplounset IFS
.fuente
$in
comillas permite expandir los comodines.La siguiente función Bash / zsh divide su primer argumento en el delimitador dado por el segundo argumento:
Por ejemplo, el comando
rendimientos
Esta salida puede, por ejemplo, ser canalizada a otros comandos. Ejemplo:
En comparación con las otras soluciones dadas, esta tiene las siguientes ventajas:
IFS
no se anula: debido al alcance dinámico de incluso las variables locales, la anulación deIFS
un bucle hace que el nuevo valor se filtre en las llamadas a funciones realizadas desde dentro del bucle.Las matrices no se usan: leer una cadena en una matriz usando
read
requiere la bandera-a
en Bash y-A
en zsh.Si lo desea, la función se puede poner en un script de la siguiente manera:
fuente
help read
:-d delim continue until the first character of DELIM is read, rather than newline
puedes aplicar awk a muchas situaciones
también puedes usar esto
fuente
Hay una manera simple e inteligente como esta:
Pero debe usar gnu xargs, BSD xargs no puede admitir -d delim. Si usas apple mac como yo. Puede instalar gnu xargs:
entonces
fuente
Esta es la forma más sencilla de hacerlo.
fuente
Aquí hay algunas respuestas geniales (errator esp.), Pero para algo similar a dividirse en otros idiomas, que es lo que entendí que significaba la pregunta original, me decidí por esto:
Ahora
${a[0]}
,${a[1]}
etc., son como cabría esperar. Usar${#a[*]}
para varios términos. O para iterar, por supuesto:NOTA IMPORTANTE:
Esto funciona en casos donde no hay espacios de los que preocuparse, lo que resolvió mi problema, pero puede que no resuelva el tuyo. Vaya con la
$IFS
(s) solución (es) en ese caso.fuente
IN
contiene más de dos direcciones de correo electrónico. Consulte la misma idea (pero fija) en la respuesta de palindrom${IN//;/ }
(doble barra) para que también funcione con más de dos valores. Tenga en cuenta que cualquier comodín (*?[
) se expandirá. Y un campo vacío final será descartado.Salida
Sistema: Ubuntu 12.04.1
fuente
read
aquí y, por lo tanto, puede alterar el resto del código, si corresponde.Si no hay espacio, ¿por qué no esto?
fuente
Use el
set
incorporado para cargar la$@
matriz:Entonces, que comience la fiesta:
fuente
set -- $IN
para evitar algunos problemas con "$ IN" que comienza con el guión. Aún así, la expansión sin comillas de$IN
expandirá comodines (*?[
).Dos alternativas de bourne-ish donde ninguna requiere matrices bash:
Caso 1 : manténgalo simple y agradable: utilice una nueva línea como separador de registros ... por ejemplo.
Nota: en este primer caso, no se bifurca ningún subproceso para ayudar con la manipulación de la lista.
Idea: Quizás valga la pena usar NL de manera extensiva internamente , y solo convertirlo a un RS diferente al generar el resultado final externamente .
Caso 2 : Usando un ";" como un separador de registros ... por ejemplo.
En ambos casos, una sublista puede estar compuesta dentro del ciclo es persistente después de que el ciclo se haya completado. Esto es útil al manipular listas en la memoria, en lugar de almacenar listas en archivos. {ps mantén la calma y continúa B-)}
fuente
Además de las fantásticas respuestas que ya se proporcionaron, si solo se trata de imprimir los datos que puede considerar usar
awk
:Esto establece el separador de campo en
;
, para que pueda recorrer los campos con unfor
bucle e imprimir en consecuencia.Prueba
Con otra entrada:
fuente
En el shell de Android, la mayoría de los métodos propuestos simplemente no funcionan:
Lo que funciona es:
donde
//
significa reemplazo global.fuente
Salida:
Explicación: La asignación simple usando paréntesis () convierte la lista separada por punto y coma en una matriz siempre que tenga IFS correcto mientras lo hace. El bucle FOR estándar maneja elementos individuales en esa matriz como de costumbre. Tenga en cuenta que la lista dada para la variable IN debe ser "dura", es decir, con marcas simples.
IFS debe guardarse y restaurarse ya que Bash no trata una tarea de la misma manera que un comando. Una solución alternativa es envolver la asignación dentro de una función y llamar a esa función con un IFS modificado. En ese caso, no es necesario guardar / restaurar por separado IFS. Gracias por "Bize" por señalar eso.
fuente
!"#$%&/()[]{}*? are no problem
bueno ... no del todo:[]*?
son personajes glob. Entonces, ¿qué pasa con la creación de este directorio y archivo: `mkdir '!" # $% &'; Touch '! "# $% & / () [] {} Te tengo jajajaja - ¿no hay problema' y ejecutas tu comando? simple puede ser hermoso, pero cuando está roto, está roto.mkdir '!"#$%&'; touch '!"#$%&/()[]{} got you hahahaha - are no problem'
. Debo admitir que solo crearán un directorio y un archivo con nombres de aspecto extraño. A continuación, ejecute los comandos con la exactaIN
diste:IN='[email protected];[email protected];Charlie Brown <[email protected];!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
. Verá que no obtendrá el resultado que espera. Porque estás usando un método sujeto a expansiones de nombre de ruta para dividir tu cadena.*
,?
,[...]
e incluso, siextglob
se establece,!(...)
,@(...)
,?(...)
,+(...)
son problemas con este método!Ok chicos!
Aquí está mi respuesta!
¿Por qué este enfoque es "el mejor" para mí?
Por dos razones:
[]
fuente
/etc/os-release
y/etc/lsb-release
están destinados a ser obtenidos, y no analizados. Entonces tu método está realmente equivocado. Además, no estás respondiendo la pregunta sobre dividir una cadena en un delimitador.Una línea para dividir una cadena separada por ';' en una matriz es:
Esto solo establece IFS en una subshell, por lo que no tiene que preocuparse por guardar y restaurar su valor.
fuente
0: [email protected];[email protected]\n 1:
(\ n es una nueva línea)$IN
se cita para que no esté sujeto a la división IFS. 3. La sustitución del proceso se divide por espacios en blanco, pero esto puede corromper los datos originales.Quizás no sea la solución más elegante, pero funciona con
*
y espacios:Salidas
Otro ejemplo (delimitadores al principio y al final):
Básicamente, elimina todos los personajes que no sean,
;
pordelims
ejemplo, hacer .;;;
. Luego sefor
repite de1
anumber-of-delimiters
como lo cuenta${#delims}
. El paso final es obtener la$i
parte th de forma seguracut
.fuente