A maneira mais simples (para shells com < < <) é:
IFS='_' read -r a second a fourth a <<<"$string"
Usando uma variável temporal $a
em vez de $_
porque uma shell reclama.
Em um script completo:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<<"$string"
echo "$second $fourth"
Não há alteração do IFS, nem problemas com set -f
(expansão do nome do caminho) Nenhuma alteração nos parâmetros posicionais ("$ @").
Para uma solução portátil para todas shells (sim, todos os POSIX incluídos) sem alterar o IFS ou set -f
, use o equivalente heredoc (um pouco mais complexo):
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<-_EOF_
$string
_EOF_
echo "$second $fourth"
Entenda que essas soluções (tanto o aqui-doc quanto o uso de <<<
removerão todas as novas linhas finais.
E isso é projetado para um conteúdo variável "one liner".
Soluções para multi-liners são possíveis, mas precisam de construções mais complexas.
Uma solução muito simples é possível na versão bash 4.4
readarray -d _ -t arr <<<"$string"
echo "array ${arr[1]} ${arr[3]}" # array numbers are zero based.
Não há equivalente para shells POSIX, já que muitos shells POSIX não possuem arrays.
Para shells que possuem arrays podem ser simples como:
(testado trabalhando em attsh, lksh, mksh, ksh e bash)
set -f; IFS=_; arr=($string)
Mas com muita canalização adicional para manter e redefinir variáveis e opções:
string='one_* *_three_four_five'
case $- in
*f*) noglobset=true; ;;
*) noglobset=false;;
esac
oldIFS="$IFS"
set -f; IFS=_; arr=($string)
if $noglobset; then set -f; else set +f; fi
echo "two=${arr[1]} four=${arr[3]}"
No zsh, os arrays começam em 1 e não dividem a string por padrão.
Portanto, algumas mudanças precisam ser feitas para que isso funcione em zsh.