Jak skopiować co czwarty plik w folderze

15

Mam dużo plików w folderze o nazwie jak 00802_Bla_Aquarium_XXXXX.jpg. Teraz muszę skopiować każdy 4th plik do podfolderu, mówiąc w 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

Jak mam to zrobic?

Aebersold
źródło
Możesz być w stanie zrobić jak superuser.com/q/396536/87552 na niektórych danych wyjściowych ls. Zobacz także stackoverflow.com/q/4553751/789593, który pyta o co drugą linię.
NN,
Jeśli są numerowane i znasz ostatni numer, możesz rozważyć odmianęfor n in $(seq -w 20 4 200); do cp "00802_..._00${n}" ...; done
Ulrich Schwarz

Odpowiedzi:

12

Zsh możesz zrobić:

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

POSIXly, ten sam pomysł, tylko trochę bardziej szczegółowy:

# 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

Ponieważ te nazwy plików nie zawierają żadnych pustych ani symboli wieloznacznych, możesz także:

cp $(printf '%s\n' 00802_Bla_Aquarium_?????.jpg | awk 'NR%4 == 1') /some/place
Stéphane Chazelas
źródło
Czy możesz wstawić komentarz również w kodzie posix
Rahul Patil
@ChrisDown, for i dojest standardem i faktycznie jest bardziej przenośny niż for i; doi jest kanonicznym sposobem na zapętlenie parametrów pozycyjnych.
Stéphane Chazelas,
7

W bashu, zabawna możliwość, która zadziała tutaj całkiem dobrze:

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

To zdecydowanie najkrótsza i najbardziej wydajna odpowiedź: bez podpowłoki, bez pętli, bez potoku, bez awkzewnętrznego procesu totem; tylko jeden rozwidlenie do cp(którego i tak nie można uniknąć) i jedno rozszerzenie nawiasów klamrowych i glob (które można całkowicie się pozbyć, ponieważ wiesz, ile plików masz).

gniourf_gniourf
źródło
4
… I 2 założenia: pliki są ponumerowane w sposób ciągły, a pierwszy jest podzielny za pomocą 4.
manatwork
@manatwork nie ma problemu, jeśli pierwszego nie da się podzielić przez 4: np. cp 00802_Bla_Aquarium_*{03..99..4}.jpg selectedjeśli chcesz 3 mod 4. (Korzystam ze wspaniałego faktu, że 100 można podzielić przez 4). Teraz, w przypadku plików o ciągłym numerowaniu, wszystkie pozostałe odpowiedzi zakładają to samo.
gniourf_gniourf
Jakie inne odpowiedzi zakładają ciągłą numerację? Znalazłem tylko komentarz Ulricha Schwarza i nie ma takiej odpowiedzi.
manatwork
5

Po prostu dzięki bash możesz:

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

Wzorzec ./*.jpgzostanie zastąpiony alfabetycznie uporządkowaną listą nazw plików podaną przez bash man, więc powinien pasować do twojego celu.

jfg956
źródło
W bash, linia wewnątrz trzy forpętli można zastąpić tylko tej linii: (((++n)%4)) || cp "$file" selected/. ;-). Problem polega jednak na tym, że tworzysz cpdla każdego pliku, który nie jest tak naprawdę wydajny.
gniourf_gniourf
1

Jeśli rubyzainstalowałeś, możesz użyć następującej linijki. Zauważ, że zakłada, że ​​wybrany katalog istnieje.

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

Pobiera listę wszystkich plików z rozszerzeniem .jpg w bieżącym katalogu i dzieli go na listy czterech elementów i kopiuje pierwszy element z każdej takiej listy do katalogu wybranego w bieżącym katalogu.

NN
źródło
1

Jeśli wiesz, że w nazwach plików nie będzie nowego wiersza, możesz użyć:

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
źródło
@Gilles: dzięki za edycję. Usunąłem zmienną pliku w 3 odczytanym, ponieważ chcę zaznaczyć, że nie są one potrzebne.
jfg956
1

Możesz użyć menedżera plików GUI i zmienić rozmiar obramowania okna, aż co 5 plików zajmie wiersz, a następnie za pomocą myszy wybierz pierwszą kolumnę ...

John Chain
źródło
-1

Patrząc na stackoverflow.com/q/4553751/789593, który oferuje rozwiązanie do kopiowania co drugi, jak wspomniano w @NN, bardzo prostym rozwiązaniem byłoby:

  • Usuń pierwszy plik z folderu początkowego
  • Skopiuj co sekundę z folderu początkowego do folderu pośredniego
  • Skopiuj co sekundę z folderu pośredniego do folderu końcowego
  • Dodaj pierwszy plik ręcznie

Może nie być bardzo wydajny, ponieważ wymaga dodatkowego kroku kopiowania, co powinno być bardzo łatwe do zrobienia.

Dennis Jaheruddin
źródło