Shells parecidos com Bourne / POSIX têm um operador split + glob e são invocados toda vez que você deixa uma expansão de parâmetro ( $var
, $-
...), substituição de comando ( $(...)
) ou expansão aritmética ( $((...))
) sem aspas no contexto da lista.
Na verdade, você invocou por engano quando criou for name in ${array[@]}
em vez de for name in "${array[@]}"
. (Na verdade, você deve tomar cuidado para invocar esse operador por engano como fonte de muitos bugs e vulnerabilidades de segurança ).
Esse operador é configurado com o parâmetro $IFS
special (para informar em quais caracteres dividir (embora tenha cuidado com o espaço, a guia e a nova linha recebam um tratamento especial)) e a opção -f
para desativar ( set -f
) ou habilite ( set +f
) a glob
part.
Observe também que, embora o S
em $IFS
tenha sido originalmente (no shell Bourne de onde vem $IFS
) para o eparator S , em shells POSIX, os caracteres em $IFS
deve ser visto como delimitadores ou terminadores (veja abaixo um exemplo).
Então, para dividir em _
:
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 a distinção entre separador e delimitador , experimente:
string='var1_var2_'
Isso dividirá em var1
e var2
apenas (sem elemento extra vazio).
Assim, para torná-lo semelhante ao split()
do JavaScript, você precisaria de uma etapa extra:
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
(observe que ele dividiria um elemento vazio $string
em 1 (não 0 ), como split()
do JavaScript).
Para ver a guia tratamentos especiais, o espaço e a nova linha, compare:
IFS=' '; string=' var1 var2 '
(onde você obtém var1
e var2
) com
IFS='_'; string='_var1__var2__'
onde você obtém: ''
, var1
, ''
, var2
, ''
.
Observe que o shell zsh
não invoca implicitamente o operador split + glob a menos que em sh
ou ksh
emulação. Lá, você tem que invocá-lo explicitamente. $=string
para a parte dividida, $~string
para a parte glob ( $=~string
para ambos) e também tem um operador split onde você pode especificar o separador:
array=(${(s:_:)string})
ou para preservar os elementos vazios:
array=("${(@s:_:)string}")
Observe que há s
para divisão , não delimitando (também com $IFS
, uma não conformidade conhecida POSIX de zsh
). É diferente do split()
do JavaScript em que uma string vazia é dividida em 0 (não 1).
Uma diferença notável com $IFS
-split é que ${(s:abc:)string}
divide na string abc
, enquanto com IFS=abc
, que seria dividido em a
, b
ou c
.
Com zsh
e ksh93
, o tratamento especial que o espaço, a guia ou a nova linha receber pode ser removido, dobrando-os em $IFS
.
Como uma nota histórica, o shell Bourne (o ancestral ou as atuais estruturas POSIX) sempre despojou os elementos vazios. Ele também tinha vários bugs relacionados à divisão e expansão de $ @ com valores não padrão de $IFS
. Por exemplo, IFS=_; set -f; set -- $@
não seria equivalente a IFS=_; set -f; set -- $1 $2 $3...
.
Divisão em regexps
Agora, para algo mais próximo do split()
do JavaScript que pode ser dividido em expressões regulares, você precisaria confiar em utilitários externos.
Na caixa de ferramentas POSIX, awk
tem um operador split
que pode dividir em expressões regulares estendidas (elas são mais ou menos um subconjunto das expressões regulares semelhantes a Perl suportadas por 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" '[_+]+'))"
O shell zsh
tem suporte embutido para expressões regulares compatíveis com Perl (em seu zsh/pcre
module), mas usá-lo para dividir uma string, embora possível, é relativamente incômodo.