Comment copier chaque 4ème fichier dans un dossier

15

J'ai beaucoup de fichiers dans un dossier, nommé comme 00802_Bla_Aquarium_XXXXX.jpg. Maintenant, je dois copier chaque 4ème fichier dans un sous-dossier, en disant selected/.

00802_Bla_Aquarium_00020.jpg <= this one
00802_Bla_Aquarium_00021.jpg
00802_Bla_Aquarium_00022.jpg
00802_Bla_Aquarium_00023.jpg
00802_Bla_Aquarium_00024.jpg <= this one
00802_Bla_Aquarium_00025.jpg
00802_Bla_Aquarium_00026.jpg
00802_Bla_Aquarium_00027.jpg
00802_Bla_Aquarium_00028.jpg <= this one
00802_Bla_Aquarium_00029.jpg

Comment puis-je faire cela?

aebersold
la source
Vous pourrez peut-être faire comme les solutions de superuser.com/q/396536/87552 sur certaines sorties de ls. Voir aussi stackoverflow.com/q/4553751/789593 qui pose des questions sur toutes les deux lignes.
NN
S'ils sont numérotés et que vous connaissez le dernier numéro, vous pourriez envisager une variation defor n in $(seq -w 20 4 200); do cp "00802_..._00${n}" ...; done
Ulrich Schwarz

Réponses:

12

Avec zsh, vous pourriez faire:

n=0; cp 00802_Bla_Aquarium_?????.jpg(^e:'((n++%4))':) /some/place

POSIXly, même idée, juste un peu plus verbeuse:

# put the file list in the positional parameters ($1, $2...).
# the files are sorted in alphanumeric order by the shell globbing
set -- 00802_Bla_Aquarium_?????.jpg

n=0
# loop through the files, increasing a counter at each iteration.
for i do
  # every 4th iteration, append the current file to the end of the list
  [ "$(($n % 4))" -eq 0 ] && set -- "$@" "$i"

  # and pop the current file from the head of the list
  shift
  n=$(($n + 1))
done

# now "$@" contains the files that have been appended.
cp -- "$@" /some/place

Étant donné que ces noms de fichiers ne contiennent aucun caractère vierge ou générique, vous pouvez également effectuer:

cp $(printf '%s\n' 00802_Bla_Aquarium_?????.jpg | awk 'NR%4 == 1') /some/place
Stéphane Chazelas
la source
Pouvez-vous s'il vous plaît mettre également un commentaire en code posix
Rahul Patil
@ChrisDown, for i doest standard et en fait plus portable que for i; doet est la manière canonique de boucler sur les paramètres positionnels.
Stéphane Chazelas
7

En bash, une drôle de possibilité, qui fonctionnera plutôt bien ici:

cp 00802_Bla_Aquarium_*{00..99..4}.jpg selected

C'est certainement la réponse la plus courte et la plus efficace: pas de sous-coque, pas de boucle, pas de tuyau, pas awkde processus externe de salle; juste une fourchette vers cp(que vous ne pouvez pas éviter de toute façon) et une expansion et un glob bash brace (dont vous pouvez vous débarrasser complètement puisque vous savez combien de fichiers vous avez).

gniourf_gniourf
la source
4
… Et 2 hypothèses: les fichiers sont numérotés en continu et le premier est divisible par 4.
manatwork
@manatwork pas de problème si le premier n'est pas divisible par 4: par exemple, cp 00802_Bla_Aquarium_*{03..99..4}.jpg selectedsi vous le souhaitez 3 mod 4. (J'utilise le fait merveilleux que 100 est divisible par 4). Maintenant, à propos des fichiers numérotés en continu, toutes les autres réponses supposent la même chose.
gniourf_gniourf
Quelles autres réponses supposent une numérotation continue? Je n'ai trouvé que le commentaire d'Ulrich Schwarz et aucune réponse.
manatwork
5

Simplement avec bash, vous pouvez faire:

n=0
for file in ./*.jpg; do
   test $n -eq 0 && cp "$file" selected/
   n=$((n+1))
   n=$((n%4))
done

Le modèle ./*.jpgsera remplacé par une liste triée par ordre alphabétique des noms de fichiers, comme indiqué par l'homme bash, il devrait donc correspondre à votre objectif.

jfg956
la source
En bash, vos trois lignes dans la forboucle peut être simplement remplacé par cette ligne: (((++n)%4)) || cp "$file" selected/. ;-). Le problème est que vous cherchez un fichier cppour chaque fichier, ce qui n'est pas vraiment efficace.
gniourf_gniourf
1

Si vous avez rubyinstallé, vous pouvez utiliser la doublure suivante. Notez qu'il suppose que le répertoire sélectionné existe.

ruby -rfileutils -e 'files = Dir.glob("*.jpg").each_slice(4) { |file| FileUtils.cp(file.first, "selected/" + file.first) }'

Il prend une liste de tous les fichiers avec l'extension .jpg dans le répertoire courant et le découpe en listes de quatre éléments et copie le premier élément de chacune de ces listes dans le répertoire sélectionné dans le répertoire courant.

NN
la source
1

Si vous savez que vous n'aurez pas de retour à la ligne dans vos noms de fichiers, vous pouvez utiliser:

find . -maxdepth 1 -name "*.jpg" | sort | while IFS= read -r file; do
  cp "$file" selected/
  IFS= read -r; IFS= read -r; IFS= read -r
done
jfg956
la source
@Gilles: merci pour l'édition. J'ai cependant supprimé la variable de fichier dans la lecture 3, car je tiens à souligner qu'elles ne sont pas nécessaires.
jfg956
1

Vous pouvez utiliser un gestionnaire de fichiers GUI et redimensionner la bordure de la fenêtre jusqu'à ce que tous les 5 fichiers occupent une ligne, puis utilisez la souris pour sélectionner la première colonne ...

John Chain
la source
-1

En regardant stackoverflow.com/q/4553751/789593 qui offre une solution pour copier chaque seconde comme @NN l'a mentionné, une solution très simple serait:

  • Supprimez le premier fichier du dossier de départ
  • Copiez chaque seconde du dossier de départ vers un dossier intermédiaire
  • Copiez chaque seconde du dossier intermédiaire dans le dossier final
  • Ajouter le premier fichier manuellement

Il peut ne pas être très efficace car il nécessite un bit d'étape de copie supplémentaire, il devrait être très facile à faire.

Dennis Jaheruddin
la source