Respostas:
Use cut
com _
como delimitador de campo e obtenha os campos desejados:
A="$(cut -d'_' -f2 <<<'one_two_three_four_five')"
B="$(cut -d'_' -f4 <<<'one_two_three_four_five')"
Você também pode usar echo
e canalizar em vez da cadeia Here:
A="$(echo 'one_two_three_four_five' | cut -d'_' -f2)"
B="$(echo 'one_two_three_four_five' | cut -d'_' -f4)"
Exemplo:
$ s='one_two_three_four_five'
$ A="$(cut -d'_' -f2 <<<"$s")"
$ echo "$A"
two
$ B="$(cut -d'_' -f4 <<<"$s")"
$ echo "$B"
four
$ echo $FILE
my_user/my_folder/file.csv
$ A="$(cut -d'/' -f2 <<<"$FILE")"
$ echo $A
[file]*
Você sabe o que está acontecendo aqui?
echo "${s##*_}"
Usando apenas construções sh POSIX, é possível usar construções de substituição de parâmetro para analisar um delimitador por vez. Observe que esse código pressupõe que haja o número necessário de campos, caso contrário, o último campo será repetido.
string='one_two_three_four_five'
remainder="$string"
first="${remainder%%_*}"; remainder="${remainder#*_}"
second="${remainder%%_*}"; remainder="${remainder#*_}"
third="${remainder%%_*}"; remainder="${remainder#*_}"
fourth="${remainder%%_*}"; remainder="${remainder#*_}"
Como alternativa, você pode usar uma substituição de parâmetro sem aspas com a expansão de curinga desativada e IFS
configurada para o caractere delimitador (isso só funciona se o delimitador for um caractere não-espaço em branco único ou se qualquer sequência de espaço em branco for um delimitador).
string='one_two_three_four_five'
set -f; IFS='_'
set -- $string
second=$2; fourth=$4
set +f; unset IFS
Isso derruba os parâmetros posicionais. Se você fizer isso em uma função, apenas os parâmetros posicionais da função serão afetados.
Ainda outra abordagem é usar o read
builtin.
IFS=_ read -r first second third fourth trail <<'EOF'
one_two_three_four_five
EOF
unset IFS
não retorna IFS
ao padrão. Se depois disso alguém OldIFS="$IFS"
tiver um valor nulo no OldIFS. Além disso, supõe-se que o valor anterior do IFS seja o padrão, o que é muito possível (e útil) não ser. A única solução correta é armazenar old="$IFS"
e restaurar posteriormente com IFS = "$ old". Ou ... use um sub-shell (...)
. Ou, melhor ainda, leia minha resposta.
unset IFS
não restaura IFS
o valor padrão, mas retorna a divisão do campo para o efeito padrão. Sim, é uma limitação, mas geralmente aceitável na prática. O problema com um subshell é que precisamos extrair dados dele. Eu mostro uma solução que não muda o estado no final, com read
. (Ele funciona em shells POSIX, mas o IIRC não no shell Bourne porque executaria o read
subshell devido ao documento aqui.) Usar <<<
como na resposta é uma variante que funciona apenas em ksh / bash / zsh.
user/my_folder/[this_is_my_file]*
? O que eu obtenho quando sigo essas etapas é[this_is_my_file]*
/
.
Queria ver uma awk
resposta, então aqui está uma:
A=$(awk -F_ '{print $2}' <<< 'one_two_three_four_five')
B=$(awk -F_ '{print $4}' <<< 'one_two_three_four_five')
awk -F_ '{print $NF}' <<< 'one_two_3_4_five'
A maneira mais simples (para cascas com <<<) é:
IFS='_' read -r a second a fourth a <<<"$string"
Usando uma variável temporal em $a
vez de $_
porque um shell reclama.
Em um script completo:
string='one_two_three_four_five'
IFS='_' read -r a second a fourth a <<<"$string"
echo "$second $fourth"
Nenhuma alteração do IFS, nenhum problema com set -f
(expansão do nome do caminho) Nenhuma alteração nos parâmetros posicionais ("$ @").
Para uma solução portátil para todos os shells (sim, todos os POSIX incluídos) sem alterar o IFS ou set -f
, use o (um pouco mais complexo) equivalente heredoc:
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 documento em questão quanto o uso de <<<
removerão todas as novas linhas finais.
E isso foi desenvolvido para um conteúdo variável de "uma linha". As
soluções para várias linhas são possíveis, mas precisam de construções mais complexas.
Uma solução muito simples é possível na versão 4.4 do bash
readarray -d _ -t arr <<<"$string"
echo "array ${arr[1]} ${arr[3]}" # array numbers are zero based.
Não há equivalente para shells POSIX, pois muitos shells POSIX não possuem matrizes.
Para shells com matrizes pode ser tão simples quanto:
(testado trabalhando em attsh, lksh, mksh, ksh e bash)
set -f; IFS=_; arr=($string)
Mas com muito encanamento 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, as matrizes começam em 1 e não dividem a string por padrão.
Portanto, algumas alterações precisam ser feitas para que isso funcione no zsh.
read
são simples, desde que OP não quer extrair a 76 e os elementos 127 a partir de uma longa seqüência ...
readarray
poderia ser mais fácil de usar para essa situação.
Com zsh
você pode dividir a string (ativada _
) em uma matriz:
elements=(${(s:_:)string})
e acesse cada elemento / qualquer via índice de matriz:
print -r ${elements[4]}
Tenha em mente que em zsh
(ao contrário ksh
/ bash
) índices de matriz começam em 1 .
set -f
aviso à primeira solução. ... asteriscos, *
talvez?
set -f
? Eu não estou usando read
/ IFS
. Tente minhas soluções com uma string como *_*_*
ou o que quer ...
Outro exemplo estranho; mais simples de entender.
A=\`echo one_two_three_four_five | awk -F_ '{print $1}'\`
B=\`echo one_two_three_four_five | awk -F_ '{print $2}'\`
C=\`echo one_two_three_four_five | awk -F_ '{print $3}'\`
... and so on...
Também pode ser usado com variáveis.
Suponha:
this_str = "one_two_three_four_five"
Então o seguinte funciona:
A = `echo $ {this_str} | awk -F_ '{print $ 1}' '
B = `eco $ {this_str} | awk -F_ '{print $ 2}' '
C = `eco $ {this_str} | awk -F_ '{print $ 3}' '
... e assim por diante ...