Come copiare ogni 4o file in una cartella


15

Ho molti file in una cartella, chiamata like 00802_Bla_Aquarium_XXXXX.jpg. Ora devo copiare ogni quarto file in una sottocartella, dicendo in 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

Come faccio a fare questo?


Potresti riuscire a fare come le soluzioni su superuser.com/q/396536/87552 su alcuni output di ls. Vedi anche stackoverflow.com/q/4553751/789593 che richiede ogni seconda riga.
NN,

Se sono numerati e conosci l'ultimo numero, potresti considerare una variazione difor n in $(seq -w 20 4 200); do cp "00802_..._00${n}" ...; done
Ulrich Schwarz,

Risposte:


12

Con zsh, potresti fare:

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

POSIXly, stessa idea, solo un po 'più dettagliata:

# 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

Poiché quei nomi di file non contengono caratteri vuoti o jolly, puoi anche fare:

cp $(printf '%s\n' 00802_Bla_Aquarium_?????.jpg | awk 'NR%4 == 1') /some/place

Puoi per favore inserire un commento anche in codice posix
Rahul Patil

@ChrisDown, for i doè standard e in realtà più portatile di for i; doed è il modo canonico di passare in rassegna i parametri posizionali.
Stéphane Chazelas,

7

In bash, una possibilità divertente, che funzionerà piuttosto bene qui:

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

Questa è sicuramente la risposta più breve ed efficiente: nessuna subshell, nessun loop, nessuna pipe, nessun awkprocesso esterno di reparto; solo un fork a cp(che non puoi evitare comunque) e un bash brace espansione e glob (di cui puoi sbarazzarti del tutto poiché sai quanti file hai).


4
... e 2 ipotesi: i file sono numerati in modo continuo e il primo è divisibile per 4.
manatwork

@manatwork non c'è problema se il primo non è divisibile per 4: ad esempio, cp 00802_Bla_Aquarium_*{03..99..4}.jpg selectedse lo desideri 3 mod 4. (Sto usando il fatto meraviglioso che 100 è divisibile per 4). Ora, riguardo ai file numerati continuamente, tutte le altre risposte assumono lo stesso.
gniourf_gniourf,

Quali altre risposte presuppongono la numerazione continua? Ho trovato solo il commento di Ulrich Schwarz e nessuna risposta del genere.
arte

5

Semplicemente con bash, puoi fare:

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

Il modello ./*.jpgverrà sostituito da un elenco in ordine alfabetico di nomi di file come indicato dall'uomo bash, quindi dovrebbe adattarsi al tuo scopo.


In bash, la vostra linea dei tre all'interno del forciclo può essere sostituita con un solo questa linea: (((++n)%4)) || cp "$file" selected/. ;-). Il problema però è che stai eseguendo il fork di un file cpper ogni file che non è davvero efficiente.
gniourf_gniourf,

1

Se è stato rubyinstallato, è possibile utilizzare il seguente one-liner. Si noti che presuppone l'esistenza della directory selezionata.

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

Prende un elenco di tutti i file con estensione .jpg nella directory corrente e lo suddivide in elenchi di quattro elementi e copia il primo elemento da ciascuno di tali elenchi nella directory selezionata nella directory corrente.


1

Se sai di non avere newline nei nomi dei tuoi file, puoi usare:

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

@Gilles: grazie per la modifica. Ho rimosso la variabile di file nella lettura 3, tuttavia, voglio sottolineare che non sono necessari.
jfg956,

1

È possibile utilizzare un file manager della GUI e ridimensionare il bordo della finestra fino a quando ogni 5 file occupano una riga, quindi utilizzare il mouse per selezionare la prima colonna ...


-1

Guardando stackoverflow.com/q/4553751/789593 che offre una soluzione per copiare ogni secondo come menzionato da @NN, una soluzione molto semplice sarebbe:

  • Rimuovere il primo file dalla cartella iniziale
  • Copia ogni secondo dalla cartella iniziale a una cartella intermedia
  • Copia ogni secondo dalla cartella intermedia nella cartella finale
  • Aggiungi il primo file manualmente

Potrebbe non essere molto efficiente in quanto richiede un ulteriore passaggio di copia che dovrebbe essere molto facile da fare.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.