Как переместить определенный диапазон файлов из одного каталога в другой

В моем родительском каталоге у меня есть 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)

Как пользоваться

  1. Скопируйте скрипт в пустой файл, сохраните его как reorganize.py
  2. при желании вы можете изменить имя подкаталога "body" (имя раздела без номера) в заголовке скрипта, в:

    namebody = "chunk_"
    
  3. Запустите его с главным каталогом и размером чанка в качестве аргументов:

    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
  1. -print0 выводит имена файлов, разделенные NUL (\0) персонаж. Это самый безопасный способ передачи имен файлов в качестве вывода другим командам.
  2. sort с -z ищет входные данные, разделенные нулями, и --version-sort позволяет безопасно сортировать числа переменной длины, так что filename2.xyz предшествует fileName3.xyz
  3. xargs с -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
Другие вопросы по тегам