Как скопировать каждый 4-й файл в папку

15

У меня есть много файлов в папке, названной как 00802_Bla_Aquarium_XXXXX.jpg. Теперь мне нужно скопировать каждый 4-й файл в подпапку, говоря в 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

Как мне это сделать?

Aebersold
источник
Возможно, вам удастся сделать то же самое, что и решения superuser.com/q/396536/87552 для некоторых результатов ls. См. Также stackoverflow.com/q/4553751/789593, который спрашивает о каждой второй строке.
NN
Если они пронумерованы и вы знаете последний номер, вы можете рассмотреть вариантfor n in $(seq -w 20 4 200); do cp "00802_..._00${n}" ...; done
Ульрих Шварц

Ответы:

12

С Zsh вы можете сделать:

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

POSIXly, та же идея, просто более многословно:

# 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

Поскольку эти имена не содержат пустых или подстановочных знаков, вы также можете сделать:

cp $(printf '%s\n' 00802_Bla_Aquarium_?????.jpg | awk 'NR%4 == 1') /some/place
Стефан Шазелас
источник
Можете ли вы оставить комментарий также в коде posix
Рахул Патил
@ChrisDown, for i doявляется стандартным и на самом деле более переносимым, чем for i; doканонический способ зацикливания позиционных параметров.
Стефан Шазелас
7

В bash - забавная возможность, которая здесь будет работать довольно хорошо:

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

Это, безусловно, самый короткий и самый эффективный ответ: без вложенной оболочки, без цикла, без канала, без awkвнешнего процесса; только один ответвление cp(которого вы все равно не можете избежать) и одно расширение bash-скобки и глобус (от которого вы можете избавиться в целом, поскольку знаете, сколько у вас файлов).

gniourf_gniourf
источник
4
… И 2 предположения: файлы нумеруются непрерывно, а первое делится на 4.
manatwork 21.12.12
@manatwork без проблем, если первый не делится на 4: например, cp 00802_Bla_Aquarium_*{03..99..4}.jpg selectedесли вы хотите 3 mod 4. (Я использую замечательный факт, что 100 делится на 4). Теперь о непрерывно пронумерованных файлах все остальные ответы предполагают то же самое.
gniourf_gniourf
Какие другие ответы предполагают непрерывную нумерацию? Я нашел только комментарий Ульриха Шварца и такого ответа не было.
manatwork
5

Просто с помощью bash вы можете сделать:

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

Шаблон ./*.jpgбудет заменен отсортированным по алфавиту списком имен файлов, как указано в bash man, поэтому он должен соответствовать вашей цели.

jfg956
источник
В Баше, ваша три линии внутри forцикла может быть заменена только с этой линией: (((++n)%4)) || cp "$file" selected/. ;-), Проблема, однако, в том, что вы используете cpдля каждого файла, что не очень эффективно.
gniourf_gniourf
1

Если вы rubyустановили, вы можете использовать следующую однострочную. Обратите внимание, что предполагается, что выбранный каталог существует.

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

Он берет список всех файлов с расширением .jpg в текущем каталоге и разбивает его на списки из четырех элементов и копирует первый элемент из каждого такого списка в каталог, выбранный в текущем каталоге.

Н.Н.
источник
1

Если вы знаете, что в именах файлов не будет символов новой строки, вы можете использовать:

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
источник
@ Жиль: спасибо за редактирование. Я удалил файловую переменную в 3 read, поскольку хочу отметить, что они не нужны.
jfg956
1

Вы можете использовать файловый менеджер с графическим интерфейсом пользователя и изменять размер границы окна, пока каждые 5 файлов не занимают строку, а затем использовать мышь, чтобы выбрать первый столбец ...

Джон Чейн
источник
-1

Если посмотреть на stackoverflow.com/q/4553751/789593, который предлагает решение для копирования каждого второго, как упомянуто @NN, очень простое решение будет:

  • Удалить первый файл из начальной папки
  • Скопируйте каждую секунду из начальной папки в промежуточную папку
  • Скопируйте каждый второй из промежуточной папки в последнюю папку
  • Добавьте первый файл вручную

Это может быть не очень эффективно, так как требует дополнительного шага копирования, это должно быть очень легко сделать.

Деннис Джаэруддин
источник