Eu tenho uma linha (ou muitas linhas) de números que são delimitados por um caractere arbitrário. Quais ferramentas UNIX posso usar para classificar os itens de cada linha numericamente, mantendo o delimitador?
Exemplos incluem:
- lista de números; entrada
10 50 23 42
:; classificados:10 23 42 50
- Endereço de IP; entrada
10.1.200.42
:; classificados:1.10.42.200
- CSV; entrada
1,100,330,42
:; classificados:1,42,100,330
- delimitado por tubo; entrada
400|500|404
:; classificados:400|404|500
Como o delimitador é arbitrário, fique à vontade para fornecer (ou estender) uma Resposta usando um delimitador de um caractere de sua escolha.
sort
numeric-data
Jeff Schaller
fonte
fonte
cut
suporta delimitadores arbitrários com sua-d
opção.4,325 comma 55 comma 42,430
não ocorreriam nem1.5 period 4.2
).Respostas:
Você pode conseguir isso com:
substitua pontos
.
pelo seu delimitador.adicione
-u
aosort
comando acima para remover as duplicatas.ou com
gawk
( GNUawk
), podemos processar muitas linhas, enquanto o acima também pode ser estendido:substitua
*
como o separador de camposSEP='*'
pelo seu delimitador .Notas:
Pode ser necessário usar a
-g, --general-numeric-sort
opção, emsort
vez de,-n, --numeric-sort
para lidar com qualquer classe de números (número inteiro, número flutuante, científico, hexadecimal etc.).Em
awk
nenhuma necessidade de mudança, ele ainda vai lidar com isso.fonte
Usando
perl
há uma versão óbvia; divida os dados, classifique-os e junte-os novamente.O delimitador precisa ser listado duas vezes (uma vez na
split
e uma vez najoin
)por exemplo, para um
,
então
Como o
split
é um regex, o personagem pode precisar de citação:Usando as opções
-a
e-F
, é possível remover a divisão. Com o-p
loop, como antes, e defina os resultados como$_
, que serão impressos automaticamente:fonte
-l
opção em vez de usarchomp
. Isso também adiciona de volta a nova linha na impressão. Veja também-a
(com-F
) a parte de divisão.-l
e-F
, é ainda melhor:perl -F'/\./' -le 'print join(".", sort {$a <=> $b} @F)'
-l
opção; Eu tinha perdido isso!-F
flag originalmente porque ela não funciona corretamente em todas as versões (por exemplo, sua linha no CentOS 7 - perl 5.16.3 - retorna saída em branco, embora funcione bem no Debian 9). Mas, combinada a-p
ela, fornece um resultado um pouco menor, então eu adicionei isso como uma alternativa à resposta. mostrando como-F
pode ser usado. Obrigado!-a
e-n
opções quando-F
é utilizado e-n
quando-a
é usado ... então apenas mudar-le
para-lane
Usando Python e uma idéia semelhante à da resposta de Stephen Harris :
Então, algo como:
Infelizmente, ter que fazer a E / S manualmente torna isso muito menos elegante que a versão Perl.
fonte
Script Bash:
Exemplo:
Baseado em
Dividir string em uma matriz no Bash
Como classificar uma matriz no Bash
Unir elementos de uma matriz?
fonte
Concha
Carregar um idioma de nível superior leva tempo.
Por algumas linhas, o próprio shell pode ser uma solução.
Podemos usar o comando externo
sort
e do comandotr
. Uma é bastante eficiente na classificação de linhas e a outra é eficaz para converter um delimitador em novas linhas:Isso precisa do bash por causa do uso de
<<<
only. Se isso for substituído por um documento aqui, a solução é válida para o posix.Este é capaz de classificar campos com tabulações, espaços ou caracteres shell glob (
*
,?
,[
). Não novas linhas porque cada linha está sendo classificada.Mude
<<<"$2"
para<"$2"
para processar nomes de arquivos e chame-o como:O delimitador é o mesmo para o arquivo inteiro. Se isso é uma limitação, pode ser melhorado.
No entanto, um arquivo com apenas 6000 linhas leva 15 segundos para processar. Na verdade, o shell não é a melhor ferramenta para processar arquivos.
Awk
Por mais do que algumas linhas (mais do que algumas dez), é melhor usar uma linguagem de programação real. Uma solução awk pode ser:
O que leva apenas 0,2 segundos para o mesmo arquivo de 6000 linhas mencionado acima.
Entenda que os
<"$2"
arquivos for podem ser alterados novamente<<<"$2"
para linhas dentro de variáveis do shell.Perl
A solução mais rápida é perl.
Se você deseja classificar um arquivo, mude
<<<"$a"
para simplesmente"$a"
e adicione-i
às opções perl para tornar a edição do arquivo "no lugar":fonte
Usando
sed
para classificar octetos de um endereço IPsed
não possui umasort
função interna, mas se seus dados estiverem suficientemente restritos no intervalo (como nos endereços IP), você poderá gerar um script sed que implemente manualmente uma classificação de bolha simples . O mecanismo básico é procurar números adjacentes que estejam fora de ordem. Se os números estiverem fora de ordem, troque-os.O
sed
script em si contém dois comandos de busca e troca para cada par de números fora de ordem: um para os dois primeiros pares de octetos (forçando a presença de um delimitador à direita para marcar o final do terceiro octeto) e um segundo para o terceiro par de octetos (final com EOL). Se ocorrerem trocas, o programa se ramifica na parte superior do script, procurando números que estão fora de ordem. Caso contrário, ele sai.O script gerado é, em parte:
Essa abordagem codifica o período como delimitador, que precisa ser escapado, caso contrário, seria "especial" para a sintaxe da expressão regular (permitindo qualquer caractere).
Para gerar um script sed, esse loop fará:
Redirecione a saída desse script para outro arquivo, digamos
sort-ips.sed
.Uma amostra de execução pode parecer com:
A seguinte variação no script de geração usa os marcadores de limite de palavras
\<
e\>
para se livrar da necessidade da segunda substituição. Isso também reduz o tamanho do script gerado de 1,3 MB para pouco menos de 900 KB, além de reduzir bastante o tempo de execução emsed
si (para cerca de 50% a 75% do original, dependendo de qualsed
implementação está sendo usada):fonte
sed
é ridículo, e é por isso que é um desafio interessante.Aqui, uma festança que adivinha o delimitador por si só:
Pode não ser muito eficiente nem limpo, mas funciona.
Use como
bash my_script.sh "00/00/18/29838/2"
.Retorna um erro quando o mesmo delimitador não é usado consistentemente ou quando dois ou mais delimitadores se seguem.
Se o delimitador usado for um caractere especial, ele será escapado (caso contrário,
sed
retornará um erro).fonte
Essa resposta é baseada em um mal-entendido do Q., mas, em alguns casos, está correto de qualquer maneira. Se a entrada for números totalmente naturais e tiver apenas um delimitador por linha (como nos dados de amostra no Q.), ela funcionará corretamente. Ele também manipula arquivos com linhas que cada um tem seu próprio delimitador, o que é um pouco mais do que o solicitado.
Esta função shell
read
s da entrada padrão, utiliza o parâmetro de substituição POSIX para encontrar o delimitador específico em cada linha, (armazenado em$d
), e usostr
para substituir$d
com uma nova linha\n
esort
s dados dessa linha, em seguida, restaura delimitadores originais de cada linha:Aplicado aos dados fornecidos no OP :
Resultado:
fonte
Para delimitadores arbitrários:
Em uma entrada como:
Dá:
fonte
Isso deve lidar com qualquer delimitador sem dígito (0-9). Exemplo:
Resultado:
fonte
Com
perl
:With
ruby
, que é um pouco semelhante aperl
Comando personalizado e passagem apenas da string delimitadora (não regex). Funcionará se a entrada também tiver dados flutuantes
Comando personalizado para
perl
Outras leituras - Eu já tinha essa lista útil de one-liners perl / ruby
fonte
A seguir, é apresentada uma variação da resposta de Jeff, no sentido de que ele gera um
sed
script que será do tipo Bubble, mas é suficientemente diferente para garantir sua própria resposta.A diferença é que, em vez de gerar O (n ^ 2) expressões regulares básicas, isso gera O (n) expressões regulares estendidas. O script resultante terá cerca de 15 KB. O tempo de execução do
sed
script está em frações de segundo (demora um pouco mais para gerar o script).Ele é restrito à classificação de números inteiros positivos delimitados por pontos, mas não se limita ao tamanho dos números inteiros (apenas aumente
255
no loop principal) ou ao número de números inteiros. O delimitador pode ser alterado alterandodelim='.'
o código.Está pronto para acertar as expressões regulares, então deixarei de descrever os detalhes por mais um dia.
O script será mais ou menos assim:
A idéia por trás das expressões regulares geradas é padronizar a correspondência para números menores que cada número inteiro; esses dois números estão fora de ordem e, portanto, são trocados. As expressões regulares são agrupadas em várias opções OR. Preste muita atenção aos intervalos anexados a cada item, às vezes
{0}
, o que significa que o item imediatamente anterior deve ser omitido na pesquisa. As opções de regex, da esquerda para a direita, correspondem a números menores que o número especificado por:Para explicar um exemplo, use
101
(com espaços adicionais para facilitar a leitura):Aqui, a primeira alternância permite os números 100 a 100; a segunda alternância permite de 0 a 99.
Outro exemplo é
154
:Aqui a primeira opção permite 150 a 153; o segundo permite 100 a 149 e o último permite 0 a 99.
Testando quatro vezes em um loop:
Resultado:
fonte
Dividindo a entrada em várias linhas
Usando
tr
, você pode dividir a entrada usando um delimitador arbitrário em várias linhas.Essa entrada pode ser executada
sort
(usando-n
se a entrada for numérica).Se desejar reter o delimitador na saída, você poderá usá-lo
tr
novamente para adicionar novamente o delimitador.por exemplo, usando o espaço como delimitador
cat input.txt | tr " " "\n" | sort -n | tr "\n" " "
entrada:
1 2 4 1 4 32 18 3
saída:1 1 2 3 4 4 18 32
fonte