Как переместить определенный диапазон файлов из одного каталога в другой
В моем родительском каталоге у меня есть 10000 файлов, я хочу скопировать или переместить файлы в 4 подкаталога: subdir1, subdir2, subdir3, subdir4 одинаково и, если возможно, одновременно. Можно ли скопировать определенный диапазон файлов из родительского каталога в подкаталоги, т.е.
1-2500 файлов на subdir1
2500-5000 файлов в subdir2
5000-7500 файлов в subdir3
7500-10000 файлов в subdir4
Все это должно быть сделано с использованием командного файла. Является ли это возможным? Пожалуйста, помогите мне, если кто-нибудь знает.
Заранее спасибо.
4 ответа
Этот работает для произвольного числа файлов и может иметь дело со странными именами файлов (которые содержат пробелы, переводы строки, обратную косую черту или другую странность):
#!/usr/bin/env bash
## This will keep track of the number of files processed
num=0;
## This is used to choose the righht subdir
dir=1;
## The initial value of the target directory
target="subdir1"
for file in *; do
## Skip unless this is a file
if [ -f "$file" ]; then
## Create the target directory if it doesn't exist
[ -d "$target" ] || mkdir "$target"
## Move the current file
mv "$file" "$target"
## Change the target if we're at a multiple of 2500
if [[ $(( ++num % 2500 )) -eq 0 ]]; then
target="subdir"$((++dir));
fi
fi
done
Вы также можете реализовать то же самое, используя find
:
#!/usr/bin/env bash
## This will keep track of the number of files processed
num=0;
## This is used to choose the right subdir
dir=1;
## The initial value of the target directory
target="subdir1"
## Run your find, with -print0 to print NUL-separated values. This
## is needed for file names that contain newlines
find . -type f -print0 |
## The IFS= makes this deal with spaces, the -d '' sets the input delimiter
## to NUL so ti can work with -print0 and the -r makes it work with backslashes
while IFS= read -d '' -r file; do
## Create the target directory if it doesn't exist
[ -d "$target" ] || mkdir "$target"
## Move the current file
mv "$file" "$target"
## Change the target if we're at a multiple of 2500
if [[ $(( ++num % 2500 )) -eq 0 ]]; then
target="subdir"$((++dir));
fi
done
Сохранить этот скрипт как ~/bin/batch_rename.sh
сделать его исполняемым (chmod a+x ~/bin/batch_rename.sh
), а затем запустите его из каталога, в котором находятся файлы.
ЗАМЕТКИ
Первый пример найдет файлы только в текущем каталоге. Чтобы сделать его рекурсивным, добавьте эту строку в начало:
shopt -s globstar
Затем измените
for file in *
вfor file in **/*
,Второй пример найдет все файлы в этом и любом подкаталоге. Это может или не может быть то, что вы хотите.
Если заказ не является проблемой, сценарий ниже:
- разбивает файлы на (произвольные) куски
- создает подкаталоги для каждого чанка (
chunk_1
,chunk_2
так далее.) - перемещает соответствующие файлы в подкаталоги
Обратите внимание, что:
- Если заказ является проблемой, сценарий нуждается в незначительной корректировке, но затем, пожалуйста, включите правила заказа в вопрос.
- Скрипт не "возражает" за файлы (имена) с пробелами и т. Д. Даже имя "подкаталога" может содержать пробелы.
Сценарий
#!/usr/bin/env python3
import os
import shutil
import sys
#--- if desired, change the sub dir's name body below
namebody = "chunk_"
#---
dr = sys.argv[1]; size = int(sys.argv[2]);
files = [f for f in os.listdir(dr) if os.path.isfile(dr+"/"+f)]
n = max(1, size)
chunks = [files[i:i + size] for i in range(0, len(files), size)]
for i, item in enumerate(chunks):
subfolder = os.path.join(dr, namebody+str(i+1))
if not os.path.exists(subfolder):
os.makedirs(subfolder)
for f in chunks[i]:
shutil.move(dr+"/"+f, subfolder+"/"+f)
Как пользоваться
- Скопируйте скрипт в пустой файл, сохраните его как
reorganize.py
при желании вы можете изменить имя подкаталога "body" (имя раздела без номера) в заголовке скрипта, в:
namebody = "chunk_"
Запустите его с главным каталогом и размером чанка в качестве аргументов:
python3 /path/to/reorganize.py <main_directory> <chunk_size>
Это bash
Сценарий будет перемещать любое количество файлов, присутствующих в целевом каталоге, переданном ему в качестве аргумента, равномерно разделяя их на любое число, переданное ему в качестве аргумента целевых подкаталогов с именем subdir<N>
создание целевых подкаталогов, если их еще нет; он должен быть размещен вне целевого каталога для запуска, чтобы избежать его перемещения во время выполнения.
Использование:
./script.sh <path_to_target_directory> <number_of_subdirectories>
*
#!/bin/bash
for i in `seq 1 "${2}"`
do
mkdir -p \'"${1}"/subdir"${i}"\'
done
j=0
find \'"${1}"\' -maxdepth 1 -type f | while read -r filepath
do
N=$(( ${j} % ${2} + 1 ))
mv \'"${filepath}"\' \'"${1}/subdir${N}"\'
((${j}++))
done
Результаты для ./script.sh ~/testdir 4
:
До:
~ / Testdir ├── 1 10── 10 2── 2 3── 3 4── 4 5── 5 6── 6 7── 7 8── 8 9── 9
После:
~ / Testdir ├── subdir1 1 ├── 1 2 ├── 2 7 └── 7 Sub── subdir2 10 ├── 10 6 ├── 6 8 └── 8 Sub── subdir3 3 ├── 3 9 └── 9 Sub── subdir4 4── 4 5── 5
На основании моего старого скрипта с некоторыми изменениями (просто изменил значение N и имена переменных:):
Группировка каждого N файлов в отдельные каталоги
##grouping each N files in separate directories
echo 1 > dircount;
find source -type f -name 'filterFileName' -print0 | sort -z --version-sort | \
xargs -0n2500 bash -c 'read TARGET <dircount; \
echo mkdir -p "subdir$TARGET"; \
echo mv -t "subdir$TARGET" "$@"; \
expr $TARGET + 1 >dircount' grouping
-print0
выводит имена файлов, разделенные NUL (\0
) персонаж. Это самый безопасный способ передачи имен файлов в качестве вывода другим командам.sort
с-z
ищет входные данные, разделенные нулями, и--version-sort
позволяет безопасно сортировать числа переменной длины, так что filename2.xyz предшествует fileName3.xyzxargs
с-n2500
ограничить количество аргументов, применяемых к каждой команде (в этом случае 2500 аргументов).-0
для ввода с разделением нулями.
Примечание: не забывайте, что вы запускаете скрипт в тестовом случае, поэтому очистите echo
Команда рядом с соответствующими строками для выполнения для запуска фактического сценария.
Также есть два других скрипта, которые не так быстры как выше:
groupFiles=0
TARGET=1
for file in `ls -v /path/to/source/filterFileName` ; do
mkdir -p "subdir$TARGET" && mv "$file" "subdir$TARGET"
[[ ++groupFiles -eq 2500 ]] && groupFiles=0 && ((TARGET++))
done
ls -v
Команда сортирует файлы как натуральные числа (версии).
Вы можете разобратьls
команда, если имя файла не имеет пробелов, новой строки и т. д.mkdir -p "subdir$TARGET"
создает каталог на основеTARGET
переменная.mv "$file" "subdir$TARGET"
перемещает файл в каталог, которыйTARGET
определяет.- Сбросить
groupFiles=0
в ноль при перемещении 2500 файлов ([[ ++groupFiles -eq 2500 ]]
) и увеличение доTARGET
значение.
Обратите внимание, что изменение /path/to/source/
в ваш фактический исходный каталог.
Если вы не хотите анализировать ls
имя файла команды, включая пробелы, новые строки и т. д., вот еще один вариант:
groupFiles=0
TARGET=1
find /path/to/source/ -type f -name 'filterFileName' -print0 | \
sort -z --version-sort | while IFS= read -d '' -r file; do \
mkdir -p "subdir$TARGET" && mv "$file" "subdir$TARGET" ;
[[ ++groupFiles-eq 7 ]] && groupFiles=0 && ((TARGET++));
done