J'ai environ 15 000 fichiers nommés file_1.pdb
, file_2.pdb
etc. Je peux en répertorier quelques milliers dans l'ordre en faisant:
cat file_{1..2000}.pdb >> file_all.pdb
Cependant, si je fais cela pour 15 000 fichiers, j'obtiens l'erreur
-bash: /bin/cat: Argument list too long
J'ai vu ce problème être résolu en faisant, find . -name xx -exec xx
mais cela ne préserverait pas l'ordre avec lequel les fichiers sont joints. Comment puis-je atteindre cet objectif?
files
find
cat
brace-expansion
nitrate de sodium
la source
la source
cat file_{1..15000}.pdb
construction me convient parfaitement.getconf ARG_MAX
devrait dire.Réponses:
L' utilisation
find
,sort
etxargs
:La
find
commande trouve tous les fichiers pertinents, puis imprime leurs noms de chemin pour fairesort
un "tri de version" pour les obtenir dans le bon ordre (si les nombres dans les noms de fichiers avaient été remplis de zéro à une largeur fixe, nous n'aurions pas eu besoin-V
).xargs
prend cette liste de chemins triés et les exécutecat
en lots aussi importants que possible.Cela devrait fonctionner même si les noms de fichiers contiennent des caractères étranges tels que des sauts de ligne et des espaces. Nous utilisons
-print0
withfind
pour donnersort
des noms terminés par nul à trier, et lessort
traitons en utilisant-z
.xargs
lit également les noms sans terminaison avec son-0
drapeau.Notez que j'écris le résultat dans un fichier dont le nom ne correspond pas au modèle
file_*.pdb
.La solution ci-dessus utilise des indicateurs non standard pour certains utilitaires. Ceux-ci sont pris en charge par la mise en œuvre GNU de ces utilitaires et au moins par la mise en œuvre d'OpenBSD et de macOS.
Les drapeaux non standard utilisés sont
-maxdepth 1
, pourfind
ne faire entrer que le répertoire le plus haut mais pas de sous-répertoires. POSIX, utilisezfind . ! -name . -prune ...
-print0
, pour créerfind
des noms de chemin de terminaison nulles (cela a été considéré par POSIX mais rejeté). On pourrait utiliser à la-exec printf '%s\0' {} +
place.-z
, pour fairesort
prendre des enregistrements terminés par nul. Il n'y a pas d'équivalence POSIX.-V
, poursort
trier par exemple200
après3
. Il n'y a pas d'équivalence POSIX, mais pourrait être remplacé par un tri numérique sur des parties spécifiques du nom de fichier si les noms de fichiers ont un préfixe fixe.-0
, pour créer desxargs
enregistrements terminés en lecture nulle. Il n'y a pas d'équivalence POSIX. POSIX, il faudrait citer les noms de fichiers dans un format reconnu parxargs
.Si les chemins d'accès se comportent bien et si la structure du répertoire est plate (pas de sous-répertoires), alors on pourrait se passer de ces drapeaux, sauf
-V
avecsort
.la source
printf ‘file_%d.pdb\0’ {1..15000} | xargs -0 cat
, ou même avec le point de Kevin,echo file_{1..15000}.pdb | xargs cat
. Lafind
solution a considérablement plus de frais généraux car elle doit rechercher le système de fichiers pour ces fichiers, mais elle est plus utile lorsque certains fichiers peuvent ne pas exister.xargs
plutôt quecat
celle qui est redirigée (chaquecat
invocation utiliseraxargs
une sortie standard). Si nous l'avions dit,xargs -0 sh -c 'cat >all.pdb'
il aurait été judicieux d'utiliser à la>>
place de>
, si c'est ce à quoi vous faites allusion.sort -n -k1.6
cela fonctionnerait (pour lesfile_nnn
noms de fichiers originaux ousort -n -k1.5
pour ceux sans le soulignement).Avec
zsh
(d'où{1..15000}
vient cet opérateur):Ou pour tous les
file_<digits>.pdb
fichiers dans l'ordre numérique:(où
<x-y>
est un opérateur glob qui correspond aux nombres décimaux x à y. Sansx
niy
, c'est n'importe quel nombre décimal. Équivalent àextendedglob
's[0-9]##
oukshglob
'+([0-9])
(un ou plusieurs chiffres)).Avec
ksh93
, à l'aide de sacat
commande intégrée (donc pas affecté par cette limite de l'execve()
appel système car il n'y a pas d' exécution ):Avec
bash
/zsh
/ksh93
(qui supportezsh
les{x..y}
et ontprintf
intégré):Sur un système GNU ou compatible, vous pouvez également utiliser
seq
:Pour les
xargs
solutions basées sur, une attention particulière devrait être apportée aux noms de fichiers contenant des blancs, des guillemets simples ou doubles ou des barres obliques inverses.Comme pour
-It's a trickier filename - 12.pdb
, utilisez:la source
seq -f | xarg cat >
la solution la plus élégante et la plus efficace. (A MON HUMBLE AVIS).'"./-It'\''s a trickier filename - %.17g.pdb"'
- être ?Une boucle for est possible et très simple.
L'inconvénient est que vous invoquez
cat
beaucoup de fois. Mais si vous ne vous souvenez pas exactement comment faire les chosesfind
et que les frais généraux d'invocation ne sont pas trop mauvais dans votre situation, alors cela vaut la peine de garder à l'esprit.la source
echo $i;
corps dans la boucle comme "indicateur de progression"la source
seq -f file_%.10g.pdb 15000
. Notez que ceseq
n'est pas une commande standard.seq -f
c'est une excellente façon de le faire; s'en souviendra.Prémisse
Vous ne devriez pas encourir cette erreur pour seulement 15 000 fichiers avec ce format de nom spécifique [ 1 , 2 ] .
Si vous exécutez cette extension à partir d'un autre répertoire et que vous devez ajouter le chemin d'accès à chaque fichier, la taille de votre commande sera plus grande et, bien sûr, cela peut se produire.
Solution exécutez la commande à partir de ce répertoire.
Meilleure solution Si à la place j'ai deviné mauvais et que vous l'exécutez à partir du répertoire dans lequel se trouvent les fichiers ... À
mon humble avis, la meilleure solution est celle de Stéphane Chazelas :
avec printf ou seq; testé sur des fichiers 15k avec seulement leur nombre à l'intérieur pré-mis en cache, il est même le plus rapide (à l'heure actuelle et à l'exception de l'OP du même répertoire dans lequel se trouvent les fichiers).
Quelques mots de plus
Vous devriez pouvoir passer à vos lignes de commande shell plus longtemps.
Votre ligne de commande contient 213914 caractères et contient 15003 mots
cat file_{1..15000}.pdb " > file_all.pdb" | wc
... même l'ajout de 8 octets pour chaque mot est 333 938 octets (0,3 M) bien en deçà du 2097142 (2,1 M) rapporté par
ARG_MAX
sur un noyau 3.13.0 ou du 2088232 légèrement plus petit rapporté comme "Longueur maximale de commande que nous pourrions réellement utiliser " parxargs --show-limits
Donnez un aperçu de votre système à la sortie de
Solution guidée paresse
Dans des cas comme celui-ci, je préfère travailler avec des blocs, même parce que généralement une solution efficace en temps.
La logique (le cas échéant) est que je suis beaucoup trop paresseux pour écrire 1 ... 1000 1001..2000 etc etc ...
Je demande donc à un script de le faire pour moi.
Ce n'est qu'après avoir vérifié que la sortie est correcte que je la redirige vers un script.
... mais la paresse est un état d'esprit .
Comme je suis allergique à
xargs
(j'aurais vraiment dû l'utiliserxargs
ici) et que je ne veux pas vérifier comment l'utiliser, je termine ponctuellement pour réinventer la roue comme dans les exemples ci-dessous (tl; dr).Notez que puisque les noms de fichiers sont contrôlés (pas d'espaces, de nouvelles lignes ...), vous pouvez aller facilement avec quelque chose comme le script ci-dessous.
tl; dr
Version 1: passez en paramètre optionnel le 1er numéro de fichier, le dernier, la taille du bloc, le fichier de sortie
Version 2
Appel bash pour l'expansion (un peu plus lent dans mes tests ~ 20%).
Bien sûr, vous pouvez aller de l'avant et vous débarrasser complètement de
seq
[ 3 ] (de coreutils) et travailler directement avec les variables dans bash, ou utiliser python, ou compiler un programme ac pour le faire [ 4 ] ...la source
%g
c'est court pour%.6g
. Cela représenterait 1 000 000 comme 1e + 06 par exemple.xargs
zshzargs
ouksh93
lescommand -x
.seq
n'est pas un bash intégré, c'est une commande de GNU coreutils.seq -f %g 1000000 1000000
sorties 1e + 06 même dans la dernière version de coreutils.xarg
... mais je comprends que c'est personnel et peut-être lié uniquement à moi.Une autre façon de le faire pourrait être
la source