De acordo com as Leis 12.965/2014 e 13.709/2018, que regulam o uso da Internet e o tratamento de dados pessoais no Brasil, ao me inscrever na newsletter do portal DICAS-L, autorizo o envio de notificações por e-mail ou outros meios e declaro estar ciente e concordar com seus Termos de Uso e Política de Privacidade.

Uso avançado do comando "sort" no Linux

Colaboração: Rubens Queiroz de Almeida

Data de Publicação: 26 de agosto de 2026

O comando sort costuma ser apresentado apenas como uma ferramenta para colocar linhas em ordem alfabética. Em scripts e rotinas de administração, porém, seus recursos mais interessantes estão na criação de chaves complexas, ordenações estáveis, processamento de arquivos extensos, controle de localidade, mesclagem de dados previamente ordenados e tratamento seguro de nomes de arquivos.

Este tutorial parte do princípio de que o leitor já conhece comandos básicos como:

$ sort arquivo
$ sort -n numeros.txt
$ sort -r arquivo

Nosso foco será explorar situações em que a escolha incorreta das opções pode produzir resultados discretamente errados.

1. Como o "sort" realmente compara as linhas

Por padrão, o sort compara linhas completas segundo as regras da localidade ativa:

$ sort arquivo.txt

A ordenação pode ser influenciada por variáveis como:

LANG
LC_ALL
LC_COLLATE
LC_NUMERIC
LC_TIME

Confira a configuração:

$ locale

LC_COLLATE determina as regras de comparação textual. LC_NUMERIC influencia a interpretação dos números e LC_TIME afeta recursos como a ordenação pelos nomes dos meses.

Para obter uma comparação baseada nos valores dos bytes:

$ LC_ALL=C sort arquivo.txt

Essa forma possui duas vantagens importantes em scripts:

  • produz resultados previsvisíveis entre sistemas;
  • geralmente é mais rápida do que uma ordenação linguística.

Compare:

$ printf '%s\n' \
    ação \
    arvore \
    Árvore \
    casa |
    sort

com:

$ printf '%s\n' \
    ação \
    arvore \
    Árvore \
    casa |
    LC_ALL=C sort

Os resultados podem ser diferentes porque a primeira execução aplica as regras linguísticas da localidade atual, enquanto a segunda compara a representação dos caracteres.

Em scripts que alimentam outros comandos, use frequentemente:

$ LC_ALL=C sort

Em relatórios destinados à leitura humana, a localidade do usuário pode produzir uma ordem mais natural.

2. A anatomia de uma chave de ordenação

A opção -k define uma chave:

 -k INÍCIO,FIM 

Cada posição pode ser especificada como:

 CAMPO.CARACTERE 

Por exemplo:

$ sort -k2.3,2.7 arquivo.txt

Essa chave:

  • começa no terceiro caractere do segundo campo;
  • termina no sétimo caractere do segundo campo.

Quando a posição do caractere é omitida, são usados estes padrões:

  • no início da chave, primeiro caractere do campo;
  • no fim da chave, último caractere do campo.

Assim:

$ sort -k2,2 arquivo.txt

usa somente o segundo campo.

Já:

$ sort -k2 arquivo.txt

usa tudo desde o início do segundo campo até o fim da linha.

Essa diferença é uma das causas mais comuns de resultados inesperados.

3. Aplicando opções a chaves individuais

As opções de comparação podem ser globais:

$ sort -n -k3,3 arquivo.txt

ou associadas diretamente à chave:

$ sort -k3,3n arquivo.txt

A segunda forma é mais clara quando existem múltiplas chaves com regras diferentes.

Considere servidores.txt:

web02 producao 32 98.7
db01 producao 128 74.2
web01 homologacao 16 31.5
db02 producao 64 74.2
web03 homologacao 32 45.0

Os campos representam:

  1. nome;
  2. ambiente;
  3. memória;
  4. uso de CPU.

Para ordenar alfabeticamente pelo ambiente e numericamente pela memória, em ordem decrescente:

$ sort \
-k2,2 \ -k3,3nr \ servidores.txt

O modificador nr pertence somente à segunda chave.

Para acrescentar o nome do servidor como desempate:

$ sort \
    -k2,2 \
    -k3,3nr \
    -k1,1 \
    servidores.txt

Essa forma deixa explícita toda a hierarquia da ordenação.

4. Entendendo o desempate implícito

Quando todas as chaves informadas são iguais, o GNU sort normalmente usa a linha completa como último critério de desempate.

Considere:

10 Carlos
10 Ana
10 Beatriz

Execute:

$ sort -k1,1n arquivo.txt

Embora a chave numérica seja igual, os nomes poderão ser reorganizados:

10 Ana
10 Beatriz
10 Carlos

Isso acontece porque as linhas completas são usadas no desempate.

Para preservar a ordem original entre chaves iguais, use ordenação estável:

$ sort -s -k1,1n arquivo.txt

Resultado:

10 Carlos
10 Ana
10 Beatriz

A opção -s, de *stable*, desativa o desempate final pela linha completa.

Ela é indispensável quando a ordem original já representa:

  • prioridade;
  • sequência cronológica;
  • ordem de chegada;
  • resultado de uma ordenação anterior;
  • preferência definida em outra etapa.

5. Ordenação por múltiplas passagens

Uma ordenação estável permite construir prioridades em etapas.

Considere:

Ana Vendas 5200
Carlos Suporte 4300
Beatriz Vendas 6100
Daniel Suporte 4800
Eduardo Vendas 4800

Primeiro, ordene pelo salário:

$ sort -s -k3,3n funcionarios.txt > etapa1.txt

Depois, ordene pelo departamento:

$ sort -s -k2,2 etapa1.txt

Como a segunda ordenação é estável, a ordem salarial é preservada dentro de cada departamento.

O mesmo resultado pode ser obtido em uma única execução:

$ sort \
    -k2,2 \
    -k3,3n \
    funcionarios.txt

A abordagem em múltiplas passagens pode ser útil quando:

  • os critérios são produzidos por etapas diferentes;
  • uma ordenação intermediária precisa ser examinada;
  • dados previamente classificados recebem um novo agrupamento;
  • as chaves não estão todas disponíveis ao mesmo tempo.

6. A diferença entre "-n" e "-g"

O GNU sort oferece duas comparações numéricas importantes.

A opção -n:

$ sort -n valores.txt

é apropriada para números comuns:

-12.5
0
3.14
150
2000

A opção -g, de *general numeric sort*:

$ sort -g valores.txt

reconhece uma variedade maior de representações, como:

1e6
2.7e-3
NaN
-inf
inf

Exemplo:

$ printf '%s\n' \
    1e3 \
    25 \
    3.5e2 \
    0.004 |
    LC_ALL=C sort -g

Resultado:

0.004
25
3.5e2
1e3

Use -n para números decimais comuns. Use -g quando houver notação científica, infinitos ou representações especiais.

7. Ordenando valores legíveis para humanos

A opção -h interpreta sufixos de magnitude:

$ sort -h

Ela reconhece valores como:

900K
4M
1.5G
2T

Um uso comum:

$ du -h --max-depth=1 |
    sort -hr

Para evitar problemas provocados por nomes com espaços, podemos fazer com que du coloque o tamanho em uma chave separada por tabulação, como já ocorre normalmente, e limitar a ordenação ao primeiro campo:

$ du -h --max-depth=1 |
    sort -t$'\t' -k1,1hr

Nesse caso:

-k1,1hr

significa:

  • primeira coluna;
  • comparação de tamanhos legíveis;
  • ordem inversa.

Isso evita que o nome do caminho participe do desempate desejado. Se dois tamanhos forem iguais e quisermos preservar a ordem original:

$ du -h --max-depth=1 |
    sort -s -t$'\t' -k1,1hr

8. Ordenando versões corretamente

Uma comparação textual não compreende a estrutura de versões:

aplicacao-1.9
aplicacao-1.10
aplicacao-1.11
aplicacao-1.2

Use:

$ sort -V versoes.txt

Resultado:

aplicacao-1.2
aplicacao-1.9
aplicacao-1.10
aplicacao-1.11

Isso também resolve a ordenação natural de nomes numerados:

$ printf '%s\n' \
    capitulo20 \
    capitulo3 \
    capitulo100 \
    capitulo10 |
    sort -V

Resultado:

capitulo3
capitulo10
capitulo20
capitulo100

Podemos aplicar a comparação de versões a uma coluna:

$ sort -t';' -k2,2V pacotes.txt

Para mostrar as versões mais recentes primeiro:

$ sort -t';' -k2,2Vr pacotes.txt

9. Ordenando endereços IPv4

A ordenação textual de endereços IP costuma produzir resultados incorretos:

192.168.1.100
192.168.1.20
10.0.0.8
10.0.0.15

Uma solução é tratar cada octeto como uma chave numérica:

$ sort -t. \
    -k1,1n \
    -k2,2n \
    -k3,3n \
    -k4,4n \
    ips.txt

Resultado:

10.0.0.8
10.0.0.15
192.168.1.20
192.168.1.100

Se o endereço estiver na primeira coluna de um arquivo de log:

192.168.1.20 acesso-permitido
10.0.0.8 acesso-negado

a chave final precisa terminar antes do espaço. Uma solução robusta é transformar temporariamente os octetos em campos separados ou usar uma ferramenta como awk para gerar uma chave auxiliar.

Exemplo:

$ awk '
{
    split($1, ip, ".")
    printf "%03d.%03d.%03d.%03d\t%s\n",
           ip[1], ip[2], ip[3], ip[4], $0
}' acessos.txt |
    sort -k1,1 |
    cut -f2-

A chave formatada:

010.000.000.008 

é eliminada após a ordenação.

Esse padrão de gerar uma chave auxiliar é extremamente útil quando os dados não estão em um formato que o sort consiga interpretar diretamente.

10. Criando chaves auxiliares

Suponha que um arquivo contenha datas no formato brasileiro:

Ana;31/12/2025;1200
Carlos;05/01/2024;900
Beatriz;17/08/2025;1500

Podemos usar três chaves:

$ sort -t';' \
    -k2.7,2.10n \
    -k2.4,2.5n \
    -k2.1,2.2n \
    arquivo.txt

Porém, a expressão é difícil de manter. Outra abordagem é gerar uma chave ISO temporária:

$ awk -F';' '
{
    split($2, data, "/")
    printf "%04d-%02d-%02d\t%s\n",
           data[3], data[2], data[1], $0
}' arquivo.txt |
    sort -k1,1 |
    cut -f2-

A técnica segue três etapas:

  1. awk cria uma chave adequada;
  2. sort ordena por essa chave;
  3. cut remove a chave auxiliar.

Ela funciona para:

  • datas;
  • endereços IP;
  • números de telefone;
  • códigos compostos;
  • prioridades simbólicas;
  • campos extraídos de texto;
  • valores calculados.

11. Definindo uma ordem personalizada

Suponha que os níveis de prioridade sejam:

crítica
alta
média
baixa

Uma ordenação alfabética não produz essa sequência. Podemos criar uma chave numérica:

$ awk '
{
    prioridade = $1

    if (prioridade == "crítica")
        ordem = 1
    else if (prioridade == "alta")
        ordem = 2
    else if (prioridade == "média")
        ordem = 3
    else if (prioridade == "baixa")
        ordem = 4
    else
        ordem = 99

    printf "%02d\t%s\n", ordem, $0
}' tarefas.txt |
    sort -k1,1n |
    cut -f2-

Uma versão mais compacta:

awk '
BEGIN {
    ordem["crítica"] = 1
    ordem["alta"]    = 2
    ordem["média"]   = 3
    ordem["baixa"]   = 4
}
{
    valor = ($1 in ordem) ? ordem[$1] : 99
    printf "%02d\t%s\n", valor, $0
}' tarefas.txt |
    sort -k1,1n |
    cut -f2-

12. Usando "--debug" para entender as chaves

A opção --debug mostra quais partes das linhas são utilizadas na comparação:

$ sort --debug -k2,2 arquivo.txt

Para uma chave numérica:

$ sort --debug -k3,3n funcionarios.txt

O comando sublinha as regiões reconhecidas como chaves e pode emitir avisos, como:

sort: text ordering performed using ‘pt_BR.UTF-8’ sorting rules

ou:

sort: key 1 is numeric and spans multiple fields

Use --debug quando:

  • uma chave parece incluir campos adicionais;
  • os espaços estão interferindo;
  • os números não estão sendo reconhecidos;
  • a ordem varia conforme a localidade;
  • modificadores foram associados à posição errada.

O resultado contém marcações de diagnóstico. Portanto, não deve ser enviado diretamente para o arquivo final.

13. Como "-u" funciona com chaves

A opção -u mantém uma linha por grupo de chaves equivalentes.

Considere:

1001;Ana;ativo
1001;Ana;inativo
1002;Carlos;ativo
1002;Carlos;bloqueado

Execute:

$ sort -t';' -u -k1,1n registros.txt

O resultado terá apenas um registro para cada código:

1001;Ana;ativo
1002;Carlos;ativo

A eliminação não se baseou na igualdade das linhas completas. Ela se baseou apenas na chave:

-k1,1n 

Isso permite selecionar um registro por identificador, mas surge uma pergunta importante: qual deles será preservado?

A resposta depende dos critérios de ordenação e da estabilidade. Se queremos preservar a primeira ocorrência:

$ sort -s -t';' -u -k1,1n registros.txt

A combinação -s -u impede que a linha completa seja usada como desempate e conserva a primeira linha de cada grupo equivalente na entrada ordenada.

14. Selecionando o registro mais recente de cada grupo

Considere:

1001;2026-01-10;ativo
1002;2026-02-05;ativo
1001;2026-03-18;inativo
1002;2026-01-20;bloqueado

Queremos conservar o registro mais recente de cada código.

Primeiro, ordenamos pelo código e pela data decrescente:

$ sort -t';' \
    -k1,1n \
    -k2,2r \
    registros.txt

Depois, mantemos somente o primeiro registro de cada código:

$ sort -t';' \
    -k1,1n \
    -k2,2r \
    registros.txt |
    sort -s -t';' -u -k1,1n

Entretanto, a segunda execução reorganiza os dados por código. Para selecionar explicitamente a primeira linha de cada grupo, awk torna a intenção mais clara:

$ sort -t';' \
    -k1,1n \
    -k2,2r \
    registros.txt |
    awk -F';' '!visto[$1]++'

Resultado:

1001;2026-03-18;inativo
1002;2026-02-05;ativo

Esse padrão combina bem as responsabilidades:

  • sort organiza os registros;
  • awk seleciona o primeiro de cada grupo.

15. Encontrando os maiores valores por grupo

Considere:

web;servidor01;38
db;servidor02;92
web;servidor03;75
db;servidor04;64
cache;servidor05;48

Queremos o maior valor de cada categoria.

Ordene pela categoria e pelo valor decrescente:

$ sort \
    -t';' \
    -k1,1 \
    -k3,3nr \
    servidores.txt |
    awk -F';' '!visto[$1]++'

Resultado:

cache;servidor05;48
db;servidor02;92
web;servidor03;75

Para obter os três maiores valores de cada grupo:

$ sort \
    -t';' \
    -k1,1 \
    -k3,3nr \
    servidores.txt |
    awk -F';' 'contador[$1]++ < 3'

Essa combinação é útil na análise de:

  • logs;
  • vendas;
  • consumo de recursos;
  • notas;
  • acessos;
  • métricas por servidor;
  • resultados agrupados.

16. Mesclando grandes arquivos já ordenados

Quando vários arquivos já estão ordenados pelos mesmos critérios, não é necessário ordená-los novamente. Use -m:

$ sort -m arquivo1.txt arquivo2.txt arquivo3.txt

O comando intercala as linhas, mantendo a ordenação global.

Para eliminar chaves duplicadas:

$ sort -mu \
    arquivo1.txt \
    arquivo2.txt \
    arquivo3.txt

Com chaves específicas:

$ sort -m \
    -t';' \
    -k1,1n \
    parte1.csv \
    parte2.csv \
    parte3.csv

Antes, verifique cada entrada:

$ for arquivo in parte1.csv parte2.csv parte3.csv
do
    if ! sort -C -t';' -k1,1n "$arquivo"
    then
        printf 'Arquivo fora de ordem: %s\n' "$arquivo" >&2
        exit 1
    fi
done

Depois:

$ sort -m \
    -t';' \
    -k1,1n \
    parte1.csv \
    parte2.csv \
    parte3.csv \
    -o consolidado.csv

A mesclagem possui custo menor do que uma nova ordenação completa, especialmente em arquivos muito grandes.

17. Validando a ordenação em scripts

A opção -C verifica silenciosamente se a entrada está ordenada:

$ sort -C arquivo.txt

Seu valor está no código de saída:

$ if sort -C -t';' -k1,1n dados.csv
then
    echo "Arquivo corretamente ordenado."
else
    echo "Arquivo fora de ordem." >&2
    exit 1
fi

A opção -c também verifica, mas apresenta a primeira desordem encontrada:

$ sort -c -t';' -k1,1n dados.csv

Essa validação é útil antes de:

  • executar sort -m;
  • usar join;
  • aplicar algoritmos que pressupõem agrupamento;
  • comparar resultados;
  • alimentar programas que exigem chaves ordenadas.

18. Preparando arquivos para "join"

O comando join exige que os dois arquivos estejam ordenados pelos campos utilizados na junção.

Considere usuarios.txt:

1001 Ana
1002 Carlos
1003 Beatriz

E saldos.txt:

1003 950
1001 1200
1002 500

Prepare ambos com os mesmos critérios:

$ LC_ALL=C sort -k1,1 usuarios.txt > usuarios.ordenado
$ LC_ALL=C sort -k1,1 saldos.txt > saldos.ordenado

Faça a junção:

$ LC_ALL=C join \
    -1 1 \
    -2 1 \
    usuarios.ordenado \
    saldos.ordenado

Resultado:

1001 Ana 1200
1002 Carlos 500
1003 Beatriz 950

É fundamental usar a mesma localidade no sort e no join. Caso contrário, os programas podem discordar sobre a ordem das chaves.

19. Usando o caractere nulo como separador

Linhas são normalmente separadas por \n. Isso representa um problema ao processar nomes de arquivos, pois um nome válido pode conter uma quebra de linha.

Use uma cadeia terminada por caractere nulo:

$ find . -type f -print0 |
    sort -z

Para processar o resultado:

$ find . -type f -print0 |
    sort -z |
    while IFS= read -r -d '' arquivo
    do
        printf 'Arquivo: %q\n' "$arquivo"
    done

Ou:

$ find . -type f -print0 |
    sort -z |
    xargs -0 -r comando

Para ordenar por tamanho, gere registros também terminados por NUL:

$ find . -type f -printf '%s\t%p\0' |
    sort -z -t$'\t' -k1,1n

Para imprimir o resultado em linhas convencionais:

$ find . -type f -printf '%s\t%p\0' |
    sort -z -t$'\t' -k1,1n |
    tr '\0' '\n'

O último comando é apropriado para visualização. Se um nome contiver uma quebra de linha, sua representação visual ainda poderá ocupar mais de uma linha. Para continuar processando os dados com segurança, preserve o terminador NUL.

20. Listando os maiores arquivos com segurança

Um exemplo útil:

$ find /var/log \
    -type f \
    -printf '%s\t%p\0' 2>/dev/null |
    sort -z -t$'\t' -k1,1nr |
    head -z -n 20 |
    while IFS=$'\t' read -r -d '' tamanho arquivo
    do
        printf '%12d  %q\n' "$tamanho" "$arquivo"
    done

O fluxo funciona assim:

  1. find imprime tamanho e caminho, terminados por NUL;
  2. sort -z preserva os registros com segurança;
  3. head -z seleciona os vinte maiores;
  4. printf %q exibe caracteres especiais de forma inequívoca.

A opção head -z faz parte do GNU Coreutils. Em outros sistemas Unix, pode não estar disponível.

21. Ordenando logs por campos compostos

Considere um log:

2026-08-25 10:31:15 servidor02 WARN Disco quase cheio
2026-08-25 09:12:03 servidor01 INFO Serviço iniciado
2026-08-24 23:58:44 servidor03 ERROR Falha de conexão

Como a data e a hora estão no formato ISO, podemos usar:

$ sort -k1,1 -k2,2 log.txt

Para apresentar as mensagens mais recentes primeiro:

$ sort -k1,1r -k2,2r log.txt

Podemos definir uma única chave abrangendo os dois campos:

$ sort -k1,2r log.txt

Nesse caso, a comparação utiliza desde o início do primeiro campo até o fim do segundo.

Para ordenar pelo servidor e depois pela data:

$ sort \
    -k3,3 \
    -k1,1 \
    -k2,2 \
    log.txt

22. Ordenando a saída de "ps" com segurança

A saída tradicional de:

$ ps aux

possui campos separados por quantidades variáveis de espaços, e o último campo pode conter espaços. Ainda é possível processá-la, mas é melhor pedir ao ps uma saída controlada.

Processos por memória:

$ ps -eo pid=,user=,rss=,comm= |
    sort -k3,3nr |
    head -20

Processos por CPU:

$ ps -eo pid=,user=,pcpu=,comm= |
    LC_ALL=C sort -k3,3gr |
    head -20

Use -g para lidar melhor com números decimais no campo %CPU.

Para mostrar memória em MiB, gere uma chave auxiliar:

$ ps -eo pid=,user=,rss=,comm= |
    sort -k3,3nr |
    awk '
    BEGIN {
        printf "%-8s %-15s %12s %s\n",
               "PID", "USUÁRIO", "MEMÓRIA", "COMANDO"
    }
    {
        printf "%-8s %-15s %9.1f MiB %s\n",
               $1, $2, $3 / 1024, $4
    }' |
    head -21

A ordenação deve acontecer antes da conversão e da inclusão do cabeçalho.

23. Preservando cabeçalhos

Arquivos tabulares frequentemente possuem uma linha de cabeçalho.

Uma solução comum:

$ {
    IFS= read -r cabecalho
    printf '%s\n' "$cabecalho"
    LC_ALL=C sort -t';' -k3,3nr
} < dados.csv > dados-ordenados.csv

Esse formato evita executar head e tail separadamente sobre o mesmo arquivo.

O bloco:

$ {
    ...
} < dados.csv

compartilha a entrada entre read e sort. A primeira operação consome o cabeçalho e sort recebe as linhas restantes.

Para substituir o arquivo com segurança:

$ temporario=$(mktemp)

{
    IFS= read -r cabecalho
    printf '%s\n' "$cabecalho"
    LC_ALL=C sort -t';' -k3,3nr
} < dados.csv > "$temporario" &&
mv -- "$temporario" dados.csv

Em um script completo, também é recomendável remover o arquivo temporário em caso de interrupção:

$ temporario=$(mktemp) || exit 1

trap 'rm -f -- "$temporario"' EXIT HUP INT TERM

{
    IFS= read -r cabecalho
    printf '%s\n' "$cabecalho"
    LC_ALL=C sort -t';' -k3,3nr
} < dados.csv > "$temporario" || exit 1

mv -- "$temporario" dados.csv
trap - EXIT

24. Processamento de arquivos maiores que a memória

O sort divide entradas extensas em blocos, grava resultados intermediários e depois os mescla.

As opções mais relevantes são:

-S
-T
--parallel
--compress-program

Controlando o buffer

$ sort -S 2G arquivo-grande.txt

Ou como percentual da memória:

$ sort -S 40% arquivo-grande.txt

Reservar mais memória pode reduzir operações temporárias em disco, mas um valor exagerado pode pressionar o sistema e provocar uso de swap.

Escolhendo o diretório temporário

$ sort \
    -T /dados/tmp \
    arquivo-grande.txt

Podemos informar -T mais de uma vez:

$ sort \
    -T /disco1/tmp \
    -T /disco2/tmp \
    arquivo-grande.txt

Isso pode distribuir arquivos temporários por sistemas de arquivos diferentes.

Definindo paralelismo

$ sort \
    --parallel=8 \
    arquivo-grande.txt

Mais processos não garantem desempenho proporcional. O gargalo pode estar:

  • no disco;
  • na memória;
  • na descompressão;
  • na localidade;
  • na quantidade de arquivos temporários.

Comprimindo arquivos temporários

$ sort \
    --compress-program=gzip \
    arquivo-grande.txt

A compressão troca uso de armazenamento e entrada/saída por consumo de CPU. Em discos lentos e dados altamente compactáveis, ela pode trazer ganhos. Em SSDs rápidos ou processadores ocupados, pode piorar o desempenho.

25. Uma configuração para grandes volumes

Um exemplo:

$ LC_ALL=C sort \
    --parallel=8 \
    -S 40% \
    -T /dados/sort-tmp \
    -t$'\t' \
    -k1,1 \
    -k2,2n \
    entrada.tsv \
    -o saida.tsv

Antes de executar, verifique espaço:

$ df -h /dados/sort-tmp

Confira os inodes:

$ df -i /dados/sort-tmp

Em uma ordenação externa, o espaço temporário necessário pode ser significativo. Não use um diretório localizado em uma partição quase cheia.

26. Comparando desempenho

Podemos medir diferentes configurações:

$ time LC_ALL=pt_BR.UTF-8 \
    sort arquivo-grande.txt \
    -o /dev/null

Compare com:

$ time LC_ALL=C \
    sort arquivo-grande.txt \
    -o /dev/null

Depois:

$ time LC_ALL=C \
    sort \
        --parallel=4 \
        -S 1G \
        arquivo-grande.txt \
        -o /dev/null

Para evitar conclusões precipitadas:

  • execute os testes várias vezes;
  • considere o efeito do cache do sistema;
  • monitore memória e swap;
  • use o mesmo arquivo;
  • mantenha o computador com carga semelhante;
  • verifique se os resultados são equivalentes.

Uma configuração mais rápida que produz uma ordem linguística diferente não é necessariamente uma substituição válida.

27. Evitando uma opção global aplicada por engano

Considere:

$ sort -n -k2,2 -k3,3 arquivo.txt

A opção global -n pode afetar todas as chaves que não possuam um modificador específico.

Se o segundo campo for textual e o terceiro numérico, prefira:

$ sort \
    -k2,2 \
    -k3,3n \
    arquivo.txt

Associar o modificador à chave reduz ambiguidades e torna a intenção evidente para quem mantiver o script.

O mesmo vale para:

b d f g h M n R r V 

Exemplo:

$ sort \
    -k1,1f \
    -k2,2Vr \
    arquivo.txt

A primeira chave ignora diferenças entre maiúsculas e minúsculas. A segunda interpreta versões em ordem inversa.

28. Chaves, delimitadores e campos vazios

Considere:

1001;Ana;5200
1002;;4800
1003;Carlos;
1004;Beatriz;6100

O delimitador é:

$ -t';'

Campos vazios continuam existindo. Para ordenar pelo segundo campo:

$ sort -t';' -k2,2 arquivo.txt

O registro com campo vazio normalmente aparecerá antes dos nomes.

Para colocar campos vazios ao final, podemos gerar uma chave auxiliar:

$ awk -F';' '
{
    vazio = ($2 == "") ? 1 : 0
    printf "%d\t%s\t%s\n", vazio, $2, $0
}' arquivo.txt |
    sort -t$'\t' -k1,1n -k2,2 |
    cut -f3-

A primeira chave separa registros preenchidos dos vazios. A segunda organiza os valores existentes.

29. Cuidados com CSV

O uso de:

$ sort -t, -k3,3 arquivo.csv

só é confiável em arquivos simples, nos quais vírgulas não aparecem dentro dos campos.

Este registro possui uma vírgula interna:

1001,"Almeida, Ana",5200 

Para o sort, ele possui quatro campos:

1001
"Almeida
 Ana"
5200

Isso não corresponde à estrutura lógica do CSV.

Para CSVs reais, use ferramentas que compreendam:

  • campos entre aspas;
  • delimitadores internos;
  • aspas escapadas;
  • linhas embutidas;
  • codificação.

Uma solução consiste em converter o CSV para um formato intermediário seguro, ordenar e depois reconstruí-lo com uma biblioteca apropriada.

30. Um script reutilizável

O script abaixo preserva o cabeçalho, ordena um arquivo separado por ponto e vírgula e grava o resultado com segurança:

$ #!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail

if (( $# != 2 ))
then
    printf 'Uso: %s ARQUIVO_ENTRADA ARQUIVO_SAÍDA\n' \
        "${0##*/}" >&2
    exit 2
fi

entrada=$1
saida=$2

if [[ ! -r $entrada ]]
then
    printf 'Não foi possível ler: %s\n' "$entrada" >&2
    exit 1
fi

diretorio_saida=${saida%/*}

if [[ $diretorio_saida == "$saida" ]]
then
    diretorio_saida=.
fi

temporario=$(mktemp \
    "$diretorio_saida/.sort.XXXXXX")

trap 'rm -f -- "$temporario"' EXIT HUP INT TERM

{
    IFS= read -r cabecalho
    printf '%s\n' "$cabecalho"

    LC_ALL=C sort \
        -s \
        -t';' \
        -k2,2f \
        -k3,3nr
} < "$entrada" > "$temporario"

mv -- "$temporario" "$saida"

trap - EXIT

O script:

  • valida os argumentos;
  • verifica a leitura da entrada;
  • cria o arquivo temporário no diretório de destino;
  • preserva o cabeçalho;
  • ordena pelo segundo campo sem distinguir maiúsculas;
  • usa o terceiro campo como desempate numérico decrescente;
  • substitui o destino somente depois do sucesso;
  • remove o arquivo temporário se houver interrupção.

31. Padrões avançados úteis

Primeira ocorrência de cada chave

$ sort -s -k1,1 arquivo |
    awk '!visto[$1]++'

Maior valor de cada grupo

$ sort \
    -k1,1 \
    -k2,2nr \
    arquivo |
    awk '!visto[$1]++'

Dez valores mais frequentes

$ LC_ALL=C sort arquivo |
    uniq -c |
    sort -k1,1nr |
    head -10

Versão mais recente de cada pacote

Supondo pacote versão:

$ sort \
    -k1,1 \
    -k2,2Vr \
    pacotes.txt |
    awk '!visto[$1]++'

Consolidação de arquivos ordenados

$ LC_ALL=C sort \
    -m \
    -u \
    arquivo1 \
    arquivo2 \
    arquivo3

Validação silenciosa

$ if ! LC_ALL=C sort -C -k1,1 arquivo
then
    echo "Entrada fora de ordem." >&2
    exit 1
fi

### Nomes de arquivos em ordem segura

$ find . -type f -print0 |
    LC_ALL=C sort -z |
    xargs -0 -r comando

32. Resumo das opções mais importantes no uso avançado

Opção Aplicação avançada
-k Define chaves por campo e posição
-t Define um delimitador de um caractere
-s Impede desempate pela linha completa
-u Mantém uma linha por chave equivalente
-m Mescla entradas previamente ordenadas
-C Valida silenciosamente a ordenação
-c Informa o primeiro ponto fora de ordem
-z Usa NUL como terminador de registro
-V Compara números de versões
-h Compara tamanhos com sufixos
-g Reconhece representações numéricas gerais
-S Controla aproximadamente o buffer
-T Escolhe diretórios temporários
--parallel Controla o paralelismo
--compress-program Comprime arquivos temporários
--debug Mostra a interpretação das chaves
-o Grava diretamente no arquivo indicado

Conclusão

O uso avançado de sort depende menos da memorização de opções e mais da compreensão de quatro conceitos: chaves, localidade, estabilidade e formato dos registros.

Uma chave bem delimitada:

$ sort -k2,2

é diferente de:

$ sort -k2

A ordenação estável:

$ sort -s

preserva a sequência de registros com chaves equivalentes. A opção -u elimina registros segundo as chaves de comparação, e não necessariamente segundo a linha completa. A definição:

$ LC_ALL=C

torna resultados técnicos mais previsíveis, enquanto -z permite processar nomes de arquivos sem depender da quebra de linha como separador.

Quando o formato original não oferece uma chave apropriada, a solução mais flexível é criar uma chave auxiliar:

$ gerar_chave |
    sort |
    remover_chave

Esse padrão permite ordenar datas, endereços IP, prioridades personalizadas e praticamente qualquer estrutura que possa ser transformada em uma representação comparável. É nesse ponto que sort deixa de ser apenas um organizador de linhas e passa a funcionar como uma peça central no processamento de dados com shell.



Veja a relação completa dos artigos de Rubens Queiroz de Almeida