Estoy intentando un ingenuo:
$ cat * | sort -u > /tmp/bla.txt
que falla con:
-bash: /bin/cat: Argument list too long
Entonces, para evitar una solución tonta como (crea un enorme archivo temporal):
$ find . -type f -exec cat {} >> /tmp/unsorted.txt \;
$ cat /tmp/unsorted.txt | sort -u > /tmp/bla.txt
Pensé que podía procesar archivos uno por uno usando (esto debería reducir el consumo de memoria y estar más cerca de un mecanismo de transmisión):
$ cat proc.sh
#!/bin/sh
old=/tmp/old.txt
tmp=/tmp/tmp.txt
cat $old "$1" | sort -u > $tmp
mv $tmp $old
Seguido entonces por:
$ touch /tmp/old.txt
$ find . -type f -exec /tmp/proc.sh {} \;
¿Existe un reemplazo más simple de estilo unix para: cat * | sort -ucuando alcanza el número de archivos MAX_ARG? Se siente incómodo escribir un pequeño script de shell para una tarea tan común.

sortlo hace automáticamente para la entrada de múltiples archivos ... pero luegosort -u *también fallaríaArgument list too long, supongoRespuestas:
Con GNU
sort, y un shell dondeprintfestá incorporado (todos los POSIX como hoy en día excepto algunas variantes depdksh):Ahora, un problema con eso es que debido a que los dos componentes de esa tubería se ejecutan de manera simultánea e independiente, cuando el izquierdo expande el
*globo, el derecho ya puede haber creado eloutputarchivo, lo que podría causar un problema (tal vez no-uaquí) comooutputsería un archivo de entrada y de salida, por lo que es posible que desee que la salida vaya a otro directorio (> ../outputpor ejemplo), o asegúrese de que el glob no coincida con el archivo de salida.Otra forma de abordarlo en este caso es escribirlo:
De esa manera, se
sortabreoutputpara escribir y (en mis pruebas), no lo hará antes de que haya recibido la lista completa de archivos (tanto tiempo después de que el globo se haya expandido). También evitará el clobberingoutputsi ninguno de los archivos de entrada es legible.Otra forma de escribirlo con
zshobashEso está usando la sustitución del proceso (donde
<(...)se reemplaza por una ruta de archivo que se refiere al final de la lectura en la queprintfse escribe la tubería ). Esa característica provieneksh, perokshinsiste en hacer la expansión de<(...)un argumento separado para el comando para que no pueda usarlo con la--option=<(...)sintaxis. Sin embargo, funcionaría con esta sintaxis:Tenga en cuenta que verá una diferencia con respecto a los enfoques que alimentan la salida de
catlos archivos en los casos en que hay archivos que no terminan en un carácter de nueva línea:También tenga en cuenta que
sortordena usando el algoritmo de intercalación en el locale (strcollate()), esort -uinforma uno de cada conjunto de líneas que se clasifican de la misma manera por ese algoritmo, no líneas únicas a nivel de byte. Si solo le importa que las líneas sean únicas a nivel de byte y no le importe tanto el orden en que están ordenadas, es posible que desee fijar la configuración regional en C donde la ordenación se basa en valores de bytes (memcmp(); eso probablemente aceleraría cosas significativamente):fuente
sortoptimizar su consumo de memoria. Sin embargo, todavía me pareceprintf '%s\0' *un poco complejo escribir.find . -type f -maxdepth 1 -print0lugar deprintf '%s\0' *, pero no puedo afirmar que sea más fácil de escribir. ¡Y esto último es más fácil de definir como un alias, por supuesto!echotiene un-n, hubiera preferido algo comoprintf -0 %sesto parece un nivel un poco menos bajo que'%s\0'-maxdepthy-print0son extensiones GNU (aunque ampliamente compatibles en estos días). Con otrosfinds (aunque si tiene GNU sort, es probable que GNU encuentre también), puede hacerloLC_ALL=C find . ! -name . -prune -type f ! -name '.*' -exec printf '%s\0' {} +(LC_ALL=Cpara excluir archivos ocultos que contienen caracteres no válidos, incluso con GNUfind), pero eso es un poco excesivo cuando generalmente tenerprintfincorporado.print0función comoprint0() { [ "$#" -eq 0 ] || printf '%s\0' "$@";}y luegoprint0 * | sort...Una solución simple, funciona al menos en Bash, ya que
printfestá integrada, y los límites del argumento de la línea de comando no se aplican a ella:(
echo * | xargstambién funcionaría, excepto para el manejo de nombres de archivos con espacios en blanco, etc.)fuente
catproceso separado para cada archivo.find -exec {} +varios archivos por una ejecución. Confind -exec \;él sería un gato por archivo.Esto concatenará todos los archivos regulares no ocultos en el directorio actual y ordenará sus contenidos combinados (mientras elimina las líneas duplicadas) en el archivo
/path/to/sorted.txt.fuente
|que encadenará correctamente las operaciones para limitar el uso de memoria?sorthará una ordenación fuera de núcleo si los requisitos de memoria lo requieren. El lado izquierdo de la tubería consumirá muy poca memoria en comparación.La eficiencia es un término relativo, por lo que realmente debe especificar qué factor desea minimizar; CPU, memoria, disco, tiempo, etc. En aras de la discusión, voy a suponer que desea minimizar el uso de memoria y está dispuesto a gastar más ciclos de CPU para lograrlo. Soluciones como la que da Stéphane Chazelas funcionan bien
pero suponen que los archivos de texto individuales tienen un alto grado de unicidad para comenzar. Si no lo hacen, es decir, si después
sample.srt es más de un 10% más pequeño que sample.txt, entonces ahorrará una cantidad considerable de memoria al eliminar los duplicados dentro de los archivos antes de fusionarlos. También ahorrará aún más memoria al no encadenar los comandos, lo que significa que los resultados de diferentes procesos no necesitan estar en la memoria al mismo tiempo.
fuente
sortya quesortrecurre al uso de archivos temporales cuando el uso de la memoria va más allá de un umbral (generalmente relativamente pequeño).base64 /dev/urandom | sort -ullenará su disco pero no usará mucha memoria.sortimplementaciones, incluida la original en Unix v3 en 1972, pero aparentemente no debusybox sort. Presumiblemente porque está destinado a ejecutarse en sistemas pequeños que no tienen almacenamiento permanente.yes | sort -u(todos los datos duplicados) no tiene que usar más de unos pocos bytes de memoria y mucho menos el disco. Pero con GNU y Solarissortal menos, vemos que escribe una gran cantidad de archivos grandes de 2 bytes/tmp(y\npor cada pocos megabytes de entrada) para que finalmente termine llenando el disco.Como @ilkkachu, pero el gato (1) es innecesario:
Además, si los datos son tan largos, quizás desee utilizar la opción sort (1) --parallel = N
Cuando N es el número de CPU que tiene tu computadora
fuente