Ma question est similaire à Comment trier les fichiers dans la commande coller ? - qui a été résolu.
J'ai 500 fichiers csv (données de précipitations quotidiennes) dans un dossier avec la convention de dénomination chirps_yyyymmdd.csv . Chaque fichier n'a qu'une seule colonne (valeur des précipitations) avec 100 000 lignes et aucun en-tête. Je souhaite fusionner tous les fichiers csv en un seul csv par ordre chronologique.
Quand j'ai essayé ce script ls -v fichier _ *. csv | xargs coller -d,
avec seulement 100 fichiers csv, cela a fonctionné. Mais lorsque j'ai essayé d'utiliser 500 fichiers csv, j'ai eu cette erreur: paste: chirps_19890911.csv: Trop de fichiers ouverts
Comment gérer l'erreur ci-dessus?
Pour une solution rapide, je peux diviser les csv en deux dossiers et effectuer le processus en utilisant le script ci-dessus. Mais, le problème, j'ai 100 dossiers et il a 500 csv dans chaque dossier.
Merci
Exemple de données et résultat attendu: https: //www.dropbox .com / s / ndofxuunc1sm292 / data.zip? dl = 0
5 Réponses :
Commencez par créer un fichier sans le coller et changez-le en un seul doubleur avec tr
:
cat */chirps_*.csv | tr "\n" "," > long.csv
Merci pour la réponse, mais en utilisant le script ci-dessus, j'ai obtenu le résultat en 1 ligne. Alors que mes données pour chaque fichier sont 1 colonne et 100 000 lignes et sans en-tête. Si je veux fusionner 500 fichiers csv, je dois obtenir le résultat 500 colonnes et 100000 lignes
Dans ce cas, omettez le tr
, cat * / chirps _ *. Csv> long.csv
devrait fonctionner. Vous avez mentionné coller
, c'est pourquoi j'ai essayé de coller les lignes.
Le résultat est comme une transposition du code précédent.
Vous pouvez le faire avec gawk
comme ceci ...
Il suffit de lire tous les fichiers l'un après l'autre et de les enregistrer dans un tableau. Le tableau est indexé par deux nombres, d'une part le numéro de ligne dans le fichier courant ( FNR
) et d'autre part la colonne, que j'incrémente à chaque fois que nous rencontrons un nouveau fichier dans le code BEGINFILE > block.
Ensuite, à la fin, imprimez le tableau entier:
$HOME/merge > merged.csv
SEP
est juste un caractère inutilisé cela fait un séparateur entre les indices. J'utilise gawk
car BEGINFILE
est utile pour incrémenter le numéro de colonne.
Enregistrez ce qui précède dans votre répertoire HOME en tant que merge
. Ensuite, démarrez un Terminal et, une seule fois, rendez-le exécutable avec la commande:
$HOME/merge
Maintenant, changez le répertoire où sont vos chirps avec une commande comme:
cd subdirectory/where/chirps/are
Vous pouvez maintenant exécuter le script avec:
chmod +x merge
La sortie passera rapidement à l'écran. Si vous le souhaitez dans un fichier, utilisez:
gawk 'BEGINFILE{ ++col } # New file, increment column number { X[FNR SEP col]=$0; rows=FNR } # Save datum into array X, indexed by current record number and col END { for(r=1;r<=rows;r++){ comma="," for(c=1;c<=col;c++){ if(c==col)comma="" printf("%s%s",X[r SEP c],comma) } printf("\n") } }' chirps*
Merci, mais je suis désolé de ne pas connaître le code ci-dessus. Comment l'utiliser? Enregistrez-le en tant que fichier de code et exécutez-le dans le terminal? Ou simplement coller et exécuter?
Désolé, j'ai ajouté des notes sur la façon de l'utiliser à la fin. Veuillez dire si vous êtes coincé. Bonne chance.
Merci, j'ai réussi à installer gawk dans mon mac en utilisant "brew install gawk". Et puis en suivant votre ligne directrice, essayez de l'exécuter. C'est déjà 15 minutes, toujours pas encore fini. Ai-je manqué quelque chose?
Cela ne fonctionnera pas très bien si vous n'avez pas beaucoup de mémoire car il charge tous les fichiers par colonne en mémoire puis les imprime par ligne, je ne l'ai testé qu'avec votre échantillon de données et cela a bien fonctionné mais je ne l'ai pas essayez avec plus que cela. Une fois qu'il commence à sortir, ce sera presque instantané.
Ah ok. J'attendrai.
Vous pouvez essayer de vérifier la quantité de mémoire dont vous disposez dans le menu Pomme -> À propos de ce Mac , puis démarrez Activity Monitor et cliquez sur Mémoire
en haut pour voyez combien de mémoire cela prend et si cela continue de grandir.
Ou, vous pouvez exécuter à nouveau (si vous avez la patience) et changer le code où il dit ++ col
en ++ col; print col
pour voir le numéro du fichier en cours de lecture afin que vous ayez une idée de l'endroit où il se trouve.
Si l'objectif est un fichier avec 100 000 lignes et 500 colonnes, quelque chose comme ceci devrait fonctionner:
paste -d, chirps_*.csv > chirps_500_merge.csv
Un code supplémentaire peut être utilisé pour trier les chirps _... fichiers d'entrée dans n'importe quel ordre souhaité avant de coller
ing.
L'erreur vient de -n ou --file-descriptor-count Le nombre maximum de descripteurs de fichiers ouverts Sur mon système, Heureusement, nous pouvons coller la sortie collée, afin de pouvoir l'enchaîner. Testé par rapport aux données de test générées avec: Le problème semble être un peu comme Je suppose que PS. ulimit
, de man ulimit a>:
ulimit -n
renvoie 1024. func() {
paste -d, "$@"
}
tmp=()
tmpfilecreated=0
while readarray -t -n1023 files && ((${#files[@]})); do
tmp=("$(mktemp)")
func "${tmp[@]}" "${files[@]}" >"$tmp"
if ((tmpfilecreated)); then
rm "${files[0]}"
fi
tmpfilecreated=1
done
func "${tmp[@]}" "${files[@]}"
if ((tmpfilecreated)); then
rm "${files[0]}"
fi
func() {
paste -d, "$@"
}
files=()
tmpfilecreated=0
# read filenames...c
while IFS= read -r line; do
files+=("$line")
# if the limit of 1024 files is reached
if ((${#files[@]} == 1024)); then
tmp=$(mktemp)
func "${files[@]}" >"$tmp"
# remove the last tmp file
if ((tmpfilecreated)); then
rm "${files[0]}"
fi
tmpfilecreated=1
# start with fresh files list
# with only the tmp file
files=("$tmp")
fi
done
func "${files[@]}"
# remember to clear tmp file!
if ((tmpfilecreated)); then
rm "${files[0]}"
fi
foldl
avec une taille maximale de morceau à plier en une seule passe. Fondamentalement, nous voulons que coller -d, ) )
qui s'exécute de manière récursive. Avec un peu de plaisir, j'ai trouvé ce qui suit: seq 1 2000 |
xargs -P0 -n1 -t sh -c '
seq 1 1000 |
sed "s/^/ $RANDOM/" \
>"file_$(date --date="-${1}days" +%Y%m%d).csv"
' --
readarray
/ mapfile
pourrait être plus rapide, et en résulter un peu code plus clair: find . -type f -name 'file_*.csv' |
sort |
xargs -n$(ulimit -n) sh -c '
tmp=$(mktemp);
paste -d, "$@" >$tmp;
echo $tmp
' -- |
xargs sh -c '
paste -d, "$@"
rm "$@"
' --
Je souhaite fusionner tous les fichiers csv en un seul csv par ordre chronologique.
Ne serait-ce pas simplement couper
? À l'heure actuelle, chaque colonne représente un jour.
Merci pour le code. Comment l'utiliser? Enregistrez-le en tant que fichier de code et exécutez-le dans le terminal? Ou simplement coller et exécuter?
Le premier extrait de code est prêt pour la copie. Pour le reste - find. -type f -name 'fichier * .csv' | trier | (....)
- Remplacez le ...
par le code 3ème ou 4ème extrait de code.
Pardon de mon ignorance, je ne suis pas familier avec cela. J'ai entré tout le code. Où est l'emplacement de sortie (tmp)? Est-ce dans $ HOME?
Il y a un reste de mes tests -p / tmp / a
dans l'argument mktemp à l'intérieur de xargs, devrait probablement être supprimé.
Cela a fonctionné, maintenant toujours en cours d'exécution et je peux voir la progression dans Terminal. Mais comment enregistrer la sortie dans un fichier? Disons merged.csv dans le même dossier avec les données d'entrée.
Utilisez la redirection de commande blabal | blabla | blabla | blabla> fichier
bash enregistrer la sortie dans un fichier a>. Vous pouvez également | fichier tee
pour avoir la sortie à la fois dans le fichier et sur stdout
Vous pouvez essayer cette doublure Perl-one. Cela fonctionnera pour n'importe quel nombre de fichiers correspondant à * .csv sous un répertoire
$ ls -1 *csv file_1.csv file_2.csv file_3.csv $ cat file_1.csv 1 2 3 $ cat file_2.csv 4 5 6 $ cat file_3.csv 7 8 9 $ perl -e ' BEGIN { while($f=glob("*.csv")) { $i=0;open($FH,"<$f"); while(<$FH>){ chomp;@t=@{$kv{$i}}; push(@t,$_);$kv{$i++}=[@t];}} print join(",",@{$kv{$_}})."\n" for(0..$i) } ' < 1,4,7 2,5,8 3,6,9 $
Bienvenue dans StackOverflow! Il me semble que votre question ne concerne pas sed, awk ou csv, mais comment utiliser le shell. Si ce n'est pas vraiment une question de programmation, c'est probablement hors sujet pour StackOverflow. Vous pouvez envisager de le fermer ici et de publier une version révisée de votre question sur SuperUser.com ou unix.stackexchange.com . Cherchez également à utiliser une boucle
for
dans votre shell. C'est la manière canonique de traiter les fichiers de manière séquentielle. L'analysels
n'est généralement pas une bonne idée. .Salut @ghoti merci pour votre réponse, j'ai révisé la question et supprimer la balise inutile
Quel est le nom de vos dossiers?
@Cyrus Le nom du dossier est une année, de 1900 à 2018
Les bonnes questions ont souvent un exemple minimal, complet et vérifiable . Pouvez-vous nous montrer quelques exemples d'entrée et de sortie? Si une solution de
coller
ne fonctionne pas, autre chose pourrait le faire, mais il serait bon de savoir que nous sommes sur la bonne voie en étant capable de reproduire des résultats positifs.J'ai révisé ma question, et mis le lien par exemple les données et la sortie attendue
Collez les 500 fichiers de chaque répertoire dans son propre fichier, puis collez ces 100 nouveaux fichiers dans un seul fichier volumineux.