Tengo unos 15,000 archivos con nombres file_1.pdb
, file_2.pdb
etc. Puedo ordenar unos pocos miles de estos en orden haciendo:
cat file_{1..2000}.pdb >> file_all.pdb
Sin embargo, si hago esto para 15,000 archivos, me sale el error
-bash: /bin/cat: Argument list too long
He visto resolver este problema haciendo find . -name xx -exec xx
esto, pero esto no preservaría el orden con el que se unen los archivos. ¿Cómo puedo conseguir esto?
files
find
cat
brace-expansion
nitrato de sodio
fuente
fuente
cat file_{1..15000}.pdb
construcción funciona bien para mí.getconf ARG_MAX
debería decir.Respuestas:
Utilizando
find
,sort
yxargs
:El
find
comando encuentra todos los archivos relevantes, luego imprime sus nombres de ruta parasort
hacer un "ordenamiento de versiones" para obtenerlos en el orden correcto (si los números en los nombres de archivo hubieran sido rellenados con ceros a un ancho fijo, no hubiéramos necesitado-V
).xargs
toma esta lista de nombres de ruta ordenados y los ejecutacat
en lotes tan grandes como sea posible.Esto debería funcionar incluso si los nombres de archivo contienen caracteres extraños como líneas nuevas y espacios. Usamos
-print0
withfind
para darsort
nombres terminados en nulos para ordenar, y lossort
maneja usando-z
.xargs
lee también nombres terminados en nulo con su-0
bandera.Tenga en cuenta que estoy escribiendo el resultado en un archivo cuyo nombre no coincide con el patrón
file_*.pdb
.La solución anterior utiliza algunos indicadores no estándar para algunas utilidades. Estos son compatibles con la implementación de GNU de estas utilidades y al menos con la implementación de OpenBSD y macOS.
Los indicadores no estándar utilizados son
-maxdepth 1
, para hacer quefind
solo ingrese el directorio superior pero no subdirectorios. POSIXY, usefind . ! -name . -prune ...
-print0
, parafind
generar nombres de ruta terminados en nulo (esto fue considerado por POSIX pero rechazado). Se podría usar-exec printf '%s\0' {} +
en lugar.-z
, para hacersort
tomar registros terminados en nulos. No hay equivalencia POSIX.-V
, parasort
ordenar, por ejemplo,200
después3
. No hay equivalencia POSIX, pero podría reemplazarse por una clasificación numérica en partes específicas del nombre de archivo si los nombres de archivo tienen un prefijo fijo.-0
, para hacerxargs
registros leídos con terminación nula. No hay equivalencia POSIX. POSIXY, uno necesitaría citar los nombres de archivo en un formato reconocido porxargs
.Si los nombres de ruta se comportan bien, y si la estructura del directorio es plana (sin subdirectorios), uno podría prescindir de estos indicadores, excepto
-V
consort
.fuente
printf ‘file_%d.pdb\0’ {1..15000} | xargs -0 cat
, o incluso con el punto de Kevin,echo file_{1..15000}.pdb | xargs cat
. Lafind
solución tiene una sobrecarga considerablemente mayor ya que tiene que buscar esos archivos en el sistema de archivos, pero es más útil cuando algunos de los archivos pueden no existir.xargs
más quecat
eso se redirige (cadacat
invocación utilizaráxargs
la salida estándar). Si hubiéramos dicho,xargs -0 sh -c 'cat >all.pdb'
entonces tendría sentido usarlo en>>
lugar de>
, si eso es lo que estás insinuando.sort -n -k1.6
que funcionaría (para el original, losfile_nnn
nombres de archivo osort -n -k1.5
para los que no tienen el guión bajo).Con
zsh
(de dónde{1..15000}
proviene ese operador):O para todos los
file_<digits>.pdb
archivos en orden numérico:(donde
<x-y>
es un operador global que coincide con los números decimales xay. Sinx
niy
, es cualquier número decimal. Equivalente aextendedglob
's[0-9]##
okshglob
' s+([0-9])
(uno o más dígitos)).Con
ksh93
, usando sucat
comando incorporado (por lo que no se ve afectado por ese límite de laexecve()
llamada del sistema ya que no hay ejecución ):Con
bash
/zsh
/ksh93
(que ayudazsh
's{x..y}
y tienenprintf
incorporado):En un sistema GNU o compatible, también puede usar
seq
:Para las
xargs
soluciones basadas, se debe tener especial cuidado con los nombres de archivos que contienen espacios en blanco, comillas simples o dobles o barras invertidas.Me gusta para
-It's a trickier filename - 12.pdb
, use:fuente
seq -f | xarg cat >
es la solución más elegante y eficaz. (EN MI HUMILDE OPINIÓN).'"./-It'\''s a trickier filename - %.17g.pdb"'
¿ tal vez ?Un bucle for es posible, y muy simple.
La desventaja es que invocas
cat
muchas veces. Pero si no puede recordar exactamente cómo hacer las cosasfind
y la sobrecarga de la invocación no es tan mala en su situación, entonces vale la pena tenerlo en cuenta.fuente
echo $i;
en el cuerpo del bucle como un "indicador de progreso"fuente
seq -f file_%.10g.pdb 15000
. Tenga en cuenta queseq
no es un comando estándar.seq -f
es una excelente forma de hacerlo. Lo recordaré.Premisa
No debe incurrir en ese error solo para 15k archivos con ese formato de nombre específico [ 1 , 2 ] .
Si está ejecutando esa expansión desde otro directorio y tiene que agregar la ruta a cada archivo, el tamaño de su comando será mayor y, por supuesto, puede ocurrir.
La solución ejecuta el comando desde ese directorio.
La mejor solución Si en cambio adiviné mal y lo ejecutas desde el directorio en el que están los archivos ... En
mi humilde opinión, la mejor solución es la de Stéphane Chazelas :
con printf o seq; probado en archivos de 15k con solo su número dentro de la memoria caché previa, es incluso el más rápido (en la actualidad, excepto el OP del mismo directorio en el que se encuentran los archivos).
Algunas palabras mas
Debería poder pasar a sus líneas de comando de shell más tiempo.
Su línea de comando tiene 213914 caracteres y contiene 15003 palabras.
cat file_{1..15000}.pdb " > file_all.pdb" | wc
... incluso agregar 8 bytes para cada palabra es 333 938 bytes (0.3M) muy por debajo del 2097142 (2.1M) reportado
ARG_MAX
en un kernel 3.13.0 o el 2088232 ligeramente más pequeño reportado como "Longitud máxima de comando que podríamos realmente utilizar " porxargs --show-limits
Eche un vistazo en su sistema a la salida de
Solución guiada de la pereza
En casos como este, prefiero trabajar con bloques, incluso porque generalmente sale una solución eficiente en el tiempo.
La lógica (si la hay) es que soy demasiado vago para escribir 1 ... 1000 1001..2000, etc., etc.
Así que le pido a un script que lo haga por mí.
Solo después de comprobar que la salida es correcta, la redirijo a un script.
... pero la pereza es un estado mental .
Como soy alérgico a
xargs
(realmente debería haberlo usadoxargs
aquí) y no quiero comprobar cómo usarlo, termino puntualmente para reinventar la rueda como en los ejemplos a continuación (tl; dr).Tenga en cuenta que, dado que los nombres de los archivos están controlados (sin espacios, líneas nuevas ...), puede seguir fácilmente algo como el script a continuación.
tl; dr
Versión 1: pase como parámetro opcional el primer número de archivo, el último, el tamaño del bloque, el archivo de salida
Versión 2
Llamando a bash para la expansión (un poco más lento en mis pruebas ~ 20%).
Por supuesto, puede avanzar y deshacerse por completo de
seq
[ 3 ] (de coreutils) y trabajar directamente con las variables en bash, o usar python, o compilar un programa de CA para hacerlo [ 4 ] ...fuente
%g
es la abreviatura de%.6g
. Representaría 1,000,000 como 1e + 06 por ejemplo.xargs
, zsh'szargs
oksh93
'scommand -x
.seq
no es un bash incorporado, es un comando de GNU coreutils.seq -f %g 1000000 1000000
salidas 1e + 06 incluso en la última versión de coreutils.xarg
... pero entiendo que es personal y tal vez solo está relacionado conmigo.Otra forma de hacerlo podría ser
fuente