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 -u
cuando 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.
sort
lo 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 dondeprintf
está 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 eloutput
archivo, lo que podría causar un problema (tal vez no-u
aquí) comooutput
sería un archivo de entrada y de salida, por lo que es posible que desee que la salida vaya a otro directorio (> ../output
por 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
sort
abreoutput
para 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 clobberingoutput
si ninguno de los archivos de entrada es legible.Otra forma de escribirlo con
zsh
obash
Eso 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 queprintf
se escribe la tubería ). Esa característica provieneksh
, peroksh
insiste 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
cat
los 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
sort
ordena usando el algoritmo de intercalación en el locale (strcollate()
), esort -u
informa 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
sort
optimizar su consumo de memoria. Sin embargo, todavía me pareceprintf '%s\0' *
un poco complejo escribir.find . -type f -maxdepth 1 -print0
lugar 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!echo
tiene un-n
, hubiera preferido algo comoprintf -0 %s
esto parece un nivel un poco menos bajo que'%s\0'
-maxdepth
y-print0
son extensiones GNU (aunque ampliamente compatibles en estos días). Con otrosfind
s (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=C
para excluir archivos ocultos que contienen caracteres no válidos, incluso con GNUfind
), pero eso es un poco excesivo cuando generalmente tenerprintf
incorporado.print0
función comoprint0() { [ "$#" -eq 0 ] || printf '%s\0' "$@";}
y luegoprint0 * | sort...
Una solución simple, funciona al menos en Bash, ya que
printf
está integrada, y los límites del argumento de la línea de comando no se aplican a ella:(
echo * | xargs
también funcionaría, excepto para el manejo de nombres de archivos con espacios en blanco, etc.)fuente
cat
proceso 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?sort
hará 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
sort
ya quesort
recurre 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 -u
llenará su disco pero no usará mucha memoria.sort
implementaciones, 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 Solarissort
al menos, vemos que escribe una gran cantidad de archivos grandes de 2 bytes/tmp
(y\n
por 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