Linux에서 파일 이름을 기반으로 많은 수의 파일을 디렉토리로 이동

Linux에서 파일 이름을 기반으로 많은 수의 파일을 디렉토리로 이동

내 Linux 서버의 디렉터리에 다음과 같은 이름 패턴을 가진 파일이 많이 있습니다.

1_file.txt
2_file.txt
3_file.txt
...
1455728_file.txt

처음 100000개의 파일을 이동하는 방법이 있습니까(1_file.txt ~ 100000_file.txt) 디렉토리 입력1_100000, 두 번째 100000개 파일(100001_file.txt ~ 200000_file.txt) 디렉토리 입력100001_200000, 등... ?

답변1

테스트되지 않은

나는 다음과 같은 일을 할 것입니다 :

#!/bin/bash
bottom=0
while [[ $bottom -lt 150000 ]] ; do
    myfirst=$((bottom + 1))
    mylast=$((bottom + 100000))
    bottom=$((bottom + 100000))

    dir="${myfirst}_$mylast"
    [[ -d "$dir" ]] || mkdir "$dir"
    seq $myfirst $mylast | \
        while read p ; do
            q="${p}_file.txt"
            [[ -f "$q" ]] && echo "$q"
        done | \
            xargs --no-run-if-empty  echo mv -t "$dir"

done

실제로 하고 싶으면 삭제하세요 echo.echo mv

답변2

스크립트 파일

#!/bin/bash

step=100000
file_dir=$1

# Counting of files in the directory
shopt -s nullglob
file_list=("${file_dir}"/*)
file_num=${#file_list[@]}

# Every file's common part
suffix='_file.txt'

for((from = 1, to = step; from <= file_num; from += step, to += step)); do
    new_dir="${from}_${to}"
    mkdir "${file_dir}/${new_dir}"

    if ((to > file_num)); then
        to="$file_num"
    fi
    
    # Generating filenames by `seq` command and passing them to `xargs`
    seq -f "${file_dir}/%.f${suffix}" "$from" "$to" | xargs mv -t "${file_dir}/${new_dir}"
done

용법:./script.sh files

시험

다음 명령을 사용하여 파일을 생성했습니다.

printf '%s\0' files/{1..1455728}_file.txt | xargs -0 touch

그런 다음 다음을 수행하십시오.

$ time ./script.sh files

# Time is:
real    10m43,618s
user    0m9,953s
sys 0m19,671s

아주 천천히.

결과

$ ls -1v files
1_100000
100001_200000
200001_300000
300001_400000
400001_500000
500001_600000
600001_700000
700001_800000
800001_900000
900001_1000000
1000001_1100000
1100001_1200000
1200001_1300000
1300001_1400000
1400001_1500000

답변3

셸에서 산술 연산을 수행하는 것이 가능하지만 항상 어색하므로 여기에서 대부분의 작업을 수행하려면 다른 스크립팅 언어를 찾는 것이 좋습니다. 아래에서 사용되었지만 그대로 사용할 awk수도 있습니다 . 아래 예제에서도 쉽게 사용할 perl수 있다고 말하고 싶지만 구문 측면으로 인해 Python 스크립트를 이와 같은 파이프라인에 인라인으로 포함하는 방법이 명확하지 않습니다. (이 작업은 수행할 수 있지만 매우 까다롭습니다.) 실제 이동을 수행하는 것이 아니라 원하는 대상 디렉터리를 생성하는 데 필요한 계산만 수행한다는 점에 유의하십시오. 또는 를 사용하면 파일 시스템 작업도 수행할 수 있습니다.pythonpythonawkperlpython

몇 가지 가정:

  • 전체 원래 이름으로 파일을 이동하려고 합니다. 원래 숫자 접두사를 제거하기 위해 스크립트를 수정하는 것은 어렵지 않습니다(물론 파일이 모두 로 끝나지 않는 것이 더 좋지만 _file.txt).

  • _파일 이름에는 공백이 없고 하나만 있습니다. 그렇지 않은 경우에도 다음과 같은 작업은 계속 작동하지만 awk 스크립트와 후속 쉘 루프에서는 더욱 주의해야 합니다.

따라서 이를 고려하면 다음이 작동합니다.

ls | 
awk -F_ '
{
    n = $1 - 1               # working zero based is easier here
    base = n - (n % 100000)  # round down to the nearest multiple of 100,000
    printf "%d_%d %s_%s\n", base + 1, base + 100000, $1, $2
}' |
while read destdir orig
do
    mkdir -p $destdir 
    mv $orig $destdir
done

무슨 일이죠?

ls | ...

이는 파일 이름만 나열하고 출력이 터미널이 아닌 파이프로 전송되므로 한 줄에 하나의 파일 이름이 나열됩니다. 파일은 ls기본 순서로 정렬되지만 나머지 스크립트는 이에 대해 신경 쓰지 않으며 임의의 파일 이름 목록으로 잘 작동합니다.

... | awk -F_ '
{
    n = $1 - 1               # working zero based is easier here
    base = n - (n % 100000)  # round down to the nearest multiple of 100,000
    printf "%d_%d %s_%s\n", base + 1, base + 100000, $1, $2
} | ...'

복잡하지는 않지만, awk이전에 플레이해본 적이 없다면 이해하기가 조금 어렵습니다. 첫째, 여기서 목표는 한 번에 하나의 파일 이름을 읽은 ls다음 각 파일 이름에 대해 두 개의 필드가 있는 출력 라인을 생성하는 것입니다. 첫 번째 필드에는 원래 파일 이름에 대한 적절한 대상 디렉터리가 있고 두 번째 필드에는 원래 파일 이름 파일 이름이 전달됩니다. 파이프라인의 다음 부분에서 사용할 수 있도록 합니다. 그래서 좀 더 자세히 말하자면,

  • Flag는 각 입력 행을 문자 필드 로 분할하도록 -F_지시합니다 . 이러한 일이 파일 이름에 한 번만 발생한다고 가정하면 awk는 이름의 숫자 부분 도 할당합니다 . 그런 다음 방금 설명한 대로 지원 블록이 적용되고 설정됩니다 .awk__$1$2_$1$2

  • 계산을 통해 base파일이 속한 100,000개의 파일 블록이 결정됩니다. 먼저 파일명의 초기번호에서 빼서 n계산합니다 . 1이 숫자는 0부터 시작하므로 다음 줄에 사용되는 모듈러 연산을 더 쉽게 사용할 수 있습니다. 그런 다음 n가장 가까운 100,000의 배수로 내림합니다 . 이미 100,000의 배수인 경우에는 n영향을 받지 않습니다. ("%"연산자에 익숙하지 않다면 N % M나눌 때 나머지를 계산합니다. 그래서 등)NM5 % 3 == 26 % 3 == 0

  • 마지막으로 printf파이프라인의 다음 단계에 필요한 출력 와이어를 조립합니다. 공백으로 구분된 두 개의 필드를 포함하는 라인을 생성합니다. 첫 번째는 base내보내기 디렉터리 이름의 하한 및 상한을 사용하여 생성된 대상 디렉터리의 이름입니다 . 여기서는 1 기반 출력 계산 체계로 돌아갑니다. 두 번째 필드는 재구성된 원본 입력 파일 이름입니다.

... | while read destdir orig
do
    mkdir -p $destdir && mv $orig $destdir
done

이는 모든 작업이 실제로 완료되는 파이프라인의 마지막 단계입니다. 스크립트에 의해 생성된 각 줄을 awk두 개의 필드로 읽은 다음

  • mkdir -p(디렉토리가 이미 존재하는 경우 아무 작업도 수행하지 않음) 다음을 사용하여 디렉토리가 존재하는지 확인합니다 .
  • 성공하면 원본 파일을 새 디렉터리로 이동합니다.

mkdir ... && mv ...일반적으로 쉘 스크립트에서 이 패턴을 사용하는 것이 좋습니다. mkdir어떤 이유로든 실패하면 이름 바꾸기가 시도되지 않기 때문입니다.

간단하지만 유용한 방식으로 데이터를 점진적으로 변환하는 여러 파이프라인 단계의 패턴은 다양한 셸 스크립트를 작성하는 매우 효과적인 방법입니다. 이는 프로세스 및 파이프라인 제어에서 셸의 장점을 활용하는 동시에 셸이 잘 못하는 더 복잡한 계산을 더 적절한 언어로 푸시할 수 있도록 해줍니다.

답변4

에서 적응내 대답당신을 위한관련 질문:

#! /bin/zsh -

zmodload zsh/files # makes mv and a few other file manipulation commands builtin
batch=10000

highest=(<1->_file.txt(n[-1]))
highest=${highest%%_*}

for ((start = 1; start <= highest; start += batch)); do
  (( end = start + batch - 1))
  files=(<$start-$end>_file.txt(N))
  if (($#files)); then
    mkdir -p ${start}_${end} || exit
    mv -- $files ${start}_${end}/ || exit
  fi
done

관련 정보