Analizar una matriz usando IFS con valores de espacio no blancos crea elementos vacíos.
Incluso usar tr -s
para reducir múltiples delimitaciones a una sola delimitación no es suficiente.
Un ejemplo puede explicar el problema con mayor claridad.
¿Hay alguna forma de lograr resultados "normales" mediante un ajuste de IFS (¿hay una configuración asociada para cambiar el comportamiento de IFS? ... es decir, actuar de la misma manera que el espacio en blanco predeterminado IFS.
var=" abc def ghi "
echo "============== IFS=<default>"
arr=($var)
for x in ${!arr[*]} ; do
echo "# arr[$x] \"${arr[x]}\""
done
#
sfi="$IFS" ; IFS=':'
set -f # Disable file name generation (globbing)
# (This data won't "glob", but unless globbing
# is actually needed, turn if off, because
# unusual/unexpected combinations of data can glob!
# and they can do it in the most obscure ways...
# With IFS, "you're not in Kansas any more! :)
var=":abc::def:::ghi::::"
echo "============== IFS=$IFS"
arr=($var)
for x in ${!arr[*]} ; do
echo "# arr[$x] \"${arr[x]}\""
done
echo "============== IFS=$IFS and tr"
arr=($(echo -n "$var"|tr -s "$IFS"))
for x in ${!arr[*]} ; do
echo "# arr[$x] \"${arr[x]}\""
done
set +f # enable globbing
IFS="$sfi" # re-instate original IFS val
echo "============== IFS=<default>"
Aquí está la salida
============== IFS=<default>
# arr[0] "abc"
# arr[1] "def"
# arr[2] "ghi"
============== IFS=:
# arr[0] ""
# arr[1] "abc"
# arr[2] ""
# arr[3] "def"
# arr[4] ""
# arr[5] ""
# arr[6] "ghi"
# arr[7] ""
# arr[8] ""
# arr[9] ""
============== IFS=: and tr
# arr[0] ""
# arr[1] "abc"
# arr[2] "def"
# arr[3] "ghi"
============== IFS=<default>
Respuestas:
Para eliminar múltiples caracteres delimitadores consecutivos (sin espacio), se pueden usar dos expansiones de parámetros (cadena / matriz). El truco consiste en establecer la
IFS
variable en la cadena vacía para la expansión del parámetro de matriz.Esto está documentado en
man bash
virtud de la división de palabras :fuente
IFS=' '
(es decir, un espacio en blanco) se comporta igual. Esto me resulta menos confuso que un argumento nulo explícito ("" o '') deIFS
.Desde la página de
bash
manual:Significa que el espacio en blanco IFS (espacio, tabulación y nueva línea) no se trata como los otros separadores. Si desea obtener exactamente el mismo comportamiento con un separador alternativo, puede hacer un intercambio de separador con la ayuda de
tr
osed
:La
%#%#%#%#%
cosa es un valor mágico para reemplazar los espacios posibles dentro de los campos, se espera que sea "único" (o muy poco vinculado). Si está seguro de que nunca habrá espacio en los campos, simplemente suelte esta parte).fuente
tr
ejemplos para mostrar el problema ... Quiero evitar una llamada al sistema, así que buscaré una opción bash más allá de la${var##:}
que mencioné en mi comentario al contestador de Glen ... Esperaré un momento ... tal vez hay una manera de convencer a IFS, de lo contrario, la primera parte de su respuesta fue después ...IFS
es el mismo en todos los shells de estilo Bourne, se especifica en POSIX .IFS
caracteres como cadena de delimitador. Mi pregunta fue mejor respondida porjon_d
, pero la respuesta de @ nazad muestra una forma ingeniosa de usarIFS
sin bucles ni aplicaciones de utilidad.Como bash IFS no proporciona una forma interna de tratar los caracteres delimitadores consecutivos como un único delimitador (para delimitadores que no son espacios en blanco), he creado una versión de todo bash (vs.utilizando una llamada externa, por ejemplo, tr, awk, sed )
Puede manejar IFS de múltiples caracteres.
A continuación se resu su tiempo de ejecución; ts, junto con pruebas similares para el
tr
yawk
opciones que aparecen en esta / Una página Q ... Las pruebas se basan en 10000 itterations de sólo la construcción de la arrray (sin E / S) ...Aquí está la salida
Aquí está el guión
fuente
También puedes hacerlo con gawk, pero no es bonito:
salidas
fuente
$var
a${var##:}
... Yo estaba realmente tras una manera de ajustar IFS sí .. yo quiero hacer esto sin una llamada externa (tengo la sensación de que bash puede hacer esto de manera más eficiente que cualquier otra externa ... así que seguiré en ese camino) ... su método funciona (+1) ... Hasta ahora a medida que avanza la modificación de la entrada, preferiría probarlo con bash, en lugar de awk o tr (evitaría una llamada al sistema), pero realmente estoy pasando el rato con un ajuste IFS ...bash 1.276s
...call (awk) 0m32.210s
,,,call (tr) 0m32.178s
... ¡Hazlo varias veces y podrías pensar que bash es lento! ... ¿Es awk más fácil en este caso? ... no si ya tienes el fragmento :) ... lo publicaré más tarde; debo irme ahora.var="The \"X\" factor:::A single '\"' crashes:::\"One Two\""
La respuesta simple es: colapsar todos los delimitadores en uno (el primero).
Eso requiere un bucle (que se ejecuta menos de
log(N)
veces):Todo lo que queda por hacer es dividir correctamente la cadena en un delimitador e imprimirla:
No es necesario
set -f
ni cambiar IFS.Probado con espacios, líneas nuevas y caracteres globales. Todo el trabajo. Bastante lento (como debería esperarse que sea un ciclo de shell).
Pero solo para bash (bash 4.4+ debido a la opción
-d
de readarray).sh
Una versión de shell no puede usar una matriz, la única matriz disponible son los parámetros posicionales.
Usar
tr -s
es solo una línea (IFS no cambia en el script):E imprimirlo:
Todavía lento, pero no mucho más.
El comando
command
no es válido en Bourne.En zsh,
command
llama solo a comandos externos y hace que eval falle sicommand
se usa.En ksh, incluso con
command
, el valor de IFS cambia en el ámbito global.Y
command
hace que la división falle en los shells relacionados con mksh (mksh, lksh, posh) Al eliminar el comando,command
el código se ejecuta en más shells. Pero: la eliminacióncommand
hará que IFS conserve su valor en la mayoría de los shells (eval es un valor incorporado especial) excepto en bash (sin modo posix) y zsh en modo predeterminado (sin emulación). No se puede hacer que este concepto funcione en zsh predeterminado con o sincommand
.Múltiples caracteres IFS
Sí, IFS podría tener varios caracteres, pero cada carácter generará un argumento:
Saldrá:
Con bash, puede omitir la
command
palabra si no está en la emulación sh / POSIX. El comando fallará en ksh93 (IFS mantiene el valor cambiado). En zsh, el comandocommand
hace que zsh intente buscareval
como un comando externo (que no encuentra) y falla.Lo que sucede es que los únicos caracteres IFS que se contraen automáticamente en un delimitador son espacios en blanco IFS.
Un espacio en IFS colapsará todos los espacios consecutivos en uno. Una pestaña colapsará todas las pestañas. Un espacio y una pestaña contraerán series de espacios y / o pestañas en un delimitador. Repita la idea con nueva línea.
Para colapsar varios delimitadores se requieren algunos malabarismos.
Suponiendo que ASCII 3 (0x03) no se usa en la entrada
var
:La mayoría de los comentarios sobre ksh, zsh y bash (about
command
e IFS) todavía se aplican aquí.Un valor de
$'\0'
sería menos probable en la entrada de texto, pero las variables bash no pueden contener NUL (0x00
).No hay comandos internos en sh para hacer las mismas operaciones de cadena, por lo que tr es la única solución para los scripts sh.
fuente
command eval
IIRC por Gilles