Estou tentando um ingênuo:
$ cat * | sort -u > /tmp/bla.txt
que falha com:
-bash: /bin/cat: Argument list too long
Portanto, para evitar uma solução boba como (cria um enorme arquivo temporário):
$ find . -type f -exec cat {} >> /tmp/unsorted.txt \;
$ cat /tmp/unsorted.txt | sort -u > /tmp/bla.txt
Embora eu pudesse processar arquivos um por um usando (isso deve reduzir o consumo de memória e estar mais próximo de um mecanismo de streaming):
$ cat proc.sh
#!/bin/sh
old=/tmp/old.txt
tmp=/tmp/tmp.txt
cat $old "$1" | sort -u > $tmp
mv $tmp $old
Seguido então por:
$ touch /tmp/old.txt
$ find . -type f -exec /tmp/proc.sh {} \;
Existe uma substituição mais simples no estilo unix para: cat * | sort -u
quando o número de arquivos atingir MAX_ARG
? Parece estranho escrever um pequeno script de shell para uma tarefa tão comum.
sort
ele automaticamente para a entrada de vários arquivos .. mas, em seguida,sort -u *
iria falhar comArgument list too long
bem suponhoRespostas:
Com o GNU
sort
, e um shell em queprintf
está embutido (todos os similares ao POSIX atualmente, exceto algumas variantes depdksh
):Agora, um problema com isso é que, como os dois componentes desse pipeline são executados simultaneamente e de forma independente, no momento em que o esquerdo expande o
*
globo, o direito pode já ter criado ooutput
arquivo, o que poderia causar problemas (talvez não-u
aqui) comooutput
seria um arquivo de entrada e saída, você pode querer que a saída vá para outro diretório (> ../output
por exemplo) ou verifique se o glob não corresponde ao arquivo de saída.Outra maneira de resolvê-lo neste caso é escrevê-lo:
Dessa forma, está
sort
abrindooutput
para gravação e (nos meus testes), não o fará antes de receber a lista completa de arquivos (muito tempo depois que a glob foi expandida). Também evitará estroboscópiosoutput
se nenhum dos arquivos de entrada for legível.Outra maneira de escrevê-lo com
zsh
oubash
Isso está usando substituição de processo (onde
<(...)
é substituído por um caminho de arquivo que se refere ao final de leitura do pipe no qualprintf
está sendo gravado). Esse recurso vemksh
, masksh
insiste em fazer a expansão de<(...)
um argumento separado para o comando, para que você não possa usá-lo com a--option=<(...)
sintaxe. Porém, ele funcionaria com esta sintaxe:Observe que você verá uma diferença das abordagens que alimentam a saída dos
cat
arquivos nos casos em que existem arquivos que não terminam com um caractere de nova linha:Observe também que
sort
classifica usando o algoritmo de intercalação no locale (strcollate()
) esort -u
relata um de cada conjunto de linhas que são iguais por esse algoritmo, não linhas únicas no nível de bytes. Se você se preocupa apenas com as linhas serem únicas no nível de bytes e não se importa muito com a ordem em que elas são classificadas, convém fixar o código do idioma em C, onde a classificação se baseia nos valores de bytes (memcmp()
; isso provavelmente aceleraria coisas significativamente):fonte
sort
otimizar seu consumo de memória. Ainda achoprintf '%s\0' *
um pouco complexo para digitar.find . -type f -maxdepth 1 -print0
vez deprintf '%s\0' *
, mas não posso afirmar que é mais fácil digitar. E o último é mais fácil de definir como um apelido, é claro!echo
tem um-n
, eu teria algo preferido comoprintf -0 %s
este parece um pouco menos do que o baixo nível'%s\0'
-maxdepth
e-print0
são extensões GNU (embora amplamente suportadas atualmente). Com outrosfind
s (embora se você tiver a classificação GNU, é provável que o GNU encontre também), é possívelLC_ALL=C find . ! -name . -prune -type f ! -name '.*' -exec printf '%s\0' {} +
(LC_ALL=C
ainda excluir arquivos ocultos que contêm caracteres inválidos, mesmo com o GNUfind
), mas isso é um pouco exagerado quando você geralmente temprintf
embutido.print0
função comoprint0() { [ "$#" -eq 0 ] || printf '%s\0' "$@";}
e depoisprint0 * | sort...
Uma correção simples funciona pelo menos no Bash, pois
printf
está embutida e os limites do argumento da linha de comando não se aplicam a ela:(
echo * | xargs
também funcionaria, exceto no tratamento de nomes de arquivos com espaço em branco etc.)fonte
cat
processo separado para cada arquivo.find -exec {} +
vários arquivos por uma execução. Comfind -exec \;
isso haveria um gato por arquivo.Isso concatenará todos os arquivos regulares não ocultos no diretório atual e classificará o conteúdo combinado (ao remover linhas duplicadas) no arquivo
/path/to/sorted.txt
.fonte
|
que as operações serão encadeadas corretamente para limitar o uso de memória?sort
fará uma classificação fora do núcleo se os requisitos de memória exigirem. O lado esquerdo do pipeline consumirá muito pouca memória em comparação.Eficiência é um termo relativo, então você realmente precisa especificar qual fator deseja minimizar; CPU, memória, disco, tempo etc. Por uma questão de argumento, presumirei que você desejasse minimizar o uso de memória e estivesse disposto a gastar mais ciclos de CPU para conseguir isso. Soluções como a fornecida por Stéphane Chazelas funcionam bem
mas eles assumem que os arquivos de texto individuais têm um alto grau de exclusividade para começar. Se não o fizerem, ou seja, se depois
sample.srt é 10% menor que o sample.txt, e você economiza memória significativa removendo as duplicatas dos arquivos antes de mesclar. Você também economizará ainda mais memória ao não encadear os comandos, o que significa que os resultados de diferentes processos não precisam estar na memória ao mesmo tempo.
fonte
sort
poissort
recorre ao uso de arquivos temporários quando o uso de memória ultrapassa um limite (geralmente relativamente pequeno).base64 /dev/urandom | sort -u
encherá seu disco, mas não consumirá muita memória.sort
implementações, incluindo a original no Unix v3 em 1972, mas aparentemente nãobusybox sort
. Presumivelmente, porque esse é destinado a rodar em pequenos sistemas que não possuem armazenamento permanente.yes | sort -u
(todos os dados duplicados) não precisam usar mais do que alguns bytes de memória e muito menos disco. Mas com o GNU e o Solaris,sort
pelo menos, vemos gravando muitos arquivos grandes de 2 bytes/tmp
(y\n
para cada poucos megabytes de entrada), para que acabe preenchendo o disco eventualmente.Como @ilkkachu, mas o gato (1) é desnecessário:
Além disso, se os dados forem tão longos, talvez você queira usar a opção sort (1) --parallel = N
Quando N é o número de CPUs que o seu computador possui
fonte