A análise de uma matriz usando o IFS com valores de espaço não brancos cria elementos vazios.
Mesmo o uso tr -s
para encolher várias delimitações em uma única delim não é suficiente.
Um exemplo pode explicar o problema com mais clareza.
Existe uma maneira de obter resultados "normais" por meio de um ajuste do IFS (existe uma configuração associada para alterar o comportamento do IFS? ... ou seja, para agir da mesma forma que o espaço em branco padrão IFS.
var=" abc def ghi "
echo "============== IFS=<default>"
arr=($var)
for x in ${!arr[*]} ; do
echo "# arr[$x] \"${arr[x]}\""
done
#
sfi="$IFS" ; IFS=':'
set -f # Disable file name generation (globbing)
# (This data won't "glob", but unless globbing
# is actually needed, turn if off, because
# unusual/unexpected combinations of data can glob!
# and they can do it in the most obscure ways...
# With IFS, "you're not in Kansas any more! :)
var=":abc::def:::ghi::::"
echo "============== IFS=$IFS"
arr=($var)
for x in ${!arr[*]} ; do
echo "# arr[$x] \"${arr[x]}\""
done
echo "============== IFS=$IFS and tr"
arr=($(echo -n "$var"|tr -s "$IFS"))
for x in ${!arr[*]} ; do
echo "# arr[$x] \"${arr[x]}\""
done
set +f # enable globbing
IFS="$sfi" # re-instate original IFS val
echo "============== IFS=<default>"
Aqui está a saída
============== IFS=<default>
# arr[0] "abc"
# arr[1] "def"
# arr[2] "ghi"
============== IFS=:
# arr[0] ""
# arr[1] "abc"
# arr[2] ""
# arr[3] "def"
# arr[4] ""
# arr[5] ""
# arr[6] "ghi"
# arr[7] ""
# arr[8] ""
# arr[9] ""
============== IFS=: and tr
# arr[0] ""
# arr[1] "abc"
# arr[2] "def"
# arr[3] "ghi"
============== IFS=<default>
Respostas:
Para remover vários caracteres delimitadores consecutivos (sem espaço), duas expansões de parâmetros (string / array) podem ser usadas. O truque é definir a
IFS
variável como a sequência vazia para a expansão do parâmetro da matriz.Isso está documentado no
man bash
sob repartição de palavras :fonte
IFS=' '
(ou seja, um espaço em branco) se comporta da mesma maneira. Acho isso menos confuso do que um argumento nulo explícito ("" ou '') deIFS
.Da página de
bash
manual:Isso significa que o espaço em branco do IFS (espaço, tabulação e nova linha) não é tratado como os outros separadores. Se você deseja obter exatamente o mesmo comportamento com um separador alternativo, pode fazer alguma troca de separador com a ajuda de
tr
oused
:A
%#%#%#%#%
coisa é um valor mágico para substituir os possíveis espaços dentro dos campos; espera-se que seja "único" (ou muito pouco). Se tiver certeza de que nunca haverá espaço nos campos, basta soltar esta parte).fonte
tr
exemplos mostrem o problema ... eu quero evitar uma chamada do sistema, então examinarei uma opção do bash além da${var##:}
que mencionei no meu comentário para o pesquisador de Glen .... . Eu vou esperar por um tempo .. talvez haja uma maneira de IFS coaxiais, caso contrário, a primeira parte da sua resposta for estava atrás ....IFS
é o mesmo em todas as conchas no estilo Bourne, é especificado no POSIX .IFS
caracteres como delimitador-string. Minha pergunta foi melhor respondida porjon_d
, mas a resposta de @ nazad mostra uma maneira bacana de usar,IFS
sem loops e sem aplicativos utilitários.Como o IFS bash não fornece uma maneira interna de tratar caracteres de delimitador consecutivos como um delimitador único (para delimitadores que não são de espaço em branco), eu montei uma versão totalmente básica (vs.usando uma chamada externa, por exemplo, tr, awk, sed )
Ele pode lidar com IFS com vários caracteres.
Aqui estão os resultados do tempo de execução, juntamente com testes semelhantes para as opções
tr
eawk
mostrados nesta página de perguntas e respostas ... Os testes são baseados em 10000 iterações de apenas construir o arrray (sem E / S) ...Aqui está a saída
Aqui está o script
fonte
Você pode fazer isso com o gawk também, mas não é bonito:
saídas
fonte
$var
para${var##:}
... Estava realmente procurando uma maneira de ajustar o próprio IFS .. Quero para fazer isso sem uma chamada externa (tenho a sensação de que o bash pode fazer isso com mais eficiência do que qualquer lata externa ... portanto, continuarei nessa trilha) ... seu método funciona (+1) .... como modifica a entrada, eu preferiria tentar com bash, em vez de awk ou tr (isso evitaria uma chamada do sistema), mas eu estou realmente esperando por um ajuste do IFS ...bash 1.276s
...call (awk) 0m32.210s
,,,call (tr) 0m32.178s
... Do que algumas vezes e você pode pensar que o bash é lento! ... é awk mais fácil neste caso? ... não se você já possui o trecho :) ... eu o publicarei mais tarde; tenho que ir agora.var="The \"X\" factor:::A single '\"' crashes:::\"One Two\""
A resposta simples é: reduza todos os delimitadores para um (o primeiro).
Isso requer um loop (que é executado menos que o
log(N)
tempo):Tudo o que resta a fazer é dividir corretamente a sequência em um delimitador e imprimi-la:
Não há necessidade de
set -f
nem IFS mudança.Testado com espaços, novas linhas e caracteres glob. Todo o trabalho. Muito lento (como se espera que um loop de shell seja).
Mas apenas para o bash (bash 4.4+ por causa da opção
-d
de readarray).sh
Uma versão shell não pode usar uma matriz, a única matriz disponível são os parâmetros posicionais.
Usar
tr -s
é apenas uma linha (o IFS não muda no script):E imprima:
Ainda lento, mas não muito mais.
O comando
command
é inválido no Bourne.No zsh,
command
chama apenas comandos externos e faz com que eval falhe secommand
for usado.Em ksh, mesmo com
command
, o valor do IFS é alterado no escopo global.E
command
faz com que a divisão falhe em shells relacionados ao mksh (mksh, lksh, posh) A remoção do comandocommand
faz com que o código seja executado em mais shells. Mas: a remoçãocommand
fará com que o IFS mantenha seu valor na maioria dos shells (eval é um built-in especial), exceto no bash (sem modo posix) e zsh no modo padrão (sem emulação). Esse conceito não pode ser criado para funcionar no zsh padrão com ou semcommand
.IFS de vários caracteres
Sim, o IFS pode ter vários caracteres, mas cada caractere irá gerar um argumento:
Saída:
Com o bash, você pode omitir a
command
palavra se não estiver na emulação sh / POSIX. O comando falhará no ksh93 (o IFS mantém o valor alterado). No zsh, o comandocommand
faz com que o zsh tente encontrareval
como um comando externo (que não encontra) e falha.O que acontece é que os únicos caracteres IFS recolhidos automaticamente para um delimitador são os espaços em branco do IFS.
Um espaço no IFS recolherá todos os espaços consecutivos para um. Uma guia recolherá todas as guias. Um espaço e uma guia recolherão execuções de espaços e / ou guias em um delimitador. Repita a ideia com nova linha.
Para recolher vários delimitadores, é necessário algum malabarismo.
Supondo que ASCII 3 (0x03) não seja usado na entrada
var
:A maioria dos comentários sobre ksh, zsh e bash (about
command
e IFS) ainda se aplica aqui.Um valor de
$'\0'
seria menos provável na entrada de texto, mas as variáveis bash não podem conter NULs (0x00
).Não há comandos internos no sh para executar as mesmas operações de cadeia, portanto, tr é a única solução para scripts sh.
fonte
command eval
IIRC por Gilles