Eu tenho essa string armazenada em uma variável:
IN="[email protected];[email protected]"
Agora eu gostaria de dividir as strings por ;
delimitador para que eu tenha:
ADDR1="[email protected]"
ADDR2="[email protected]"
Eu não necessariamente precisa do ADDR1
e ADDR2
variáveis. Se eles são elementos de uma matriz, é ainda melhor.
Após sugestões das respostas abaixo, acabei com o seguinte:
#!/usr/bin/env bash
IN="[email protected];[email protected]"
mails=$(echo $IN | tr ";" "\n")
for addr in $mails
do
echo "> [$addr]"
done
Resultado:
> [bla@some.com]
> [john@home.com]
Havia uma solução envolvendo a configuração Internal_field_separator (IFS) como ;
. Não sei o que aconteceu com essa resposta, como você redefineIFS
padrão?
RE: IFS
solução, eu tentei isso e funciona, eu mantenho o antigo IFS
e depois restauro:
IN="[email protected];[email protected]"
OIFS=$IFS
IFS=';'
mails2=$IN
for x in $mails2
do
echo "> [$x]"
done
IFS=$OIFS
BTW, quando eu tentei
mails2=($IN)
Eu só consegui a primeira string ao imprimi-la em loop, sem colchetes ao redor $IN
dela.
local IFS=...
que possível; (b) -1 paraunset IFS
, isso não redefine exatamente o IFS para seu valor padrão, embora eu acredite que um IFS não configurado se comporte da mesma forma que o valor padrão do IFS ($ '\ t \ n'), no entanto, parece uma má prática assumindo cegamente que seu código nunca será chamado com o IFS definido como um valor personalizado; (c) outra idéia é invocar um subshell:(IFS=$custom; ...)
quando o subshell sair, o IFS retornará ao que era originalmente.ruby -e "puts ENV.fetch('PATH').split(':')"
. Se você deseja permanecer puro, o bash não ajudará, mas é mais fácil usar qualquer linguagem de script que tenha uma divisão interna.for x in $(IFS=';';echo $IN); do echo "> [$x]"; done
\n
por apenas um espaço. Então a linha final émails=($(echo $IN | tr ";" " "))
. Então agora eu posso verificar os elementos domails
usando a notação de matrizmails[index]
ou apenas iteração em um loopRespostas:
Você pode definir a variável separador interno de campo (IFS) e, em seguida, deixá-la analisar em uma matriz. Quando isso acontece em um comando, a atribuição a
IFS
ocorre apenas no ambiente desse comando (aread
). Em seguida, analisa a entrada de acordo com oIFS
valor da variável em uma matriz, sobre a qual podemos iterar.Ele analisará uma linha de itens separados por
;
, empurrando-a para uma matriz. Material para processar todo$IN
, cada vez que uma linha de entrada é separada por;
:fonte
IFS
na mesma linha doread
sem ponto-e-vírgula ou outro separador, em oposição a um comando separado, faz o escopo para esse comando - para que seja sempre "restaurado"; você não precisa fazer nada manualmente.$IN
ser citado. O bug foi corrigido nobash
4.3.Retirado da matriz dividida do script do shell Bash :
Explicação:
Essa construção substitui todas as ocorrências de
';'
(a inicial//
significa substituição global) na cadeia de caracteresIN
por' '
(um único espaço) e interpreta a cadeia delimitada por espaço como uma matriz (é o que os parênteses circundantes fazem).A sintaxe usada dentro das chaves para substituir cada
';'
caractere por um' '
caractere é denominada Expansão de parâmetro .Existem algumas dicas comuns:
IFS=':'; arrIN=($IN); unset IFS;
IFS=$'\n'; arrIN=($IN); unset IFS;
fonte
IN="[email protected];[email protected];*;broken apart"
. Em resumo: essa abordagem será interrompida se seus tokens contiverem espaços incorporados e / ou caracteres. como*
esse, faz com que um token corresponda aos nomes dos arquivos na pasta atual.;*;
, ela*
será expandida para uma lista de nomes de arquivos no diretório atual. -1Se você não se importa em processá-los imediatamente, eu gosto de fazer isso:
Você pode usar esse tipo de loop para inicializar uma matriz, mas provavelmente existe uma maneira mais fácil de fazer isso. Espero que isso ajude, no entanto.
fonte
IN="[email protected];[email protected];*;broken apart"
. Em resumo: essa abordagem será interrompida se seus tokens contiverem espaços incorporados e / ou caracteres. como*
esse, faz com que um token corresponda aos nomes dos arquivos na pasta atual.Resposta compatível
Existem várias maneiras diferentes de fazer isso em bater.
No entanto, é importante notar que existem
bash
muitos recursos especiais (os chamados basismos ) que não funcionarão em nenhum outroConcha.Em particular, matrizes , matrizes associativas e substituição padrão , que são utilizados nas soluções neste post, bem como outros no segmento, são bashisms e não podem trabalhar sob outras conchas que muitas pessoas usam.
Por exemplo: no meu Debian GNU / Linux , existe um padrão shell chamadotraço; Conheço muitas pessoas que gostam de usar outro shell chamadoksh; e também há uma ferramenta especial chamadabusybox com seu próprio interpretador de shell (cinza)
Sequência solicitada
A string a ser dividida na pergunta acima é:
Usarei uma versão modificada dessa cadeia para garantir que minha solução seja robusta para cadeias que contenham espaço em branco, o que poderia interromper outras soluções:
Dividir string com base no delimitador em bater (versão> = 4.2)
Em termos puros
bash
, podemos criar uma matriz com elementos divididos por um valor temporário para o IFS (o separador de campos de entrada ). O IFS, entre outras coisas, informabash
quais caracteres deve ser tratado como um delimitador entre os elementos ao definir uma matriz:Em versões mais recentes do
bash
, prefixando um comando com uma definição IFS altera as IFS para esse comando única e redefine para o valor anterior imediatamente depois. Isso significa que podemos fazer o acima em apenas uma linha:Podemos ver que a string
IN
foi armazenada em uma matriz chamadafields
, dividida em ponto e vírgula:(Também podemos exibir o conteúdo dessas variáveis usando
declare -p
:)Observe que
read
é a maneira mais rápida de fazer a divisão, porque não há garfos ou recursos externos chamados.Depois que a matriz é definida, você pode usar um loop simples para processar cada campo (ou melhor, cada elemento da matriz que você definiu agora):
Ou você pode soltar cada campo da matriz após o processamento usando uma abordagem de deslocamento , que eu gosto:
E se você quer apenas uma impressão simples da matriz, não precisa nem fazer um loop sobre ela:
Atualização: recente bater > = 4.4
Nas versões mais recentes do
bash
, você também pode jogar com o comandomapfile
:Essa sintaxe preserva caracteres especiais, novas linhas e campos vazios!
Se você não quiser incluir campos vazios, faça o seguinte:
Com
mapfile
, você também pode pular a declaração de uma matriz e implicitamente "fazer um loop" sobre os elementos delimitados, chamando uma função em cada:(Nota: a
\0
sequência no final da sequência de formatação é inútil se você não se importa com campos vazios no final da sequência ou eles não estiverem presentes.)Ou você pode usar
<<<
e, no corpo da função, incluir algum processamento para descartar a nova linha que ele adiciona:Dividir string com base no delimitador em Concha
Se você não pode usar
bash
, ou se deseja escrever algo que possa ser usado em muitas conchas diferentes, geralmente não pode usar basismos - e isso inclui as matrizes que usamos nas soluções acima.No entanto, não precisamos usar matrizes para fazer um loop sobre os "elementos" de uma string. Há uma sintaxe usada em muitos shells para excluir substrings de uma string da primeira ou da última ocorrência de um padrão. Observe que
*
é um curinga que representa zero ou mais caracteres:(A falta dessa abordagem em qualquer solução postada até agora é o principal motivo pelo qual estou escrevendo esta resposta;)
Conforme explicado por Score_Under :
Usando a sintaxe acima, podemos criar uma abordagem na qual extraímos "elementos" de substring da string excluindo as substrings até ou após o delimitador.
O código de bloqueio abaixo funciona bem em bater (incluindo Mac OS's
bash
),traço, kshe busyboxé cinza:Diverta-se!
fonte
#
,##
,%
, e%%
substituições têm o que é IMO uma explicação mais fácil de lembrar (para o quanto eles delete):#
e%
apagar a menor cadeia correspondente possível, e##
e%%
eliminar o mais longo possível.IFS=\; read -a fields <<<"$var"
falha nas novas linhas e adiciona uma nova linha à direita. A outra solução remove um campo vazio à direita.for sep in "#" "ł" "@" ; do ... var="${var#*$sep}" ...
Vi algumas respostas referenciando o
cut
comando, mas todas foram excluídas. É um pouco estranho que ninguém tenha elaborado isso, porque acho que é um dos comandos mais úteis para fazer esse tipo de coisa, especialmente para analisar arquivos de log delimitados.No caso de dividir este exemplo específico em uma matriz de scripts bash,
tr
é provavelmente mais eficiente, mascut
pode ser usado e é mais eficaz se você deseja extrair campos específicos do meio.Exemplo:
Obviamente, você pode colocar isso em um loop e iterar o parâmetro -f para extrair cada campo independentemente.
Isso se torna mais útil quando você tem um arquivo de log delimitado com linhas como esta:
cut
é muito útil para poder acessarcat
esse arquivo e selecionar um campo específico para processamento adicional.fonte
cut
, é a ferramenta certa para o trabalho! Muito limpo do que qualquer um desses hacks de shell.Isso funcionou para mim:
fonte
Que tal essa abordagem:
Fonte
fonte
IFS";" && Array=($IN)
$'...'
:IN=$'[email protected];[email protected];bet <d@\ns* kl.com>'
. Em seguidaecho "${Array[2]}"
, imprimirá uma string com nova linha.set -- "$IN"
também é necessário neste caso. Sim, para impedir a expansão glob, a solução deve incluirset -f
.Acho que o AWK é o comando melhor e eficiente para resolver seu problema. O AWK é incluído por padrão em quase todas as distribuições Linux.
darei
É claro que você pode armazenar cada endereço de e-mail redefinindo o campo awk print.
fonte
inode=
em;
por exemplosed -i 's/inode\=/\;/g' your_file_to_process
, em seguida, definir-F';'
quando se aplicamawk
, esperança de que pode ajudá-lo.fonte
IN="this is first line; this is second line" arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )
irão produzir uma matriz de 8 elementos, neste caso, (um para cada elemento de espaço palavra separados), em vez de duas (uma para cada elemento de linha cólon semi separados)arrIN=( $( echo "$IN" | sed -e 's/;/\n/g' ) )
para conseguir isso, e conselhos para mudar o IFSIFS=$'\n'
para aqueles que pousarem aqui no futuro e precisam dividir uma sequência contendo espaços. (e para restaurá-lo depois). :)Isso também funciona:
Cuidado, esta solução nem sempre está correta. Caso você passe "[email protected]" apenas, ele será atribuído a ambos ADD1 e ADD2.
fonte
Uma visão diferente da resposta de Darron , é assim que eu faço:
fonte
IFS=";"
atribuição existe apenas no$(...; echo $IN)
subshell; é por isso que alguns leitores (inclusive eu) inicialmente pensam que não vai funcionar. Eu assumi que todo o $ IN estava sendo absorvido pelo ADDR1. Mas o nickjb está correto; isso funciona. O motivo é que oecho $IN
comando analisa seus argumentos usando o valor atual de $ IFS, mas os ecoa para stdout usando um delimitador de espaço, independentemente da configuração de $ IFS. Portanto, o efeito líquido é como se alguém tivesse chamadoread ADDR1 ADDR2 <<< "[email protected] [email protected]"
(observe que a entrada não é separada por espaço; é separada).*
noecho $IN
com uma expansão de variável não citada.No Bash, à prova de balas, isso funcionará mesmo que sua variável contenha novas linhas:
Veja:
O truque para isso funcionar é usar a
-d
opção deread
(delimitador) com um delimitador vazio, para queread
seja forçado a ler tudo o que é alimentado. E nós alimentamosread
exatamente o conteúdo da variávelin
, sem nova linha à direita graças aprintf
. Observe que também estamos colocando o delimitadorprintf
para garantir que a sequência passadaread
tenha um delimitador à direita. Sem ele,read
apararia possíveis campos vazios à direita:o campo vazio à direita é preservado.
Atualização para Bash≥4.4
Desde o Bash 4.4, o built-in
mapfile
(akareadarray
) suporta a-d
opção de especificar um delimitador. Portanto, outra maneira canônica é:fonte
\n
espaços e*
simultaneamente. Além disso, sem loops; A variável array pode ser acessada no shell após a execução (ao contrário da resposta mais votada). Observein=$'...'
que ele não funciona com aspas duplas. Eu acho que precisa de mais votos.Que tal esse liner único, se você não estiver usando matrizes:
fonte
read -r ...
para garantir que, por exemplo, os dois caracteres "\ t" na entrada acabem com os mesmos dois caracteres em suas variáveis (em vez de um único caractere de tabulação).echo "ADDR1 $ADDR1"\n echo "ADDR2 $ADDR2"
à sua saída vontade trechoADDR1 [email protected] [email protected]\nADDR2
(\ n é de nova linha)IFS
isso ocorreu devido a um erro envolvendo as cordas aqui corrigidas nobash
4.3. A citação$IN
deve corrigi-lo. (Em teoria,$IN
não está sujeito a divisão de palavras ou globbing após a expansão, o que significa que as aspas devem ser desnecessárias. Mesmo em 4.3, no entanto, há pelo menos um bug restante - relatado e programado para ser corrigido -, portanto, a citação continua sendo uma boa opção. idéia).Sem configurar o IFS
Se você tiver apenas dois pontos, poderá fazer isso:
você vai ter:
fonte
Aqui está um 3-liner limpo:
onde
IFS
delimita as palavras com base no separador e()
é usado para criar uma matriz . Em seguida,[@]
é usado para retornar cada item como uma palavra separada.Se você tiver algum código depois disso, também precisará restaurar
$IFS
, por exemplounset IFS
.fonte
$in
aspas permite expandir caracteres curinga.A seguinte função Bash / zsh divide seu primeiro argumento no delimitador fornecido pelo segundo argumento:
Por exemplo, o comando
rendimentos
Essa saída pode, por exemplo, ser canalizada para outros comandos. Exemplo:
Comparado com as outras soluções fornecidas, esta possui as seguintes vantagens:
IFS
não é substituída: devido ao escopo dinâmico de variáveis locais, a substituição deIFS
um loop faz com que o novo valor vaze nas chamadas de função realizadas de dentro do loop.Matrizes não são usadas: a leitura de uma string em uma matriz usando
read
requer o sinalizador-a
no Bash e-A
no zsh.Se desejado, a função pode ser colocada em um script da seguinte maneira:
fonte
help read
:-d delim continue until the first character of DELIM is read, rather than newline
você pode aplicar o awk a muitas situações
Também você pode usar isso
fonte
Existe uma maneira simples e inteligente como esta:
Mas você deve usar o gnu xargs, o BSD xargs não pode suportar -d delim. Se você usa o Apple Mac como eu. Você pode instalar o gnu xargs:
então
fonte
Esta é a maneira mais simples de fazer isso.
fonte
Existem algumas respostas legais aqui (errator esp.), Mas para algo análogo se dividir em outros idiomas - que é o que eu entendi a pergunta original -, decidi sobre isso:
Agora
${a[0]}
,${a[1]}
etc, são como você esperaria. Use${#a[*]}
para o número de termos. Ou para iterar, é claro:NOTA IMPORTANTE:
Isso funciona nos casos em que não há espaços com que se preocupar, o que resolveu o meu problema, mas pode não resolver o seu. Vá com a
$IFS
(s) solução (s) nesse caso.fonte
IN
contém mais de dois endereços de email. Por favor, referir-se a mesma idéia (mas fixo) na resposta de Palindrom${IN//;/ }
(barra dupla) para que ele também funcione com mais de dois valores. Cuidado que qualquer curinga (*?[
) será expandido. E um campo vazio à direita será descartado.Resultado
Sistema: Ubuntu 12.04.1
fonte
read
daqui e, portanto, pode perturbar o restante do código, se houver.Se não houver espaço, por que não isso?
fonte
Use o
set
built-in para carregar a$@
matriz:Então, deixe a festa começar:
fonte
set -- $IN
para evitar alguns problemas com "$ IN" começando com o hífen. Ainda assim, a expansão não cotada de$IN
expandirá curingas (*?[
).Duas alternativas de bourne-ish em que nenhuma delas exige matrizes de bash:
Caso 1 : Mantenha-o agradável e simples: use uma Nova Linha como separador de registros ... por exemplo.
Nota: neste primeiro caso, nenhum subprocesso é bifurcado para ajudar na manipulação da lista.
Idéia: Talvez valha a pena usar NL extensivamente internamente e apenas converter em um RS diferente ao gerar o resultado final externamente .
Caso 2 : Usando um ";" como um separador de registros ... por exemplo.
Nos dois casos, uma sub-lista pode ser composta dentro do loop é persistente após a conclusão do loop. Isso é útil ao manipular listas na memória, em vez de armazenar listas em arquivos. {ps mantenha a calma e continue B-)}
fonte
Além das respostas fantásticas que já foram fornecidas, se for apenas uma questão de imprimir os dados, considere usar
awk
:Isso define o separador de campo como
;
, para que ele possa percorrer os campos com umfor
loop e imprimir adequadamente.Teste
Com outra entrada:
fonte
No shell do Android, a maioria dos métodos propostos simplesmente não funciona:
O que funciona é:
onde
//
significa substituição global.fonte
Resultado:
Explicação: A atribuição simples usando parênteses () converte a lista separada por ponto e vírgula em uma matriz, desde que você tenha o IFS correto ao fazer isso. O loop FOR padrão lida com itens individuais nessa matriz, como de costume. Observe que a lista fornecida para a variável IN deve ser "difícil" entre aspas, ou seja, com marcações simples.
O IFS deve ser salvo e restaurado, pois o Bash não trata uma atribuição da mesma maneira que um comando. Uma solução alternativa é agrupar a atribuição dentro de uma função e chamar essa função com um IFS modificado. Nesse caso, não é necessário salvar / restaurar separadamente o IFS. Obrigado por "Bize" por apontar isso.
fonte
!"#$%&/()[]{}*? are no problem
bem ... não exatamente:[]*?
são personagens globais. Então, que tal criar este diretório e arquivo: `mkdir '!" # $% &'; Touch '! "# $% & / () [] {} Te pegou hahahaha - não há problema' e executando seu comando? simples pode ser bonito, mas quando está quebrado, está quebrado.mkdir '!"#$%&'; touch '!"#$%&/()[]{} got you hahahaha - are no problem'
. Eles apenas criarão um diretório e um arquivo, com nomes estranhos, devo admitir. Em seguida, execute os comandos com o exatoIN
que você deu:IN='[email protected];[email protected];Charlie Brown <[email protected];!"#$%&/()[]{}*? are no problem;simple is beautiful :-)'
. Você verá que não obterá o resultado esperado. Porque você está usando um método sujeito a expansões de nome de caminho para dividir sua string.*
,?
,[...]
e até mesmo, seextglob
for definida,!(...)
,@(...)
,?(...)
,+(...)
são problemas com este método!Ok pessoal!
Aqui está a minha resposta!
Por que essa abordagem é "a melhor" para mim?
Por duas razões:
[]
fonte
/etc/os-release
e/etc/lsb-release
devem ser adquiridos e não analisados. Portanto, seu método está realmente errado. Além disso, você não está respondendo bem à pergunta sobre o spiltting de uma string em um delimitador.Uma linha para dividir uma sequência separada por ';' em uma matriz é:
Isso define apenas o IFS em um subshell, para que você não precise se preocupar em salvar e restaurar seu valor.
fonte
0: [email protected];[email protected]\n 1:
(\ n é uma nova linha)$IN
é citado para que não esteja sujeito à divisão do IFS. 3. A substituição do processo é dividida por espaços em branco, mas isso pode corromper os dados originais.Talvez não seja a solução mais elegante, mas trabalha com
*
espaços:Saídas
Outro exemplo (delimitadores no início e no final):
Basicamente, ele remove todos os personagens que não
;
fazerdelims
, por exemplo.;;;
. Em seguida, ele faz umfor
loop de1
paranumber-of-delimiters
conforme contado${#delims}
. O passo final é obter a$i
parte th com segurançacut
.fonte