como mover um intervalo específico de arquivos de um diretório para outro

3

No meu diretório pai eu tenho 10000 arquivos, eu quero copiar ou mover os arquivos para 4 subdiretórios subdir1, subdir2, subdir3, subdir4 igualmente e se possível simultaneamente. É possível copiar um determinado intervalo de arquivos do diretório pai para os subdiretórios, ou seja,

1-2500 arquivos para subdir1
2500-5000 arquivos para subdir2
5000-7500 arquivos para subdir3
7500-10000 arquivos para subdir4

Tudo isso deve ser feito usando um arquivo em lotes. É possível? Pls me ajude se alguém souber.

Obrigado antecipadamente.

    
por satyanaryana reddy 27.04.2015 / 13:18

4 respostas

2

Este funciona para um número arbitrário de arquivos e pode lidar com nomes de arquivos estranhos (aqueles que contêm espaços, novas linhas, barras invertidas ou outras estranhezas):

#!/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

Você também pode implementar a mesma coisa usando 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             

Salve esse script como ~/bin/batch_rename.sh , torne-o executável ( chmod a+x ~/bin/batch_rename.sh ) e, em seguida, execute-o no diretório onde os arquivos estão .

NOTAS

  • O primeiro exemplo só encontrará arquivos no diretório atual. Para torná-lo recursivo, adicione esta linha ao início:

    shopt -s globstar
    

    Em seguida, altere o for file in * para for file in **/* .

  • O segundo exemplo encontrará todos os arquivos neste e em qualquer subdiretório. Isso pode ou não ser o que você quer.

por terdon 27.04.2015 / 17:28
1

Se o pedido não for um problema, o script abaixo:

  • divide os arquivos em partes (arbitrárias)
  • cria os subdiretórios por trecho ( chunk_1 , chunk_2 etc.)
  • move os arquivos correspondentes para os subdiretórios

Note que:

  • Se o pedido for um problema, o script precisará de um ajuste menor, mas inclua as regras de pedido na pergunta.
  • O script não "importa" arquivos (-names) com espaços, etc. Mesmo o nome do subdiretório "body" pode incluir espaços.

O script

#!/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)

Como usar

  1. Copie o script em um arquivo vazio, salve-o como reorganize.py
  2. se desejar, você pode alterar o nome do subdiretório "body" (a seção do nome sem o número) no cabeçalho do script, em:

    namebody = "chunk_"
    
  3. Execute-o com o diretório principal e o tamanho do bloco como argumentos:

    python3 /path/to/reorganize.py <main_directory> <chunk_size>
    
por Jacob Vlijm 27.04.2015 / 14:21
0

Baseado no meu script antigo com alguma modificação (apenas alterei o valor N e os nomes das variáveis:):

Agrupando cada N arquivos em diretórios separados

##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 imprime os nomes dos arquivos separados pelo caractere NUL ( sort ). É a maneira mais segura de passar nomes de arquivos como saída para outros comandos.
  2. -z com --version-sort procura entrada delimitada por nul e xargs permite classificar com segurança números de comprimento variável, de forma que filename2.xyz vem antes fileName3.xyz
  3. -n2500 com -0 limita o número de argumentos aplicados a cada comando (neste caso, 2500 argumentos). O echo é para entrada delimitada por nul.

Observação: não se esqueça de que você está executando o script no caso de teste, portanto, limpe o comando ls -v ao lado das linhas relacionadas para executar para executar o script real.

Além disso, existem dois outros scripts que não são tão rápidos como acima:

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
  • O comando ls classifica os arquivos como um tipo natural de números (versão).
    Você pode analisar o comando mkdir -p "subdir$TARGET" se o nome dos arquivos não tiver espaço em branco, nova linha, etc.
  • TARGET cria o diretório com base na variável mv "$file" "subdir$TARGET" .

  • TARGET move o arquivo para o diretório que groupFiles=0 especifica.

  • Redefina [[ ++groupFiles -eq 2500 ]] para zero quando 2500 arquivos forem movidos ( TARGET ) e incremento para /path/to/source/ value.

Observe que altere ls para o diretório de origem atual.

Se você não quer analisar o nome dos arquivos devidos do comando %code% , incluindo espaços, novas linhas e etc, aqui está outra opção para fazer:

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
    
por devWeek 27.04.2015 / 20:16
0

Este script bash moverá qualquer número de arquivos presentes no diretório de destino passado para ele como um argumento, subdividindo-os uniformemente em qualquer número passado para ele como um argumento de subdiretórios de destino denominado subdir<N> , criando os subdiretórios de destino se não está presente já; ele deve ser colocado fora do diretório de destino a ser executado para evitar que ele seja movido durante a execução.

Uso:

./script.sh <path_to_target_directory> <number_of_subdirectories>

* < caminho_para_diretório_diretório > = caminho para o diretório que contém os arquivos para subdividir; < number_of_subdirectories > = número de subdiretórios nos quais subdividir os arquivos

#!/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

Resultados para ./script.sh ~/testdir 4 :

Antes:

~/testdir
├── 1
├── 10
├── 2
├── 3
├── 4
├── 5
├── 6
├── 7
├── 8
└── 9

Depois:

~/testdir
├── subdir1
│   ├── 1
│   ├── 2
│   └── 7
├── subdir2
│   ├── 10
│   ├── 6
│   └── 8
├── subdir3
│   ├── 3
│   └── 9
└── subdir4
    ├── 4
    └── 5
    
por kos 27.04.2015 / 14:26