Як скопіювати кожен 4-й файл у папку


15

У мене в папці багато файлів, названих як 00802_Bla_Aquarium_XXXXX.jpg. Тепер мені потрібно скопіювати кожен четвертий файл у підпапку, сказавши в 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

Як це зробити?


Можливо, вам вдасться зробити подібні рішення до 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

У баші, смішна можливість, що тут буде працювати досить добре:

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

Це, безумовно, найкоротша і найефективніша відповідь: немає підпалубника, немає петлі, немає труби, немає awkзовнішньої обробки кабінети; лише один розвід для cp(якого ви ніяк не можете уникнути) і одне розширення bash brace і глобул (що ви можете позбутися взагалі, оскільки знаєте, скільки файлів у вас є).


4
… І 2 припущення: файли нумеруються постійно, а перший ділиться на 4.
маніпулювання

@manatwork не проблема, якщо перший не ділиться на 4: наприклад, cp 00802_Bla_Aquarium_*{03..99..4}.jpg selectedякщо ви хочете 3 mod 4. (Я використовую чудовий факт, що 100 ділиться на 4). Тепер щодо файлів, що постійно пронумеровані, всі інші відповіді припускають те саме.
gniourf_gniourf

Які інші відповіді передбачають безперервну нумерацію? Я знайшов лише коментар Ульріха Шварца і такої відповіді немає.
манатура

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, тому він повинен відповідати вашим цілям.


У БАШЕЄВ, ваша три лінії всередині 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

@Gilles: дякую за редагування. Хоча я видалив змінну файлу в прочитаному 3, хоч хочу зазначити, що вони не потрібні.
jfg956

1

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


-1

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

  • Видаліть перший файл із початкової папки
  • Скопіюйте кожну секунду зі стартової папки в проміжну папку
  • Скопіюйте кожну секунду з проміжної папки в остаточну папку
  • Додайте перший файл вручну

Це може бути не дуже ефективно, оскільки для цього потрібен додатковий крок копіювання, це потрібно зробити дуже просто.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.