J'ai un dossier contenant 2 Go d'images, avec des sous-dossiers de plusieurs niveaux profonds.
Je voudrais archiver seulement N
files de chaque (sous) dossier dans un file tar. J'ai essayé d'utiliser find
puis tail
puis tar
mais je n'arrivais pas à le faire fonctionner. Voici ce que j'ai essayé (en supposant que N = 10
):
find . | tail -n 10 | tar -czvf backup.tar.gz
… qui génère cette erreur:
Cannot stat: File name too long
Qu'est-ce qui ne va pas ici? en y pensant – même si cela fonctionne, je pense que cela ne goudrera que les 10 premiers files de tous les dossiers, pas les 10 files de chaque dossier.
Comment puis-je get N
files de chaque dossier? (Aucun ordre de file nécessaire)
Si votre pax
prend en charge l'option -0
, avec zsh
:
print -rN dir/**/*(D/e:'reply=($REPLY/*(ND^/[1,10]))':) | pax -w0 | xz > file.tar.xz
Il inclut les 10 premiers files non-directory de chaque directory de la list sortingés par nom de file. Vous pouvez choisir un ordre de sorting différent en ajoutant le qualificateur om
glob (ordre par heure de modification, Om
pour inverser l'ordre), oL
(ordre par longueur), non
(sorting par nom mais numériquement) …
Si vous n'avez pas la command pax
standard, ou si elle ne supporte pas -0
mais que vous avez la command GNU tar
, vous pouvez faire:
print -rN -- dir/**/*(D/e:'reply=($REPLY/*(ND^/[1,10]))':) | tar --null -T - -cjf file.tar.xz
Si vous ne pouvez pas utiliser zsh
, mais avoir access à bash
(le shell du projet GNU), vous pourriez faire:
find dir -type d -exec bash -O nullglob -O dotglob -c ' for dir do set -- "$dir/*"; n=0 for file do if [ ! -d "$file" ] || [ -L "$file" ]; then printf "%s\0" "$file" (( n++ < 10 )) || break fi done done' bash {} + | pax -0w | xz > file.tar.xz
Ce serait cependant beaucoup less efficace.
Supposons que votre directory principal soit /tmp/dir
partir duquel vous ne souhaitez archiver que des files N (par exemple N = 10) de chaque sous-dossier dans un file backup.tar.gz
.
Exemple d' tree
pour /tmp/dir
:
dir/ ├── one │ ├── one10.txt │ ├── one11.txt │ ├── one1.txt │ ├── one2.txt │ ├── one3.txt │ ├── one4.txt │ ├── one5.txt │ ├── one6.txt │ ├── one7.txt │ ├── one8.txt │ ├── one9.txt │ └── one_deep │ ├── one_deep1 │ ├── one_deep10 │ ├── one_deep11 │ ├── one_deep2 │ ├── one_deep3 │ ├── one_deep4 │ ├── one_deep5 │ ├── one_deep6 │ ├── one_deep7 │ ├── one_deep8 │ └── one_deep9 ├── three │ ├── three10.txt │ ├── three11.txt │ ├── three1.txt │ ├── three2.txt │ ├── three3.txt │ ├── three4.txt │ ├── three5.txt │ ├── three6.txt │ ├── three7.txt │ ├── three8.txt │ ├── three9.txt │ └── three_deep │ ├── three_deep1 │ ├── three_deep10 │ ├── three_deep11 │ ├── three_deep2 │ ├── three_deep3 │ ├── three_deep4 │ ├── three_deep5 │ ├── three_deep6 │ ├── three_deep7 │ ├── three_deep8 │ └── three_deep9
cd /tmp; for i in `find dir/* -type d`; do find $i -maxdepth 1 -type f | tail -n 10 | xargs -I file tar -rf backup.tar file; done; gzip backup.tar
Cela créera un backup.tar.gz
avec 10 files de chaque sous-dossier de sous /tmp/dir
.
Comme la sortie de find
est plate, vous ne savez pas vraiment quels files appartiennent aux mêmes directorys sans regarder les paths. L'alternative est d'utiliser plusieurs find
(une par dossier), sans avoir à regarder les paths. C'est ce que j'ai fait. Pour utiliser jusqu'à 10 files de chaque sous-dossier, utilisez quelque chose comme ceci:
for dir in $(find . -type d); do find "$dir" -maxdepth 1 -type f -printf "\"%p\"\n" | tail -10 done | xargs tar cvfz backup.tar.gz
Cela récursivement trouve tous les directorys dans le dossier en cours. Pour chaque directory, il trouve jusqu'à 10 files exactement dans ce dossier ( -maxdepth 1
). Une fois la boucle terminée, la command tar
est exécutée sur tous les files qui ont été générés par la boucle. J'ai également pris en count les noms de directory et de dossier avec des espaces en citant $dir
et en ayant find
printing de chaque nom de file entre guillemets en utilisant l'option -printf
.
for d in ./*/ do cd "$d" tar -rvf ../backup.tar $(ls | tail -10) cd .. done gzip backup.tar
autre variante
find * -prune -type d -exec bash -c 'printf "%s\n" $0/* | tail -10' {} \; | tar czvf backup.tar.gz -T -
Utilisez un hachage sur le nom du directory et n'émettez le nom de file que si le nombre de valeurs de hachage est inférieur au seuil. Par exemple
find . -depth -type f \ | perl -MFile::Spec -nle '(undef,$d,$f)=File::Spec->splitpath($_); print if $seen{$d}++ < 3' \ | tar ...
Le moyen le plus simple (ou le plus facile à comprendre) est d'utiliser xargs avec l'option -N max-args
.
Gardez à l'esprit que votre input doit toujours être quelque chose qui ne nécessite pas de command line, alors echo *.*
Fonctionnera comme input, où ls *.*
Ne le fait pas (trop long)
find devrait être bien, car son argument est seulement le path, pas une list de files.
OP a demandé cela dans Stackoverflow aussi . Voici la réponse que j'ai offert là.
La sélection et l'ordre des files dans cette réponse sont déterminés par l'ordre de find
, donc "premier" n'est pas bien défini ici. Cela peut également dépendre de GNU Awk 4.1.0.
find . -type f | awk -v N = 10 -F / 'match ($ 0, /.*\//, m) && a [m [0]] ++ <N' | xargs -r -d '\ n' tar -rvf /tmp/backup.tar gzip /tmp/backup.tar
Commentaires:
find . -type f
find . -type f
pour s'assurer que les files ont un préfixe de premier nom de directory, de sorte que la prochaine étape peut fonctionner awk
suit ces noms de directorys principaux et émet les noms de paths complets jusqu'à ce que des files N (10, ici) soient émis avec le même directory principal (peut-être plus simple) xargs
pour invoquer tar
– nous recueillons des noms de files réguliers, et ils doivent être des arguments pour cette command d'archivage xargs
peut invoquer tar
plus d'une fois, donc nous allons append (option -r) à une archive simple, puis la compresser après tout ce qui est écrit En outre, vous ne voudrez peut-être pas écrire un file de sauvegarde dans le directory courant, puisque vous numérisez cela – c'est pourquoi cette suggestion écrit dans / tmp.