Los shells tipo Bourne / POSIX tienen un operador split + glob y se invoca cada vez que deja una expansión de parámetro ( $var
, $-
...), una sustitución de comando ( $(...)
) o una expansión aritmética ( $((...))
) sin comillas en el contexto de la lista.
En realidad, lo invocaste por error cuando lo hiciste en for name in ${array[@]}
lugar de hacerlo for name in "${array[@]}"
. (En realidad, debe tener cuidado de que invocar a ese operador por error es fuente de muchos errores y vulnerabilidades de seguridad ).
Ese operador está configurado con el $IFS
parámetro especial (para indicar en qué caracteres dividir (aunque tenga cuidado con que el espacio, la pestaña y la nueva línea reciban un tratamiento especial allí)) y la -f
opción de deshabilitar ( set -f
) o habilitar ( set +f
) la glob
parte.
También tenga en cuenta que si bien el S
in $IFS
era originalmente (en el shell Bourne de donde $IFS
proviene) para el Sseparador, en los shells POSIX, los caracteres en $IFS
deberían verse como delimitadores o terminadores (ver a continuación un ejemplo).
Entonces para dividir _
:
string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
array=($string) # invoke the split+glob operator
for i in "${array[@]}"; do # loop over the array elements.
Para ver la distinción entre separador y delimitador , pruebe:
string='var1_var2_'
Eso lo dividirá en var1
y var2
solo (sin elemento vacío adicional).
Entonces, para que sea similar a JavaScript split()
, necesitarías un paso adicional:
string='var1_var2_var3'
IFS=_ # delimit on _
set -f # disable the glob part
temp=${string}_ # add an extra delimiter
array=($temp) # invoke the split+glob operator
(tenga en cuenta que dividiría un elemento vacío $string
en 1 (no 0 ), como JavaScript split()
).
Para ver la pestaña de tratamientos especiales, recibir espacio y nueva línea, compare:
IFS=' '; string=' var1 var2 '
(donde consigues var1
y var2
) con
IFS='_'; string='_var1__var2__'
donde se obtiene: ''
, var1
, ''
, var2
, ''
.
Tenga en cuenta que el zsh
shell no invoca ese operador split + glob implícitamente así, a menos que esté en sh
o ksh
emulación. Allí, debes invocarlo explícitamente. $=string
para la parte dividida, $~string
para la parte glob ( $=~string
para ambos), y también tiene un operador dividido donde puede especificar el separador:
array=(${(s:_:)string})
o para preservar los elementos vacíos:
array=("${(@s:_:)string}")
Tenga en cuenta que existe s
para dividir , no delimitar (también con $IFS
una no conformidad POSIX conocida de zsh
). Es diferente de JavaScript split()
en que una cadena vacía se divide en 0 (no 1) elemento.
Una diferencia notable con $IFS
-splitting es que se ${(s:abc:)string}
divide en la abc
cadena, mientras que con IFS=abc
, se dividiría en a
, b
o c
.
Con zsh
y ksh93
, el tratamiento especial que recibe el espacio, la pestaña o la nueva línea se puede eliminar al duplicarlos $IFS
.
Como nota histórica, el shell Bourne (el ancestro o los shell POSIX modernos) siempre despojó a los elementos vacíos. También tenía una serie de errores relacionados con la división y expansión de $ @ con valores no predeterminados de $IFS
. Por ejemplo IFS=_; set -f; set -- $@
, no sería equivalente a IFS=_; set -f; set -- $1 $2 $3...
.
División en expresiones regulares
Ahora, para algo más cercano a JavaScript split()
que puede dividirse en expresiones regulares, necesitaría confiar en utilidades externas.
En el cofre de herramientas POSIX, awk
tiene un split
operador que puede dividirse en expresiones regulares extendidas (que son más o menos un subconjunto de las expresiones regulares similares a Perl compatibles con JavaScript).
split() {
awk -v q="'" '
function quote(s) {
gsub(q, q "\\" q q, s)
return q s q
}
BEGIN {
n = split(ARGV[1], a, ARGV[2])
for (i = 1; i <= n; i++) printf " %s", quote(a[i])
exit
}' "$@"
}
string=a__b_+c
eval "array=($(split "$string" '[_+]+'))"
El zsh
shell tiene soporte incorporado para expresiones regulares compatibles con Perl (en su zsh/pcre
módulo), pero usarlo para dividir una cadena, aunque es posible, es relativamente engorroso.
shell
estás usando, con lobash
que puedes hacerIFS='_' read -a array <<< "${string}"