Se fizermos:
VAR=100:200:300:400
Nós podemos fazer:
echo ${VAR%%:*}
100
e
echo ${VAR##*:}
400
Existe algum equivalente que eu possa usar para obter os valores de 200 e 300?
Por exemplo, uma maneira de obter apenas o que está entre o segundo e o terceiro dois pontos, especificamente no caso de haver mais de três dois pontos?
O estilo ksh (e agora especificado pelo POSIX para sh)
${var#pattern}
e${var%pattern}
(e variantes gananciosas com##
e%%
) removem apenas o texto do início e do fim, respectivamente, do conteúdo da variável.Para
200
sair100:200:300:400
desses operadores, você precisaria aplicar ambos${VAR#*:}
para remover100:
do início e depois${VAR%%:*}
para remover:300:400
do final.Isso seria com
${${VAR#*:}%%:*}
, mas embora isso funcione no zsh, não funciona no bash ou ksh, que não permitem encadear operadores de expansão de parâmetros.Em
zsh
, você prefere usar$VAR[(ws[:])2]
para obter a2
segunda ordem:
s
separada de ¹, ou usar para primeiro dividir a variável em uma matriz e depois obter o segundo elemento.w
$var
${${(s[:])VAR}[2]}
No ksh93, você pode fazer
${VAR/*:@(*):*:*/\1}
, mas embora o bash (como o zsh²) tenha copiado o operador do ksh93${param/pattern/replacement}
, ele não copiou a parte de captura e referência.No bash (que ainda é amplamente utilizado apesar de todas as suas limitações, já que é o shell GNU, pré-instalado em praticamente todos os sistemas GNU/Linux, bem como em alguns sistemas não GNU), você pode fazer isso em duas etapas:
O único operador de divisão interno do bash (a menos que você queira considerar o bash 4.4+,
readarray -d
que é mais para ler registros de algum fluxo de entrada em uma matriz; e o tipo de divisão que ele faz separando argumentos em sua sintaxe), que é a divisão IFS no estilo Bourne (estendida pelo Korn), que é executada em expansões sem aspas (você a usou por enganoecho ${VAR%%:*}
onde esqueceu as aspas) e porread
.read
funciona em uma linha, portanto, só pode ser usado para variáveis que não contêm caracteres de quebra de linha. Usar split+glob (glob sendo o outro efeito colateral de deixar uma expansão sem aspas) é trabalhoso, pois precisamos desabilitar a parte glob e alterar um$IFS
parâmetro global.No bash 4.4+, você pode fazer assim:
Sem o
''
,100::300:
seria dividido em "100", "", "300" apenas, então, por exemplo,$#
expandiria para 3, mesmo que a variável tivesse:
um campo separado por 4. Atenção, porém, pois isso significa que uma variável vazia é dividida em 1 elemento vazio em vez de nenhum.Para
read
(oureadarray
), uma solução alternativa semelhante seria adicionar um delimitador extra ao final da entrada. Como as variáveis bash (ao contrário das do zsh) não podem conter caracteres NUL, comread
valores de variáveis arbitrários, você poderia fazer:(
- 1
porqueread
começa a preencher o array no índice 0, não 1;LC_ALL=C
contorna alguns bugs para texto não codificado na codificação do usuário nas versões bash 5.0 a 5.2)Com
readarray
(bash 4.4):Novamente,
:
adicionado no final para evitar que um elemento vazio final seja descartado (mas novamente significa que uma entrada vazia resulta em um elemento vazio).¹ Observe que ele se divide
::a:b::c:::
ema
,b
ec
somente como a divisão IFS fazia no shell Bourne (mas não em shells modernos do tipo Bourne, exceto quando espaço, tabulação ou nova linha são usados como separadores).² O zsh suporta isso, mas com uma sintaxe diferente e isso exige que a
extendedglob
opção seja habilitada:${VAR/(#b)*:(*):*:*/$match[1]}
Não tento usar a sintaxe de substituição no valor da string separada por dois pontos. Em vez disso, uso a divisão para colocar as partes entre dois pontos em um array e, em seguida, realizar a substituição de variáveis (e até mesmo o fatiamento do array) para retornar os elementos desejados do array:
produz a saída:
Isso pode não ter os efeitos precisos que você deseja se caracteres de espaço em branco também aparecerem entre dois pontos no valor original (você deve testar essas coisas cuidadosamente), mas seu exemplo não tinha nenhum.
Você pode usar IFS e definir para separar e acessar todos os quatro:
Isso exigiria muita expansão manual de parâmetros. Você poderia fazer um loop usando o delimitador, algo como
Saída:
Com uma versão do Bash que tenha
loadable builtin
chamadodsv
isso, é possível analisar uma string como o quecut
está sendo feito. Você pode tentar algo como:Saída:
Se alguns campos estiverem vazios, como abaixo:
A
-g
opção pode ser usada para ignorá-lo/removê-lo.Em alguns casos, a entrada pode ter campos entre aspas duplas, como abaixo.
A análise entende e ignora strings entre aspas duplas.
Saída:
bash
eu sei, não está incluído.De acordo com
help dsv