일치하는 파일 수가 10보다 큰 경우 특정 이름과 일치하는 모든 파일을 새 폴더로 이동하는 방법은 무엇입니까?

일치하는 파일 수가 10보다 큰 경우 특정 이름과 일치하는 모든 파일을 새 폴더로 이동하는 방법은 무엇입니까?

실행 시 디렉터리를 조사하고 모든 파일을 검색한 다음 자동으로 파일 이름 패턴을 검색하고 아래에 설명된 추가 논리에 따라 이동하는 스크립트를 만들고 있습니다.

내 폴더에 다음 파일이 있다고 가정해 보겠습니다.

  • AAA.txt
  • 임시-203981.log
  • 임시-098723.log
  • 임시-123197.log
  • 임시-734692.log
  • test1.sh
  • test2.sh
  • test3.sh

스크립트는 자동으로 디렉터리를 검색하고 이름에 일치하는 접두사가 있는 4개의 파일(temp-XXX.log)과 3개의 파일(testXXX.sh)을 찾아야 합니다. 그런 다음 파일 수를 찾으면 이를 정의된 제한(예: 3)과 비교해야 합니다.

지정된 이름과 일치하는 파일 수가 제한보다 큰 경우 발견된 파일은 일치하는 파일 이름 부분으로 명명된 폴더로 이동되어야 합니다.

따라서 위의 상위 폴더는 이제 다음과 같아야 합니다.

  • AAA.txt
  • temp.log(temp-734692.log, temp-123197.log, temp-098723.log, temp-203981.log가 포함된 폴더임)
  • test.sh(test1.sh, test2.sh, test3.sh가 포함된 폴더가 됩니다)

이것이 의미가 있기를 바랍니다.

PS 저는 이 스크립트에서 ASH를 사용하고 있으므로 멋진 bash 기능 없이도 실행할 수 있어야 합니다. 그렇지 않으면 이것이 더 쉬울 것입니다.

감사해요!

편집: 시작하자마자 명확성이 변경되었습니다. 또한 모든 파일 이름에 대해 미리 정의된 구분 기호(예: "&")를 제공하면 더 쉬울 수 있습니다. 스크립트에서는 여전히 구분 기호 앞의 파일 이름을 기반으로 가변 폴더 이름을 생성해야 하지만 이렇게 하면 작업이 더 명확하고 쉬워질 것이라고 생각합니다.

답변1

작동하는지 확인하고 작동 방식에 대한 설명을 추가하겠습니다. 에서 테스트했습니다 dash.

노트:파일 이름에는 공백이나 줄바꿈이 포함되어서는 안 됩니다.

#!/bin/dash

limit=1

printf "%s\n" * |
sed 's/[-0-9]*\..*$//' |
uniq -c |
awk -v lim=${limit} '$1 >= lim {print $2}' |
sort -r |
while read -r i; do
    for j in "${i}"*; do
        [ -f "$j" ] || continue

        dir=${i}.${j#*.}

        [ -d "$dir" ] || mkdir "$dir"
        mv -v "$j" "$dir"
    done
done

여기에는 문제가 있습니다. 예를 들어 파일 이름이 향후 디렉토리 이름과 같은 경우입니다 aaa.txt. 이 aaa.txt경우 파일 이름에 추가 문자가 없으므로 아무것도 제거되지 않으므로 새 디렉터리 이름이 동일하므로 오류가 발생합니다.

mkdir: cannot create directory ‘aaa.txt’: File exists
mv: 'aaa.txt' and 'aaa.txt' are the same file

이 문제에 대한 한 가지 해결 방법은 가정된 디렉터리 이름이 파일 이름과 동일한지 확인한 다음 향후 디렉터리 이름에 몇 가지 숫자를 추가하는 aaa1.txt것입니다 .

데모

스크립트가 실행되기 전에.

$ tree
.
├── aaa.txt
├── temp-098723.log
├── temp-123197.log
├── temp-203981.log
├── temp-734692.log
├── temp-new-file123.log
├── temp-new-file-2323-12.log
├── temp-new-file-342.log
├── test1.sh
├── test2.sh
└── test3.sh

0 directories, 11 files

스크립트가 실행된 후: script.sh

$ tree
.
├── aaa.txt
├── temp.log
│   ├── temp-098723.log
│   ├── temp-123197.log
│   ├── temp-203981.log
│   └── temp-734692.log
├── temp-new-file.log
│   ├── temp-new-file123.log
│   ├── temp-new-file-2323-12.log
│   └── temp-new-file-342.log
└── test.sh
    ├── test1.sh
    ├── test2.sh
    └── test3.sh

3 directories, 11 files

답변2

여기서 질문하신 내용을 오해했을 수도 있지만 말씀드린 대로 문제에는 상대적으로 복잡한 솔루션이 필요한 몇 가지 미묘함이 있다고 생각합니다. 즉, 원하는 작업을 수행하기 위해 스크립트가 얼마나 간단한지 모르겠습니다. . 예를 들어 샘플 파일 목록을 자세히 살펴보겠습니다.

AAA.txt
임시-203981.log
임시-098723.log
임시-123197.log
임시-734692.log
test1.sh
test2.sh
test3.sh

귀하의 질문에 따르면 이 목록에서 추출하려는 접두사는 temp및 입니다 test. 이는 접두사 aaa로 파일이 하나만 있고 예제 임계값이 3이기 때문에 제외됩니다 . 그런데 로 시작하는 파일이 7개 있는데 aaa왜 접두사가 없는 걸까요 ? 또는 먼저 파일 이름 접미사를 기준으로 파일을 그룹화하려는 것 같으니 왜 새 하위 디렉터리 중 하나가 아니면 입니까 ? 이 토론을 통해 프로그램이 접두사 목록을 인수로 사용하지 않고 자체적으로 잠재적인 접두사를 결정하기를 원하는 경우 문제 설명에 해결해야 할 몇 가지 모호함이 있음을 분명히 알 수 있기를 바랍니다. 만들어야 하는 것입니다).tetet.logtemp-.logtemp.log

이것은 간단한 것을 사용하는 Python 스크립트입니다.테리나무일부 제약 조건을 충족하는 가장 긴 일치 접두사를 검색하는 데 사용되는 데이터 구조(인수로 제공될 수 있음):

#!/usr/bin/env python2
# -*- coding: ascii -*-
"""
trieganize.py

Use the trie data structure to look for prefixes of filenames in a given
directory and then reorganiz those files into subdirectories based on
those prefixes.

In this script the trie data structure is just a dictionary of the
following form:

    trie = {
        "count":    integer,
        "children": dictionary,
        "leaf":     boolean
    }

Where the dictionary keys have the following semantics.

count:
    stores the number of total descendents of the given trie node

children:
    stores the child trie nodes of the given node

leaf:
    denotes whether this trie corresponds to the final character in a word
"""

import sys
import os
import string

def add_word_to_trie(trie, word):
    """Add a new word to the trie."""
    if word:
        trie["count"] += 1
        if word[0] not in trie["children"]:
            trie["children"][word[0]] = \
                {"count": 0, "children": {}, "leaf": False}
        add_word_to_trie(trie=trie["children"][word[0]], word=word[1:])
    else:
        trie["leaf"] = True
    return(trie)

def expand_trie(trie, prefix='', words=None):
    """Given a trie, return the list of words it encodes."""
    if words is None:
        words = list()
    if trie["leaf"]:
        words.append(prefix)
    for character, child in trie["children"].iteritems():
        if trie["children"]:
            expand_trie(trie=child, prefix=prefix+character, words=words)
    return(words)

def extract_groups_from_trie(
    trie, threshold=0, prefix='', groups=None,
    minimum_prefix_length=0,
    maximum_prefix_length=float("inf"),
    prefix_charset=string.ascii_letters,
):
    """Given a trie and some prefix constraints, return a dictionary which
    groups together the words in the trie based on shared prefixes which
    satisfy the specified constraints.
    """
    if groups is None:
        groups = dict()
    if trie["count"] >= threshold:
        children = {
            character: child
            for character, child in trie["children"].iteritems()
            if (
                child["count"] >= threshold and
                len(prefix) + 1 >= minimum_prefix_length and
                len(prefix) + 1 <= maximum_prefix_length and
                character in prefix_charset
            )
        }
        if not children:
            groups[prefix] = expand_trie(trie, prefix)
        else:
            for character, child in children.iteritems():
                extract_groups_from_trie(
                    trie=child, threshold=threshold,
                    prefix=prefix+character, groups=groups
                )
    return(groups)

def reorganize_files(basedir, suffix_separator='.', threshold=3):
    """Takes a path to a directory and reorganizes the files in that
    directory into subdirectories based on the prefixes of their
    filenames."""

    # Get the list of file names
    filenames = os.listdir(basedir)

    # Group the filenames by suffix
    suffixes = {}
    for filename in filenames:
        basename, separator, suffix = filename.rpartition(suffix_separator)
        if suffix not in suffixes:
            suffixes[suffix] = []
        suffixes[suffix].append(basename)

    # For each suffix, search for prefixes
    for suffix, basenames in suffixes.iteritems():

        # Initialize a trie object
        trie = {"count":0, "children": {}, "leaf": False}

        # Add the filenames to the trie
        for basename in basenames:
            add_word_to_trie(trie, basename)

        # Break the filenames up into groups based on their prefixes
        groups = extract_groups_from_trie(trie, threshold)

        # Organize the groups of files into subdirectories
        for prefix, group in groups.iteritems():
            targetdir = os.path.join(basedir, prefix + suffix_separator + suffix)
            os.mkdir(targetdir)
            for basename in group:
                filename = basename + suffix_separator + suffix
                sourcefile = os.path.join(basedir, filename) 
                targetfile = os.path.join(targetdir, filename)
                os.rename(sourcefile, targetfile)

if __name__=="__main__":
    reorganize_files(basedir=sys.argv[1])

이 Python 스크립트를 시연하기 위해 테스트 디렉터리를 만들고 채우는 작은 셸 스크립트를 작성했습니다.

#!/usr/bin/bash

# create-test-dir.sh

rm -rf /tmp/testdir
mkdir -p /tmp/testdir

files=(
aaa.txt
temp-203981.log
temp-098723.log
temp-123197.log
temp-734692.log
test1.sh
test2.sh
test3.sh
)

for file in ${files[@]}; do touch "/tmp/testdir/${file}"; done

스크립트를 실행할 수 있습니다:

bash create-test-dir.sh

이후 테스트 디렉터리는 다음과 같습니다(실행 중 tree /tmp/testdir).

/tmp/테스트 디렉토리/
|-- aaa.txt
|-- 임시-098723.log
|-- 임시-123197.log
|-- 임시-203981.log
|-- 임시-734692.log
|-- test1.sh
|-- test2.sh
`--test3.sh

디렉터리 0개, 파일 8개

이제 Python 스크립트를 실행할 수 있습니다.

python trieganize.py /tmp/testdir

그런 다음 파일은 다음과 같이 구성됩니다.

/tmp/테스트 디렉토리/
|-- aaa.txt
|-- 온도 기록
|-- 임시-098723.log
|-- 임시-123197.log
|-- 임시-203981.log
`--temp-734692.log
`--test.sh
    |-- test1.sh
    |-- test2.sh
    `--test3.sh

2개의 디렉토리, 8개의 파일

답변3

예, bash이렇게 하면 작업이 더 쉬워지지만 POSIX 솔루션은 다음과 같습니다.

#!/bin/sh

for pattern in "$@"; do
   set -- "$pattern"*

   if [ $# -gt 2 ]; then

      for f in "$@"; do
         [ -f "$f" ] || continue
         ext="${f##*.}"
         dest="${pattern}.${ext}"
         [ -d "$dest" ] || mkdir "$dest"
         mv "$f" "$dest"
      done

   fi
done

exit

이를 위해서는 다양한 패턴이 필요합니다(예: ) ./script temp test. 각 패턴에 대해 위치 매개변수를 패턴과 일치하는 파일로 설정하고 지정된 폴더로 이동합니다( <pattern>.<file_extension>패턴과 일치하는 파일이 3개 이상인 경우). 귀하의 샘플 파일을 사용하여 예상한 결과를 얻었습니다.

편집: $f디렉토리 이동 등을 피하기 위해 이것을 일반 파일로 테스트했습니다.

관련 정보