패턴 및 교체 목록을 사용하여 파일 이름을 반복적으로 바꿉니다.

패턴 및 교체 목록을 사용하여 파일 이름을 반복적으로 바꿉니다.

다음과 같은 파일 구조가 있습니다.

  • 일부 디렉토리
    • 일부파일.txt
    • 여기에 또 다른 파일이 있습니다. .log
    • .mp3 파일도 있습니다.
  • 다른 디렉토리
    • 다른 file.txt와 함께
  • 루트 수준 file.txt
  • 루트 수준의 다른 파일.ext

지금 내가 원하는 것은 패턴/교체 쌍이 포함된 다른 파일을 입력으로 사용하여 해당 파일을 기반으로 해당 파일의 이름을 반복적으로 바꾸는 작은 스크립트를 실행하는 것입니다. 이렇게 하면 모든 "another"(대소문자 구분 안 함)가 "foo"로 대체되거나 모든 "some"이 "bar"로 대체됩니다.

파일을 반복하고 해당 입력 파일을 읽는 여러 가지 방법을 시도했지만 아무것도 작동하지 않았고 결국 실수로 테스트 스크립트를 덮어쓰게 되었습니다. 하지만 ls, while, sed가 많이 mv사용되고 있습니다.

나 스스로 알 수 없는 두 가지는 파일 이름의 공백을 처리하는 방법과 이전 패턴 일치에서 이름이 변경된 파일을 처리하지 않는 방법입니다.

어쩌면 당신이 나에게 올바른 방향을 알려줄 수 있을까요?

답변1

TOP="`pwd -P`" \
find . -type d -exec sh -c '
   for d
   do
      cd "$d" && \
         find . ! -name . -prune -type f -exec sh -c '\''
            while IFS=\; read -r pat repl
            do
               rename "s/$pat/$repl/g" "$@"
               N=$#
               for unmoved
               do
                  if [ -f "$unmoved" ]
                  then
                     set X ${1+"$@"} "$unmoved"
                     shift
                  fi
               done
               shift "$N"
               case $# in 0 ) break ;; esac
            done < patterns.csv
         '\'' x \{\} +
      cd "$TOP"
   done
' x {} +
  • find네트워크 디렉터리를 설정하고 삼키기만 하면 sh됩니다. 이렇게 하면 호출 횟수가 최소화됩니다 sh.
  • find각 디렉터리의 네트워크 파일을 regular깊이 수준 1로 설정하고 sh꿀꺽 꿀꺽 꿀꺽 꿀꺽 마시세요. 이렇게 하면 rename유틸리티 호출 횟수가 최소화됩니다 .
  • while개별 쌍으로 읽고 pattern <-> replacement이를 모든 파일에 적용하도록 루프를 설정합니다 regular.
  • 진행중 rename-영국rename처리 후에도 파일이 여전히 존재하는지 여부를 기록합니다 . 파일이 여전히 존재한다는 것은 어떤 이유로든 이름을 바꿀 수 없으며 다음 반복에서 시도된다는 의미입니다 pat/repl. OTOH, 파일 이름이 성공적으로 변경된 경우 pat/repl명령줄 인수 목록에서 파일을 제거하여 파일에 다음 반복을 적용하지 않습니다.

답변2

rPairs="/tmp/rename_pairs" \
find . -type f -exec sh -c '
   while read -r old new; do
      rename "s/$old/$new/i" "$@"
   done < "$rPairs"
' x {} +

이름 바꾸기 쌍 파일에 ASCII가 아닌 문자가 없고 파일이 검색 경로에서 멀리 떨어져 있다고 가정합니다.

답변3

Rakesh Sharma의 답변에 따라 더 많은 노력을 하고 잠시 잠을 자고 나서 올바른 방향을 찾았습니다.

마침내 나는 다음 스크립트를 생각해 냈습니다.

#!/bin/bash


while IFS=";" read pattern replacement
do
  if [[ ! -z $pattern ]]
  then
    echo "Checking files for pattern '$pattern'."

    find ./files -name "*$pattern*" -type f | while read fpath
    do
      fname=$(basename "$fpath")
      dname=$(dirname "$fpath")

      echo "  Found file '$fname' in directory '$dname'. Renaming to '${fname/$pattern/$replacement}'."
      mv -- "$fpath" "$dname/${fname/$pattern/$replacement}"
    done
  fi
done < patterns.csv

파일을 읽고 pattern.csv채우기 및 변수 행을 반복합니다. 두 번째 단계에서는 현재 패턴과 일치하는 디렉터리의 모든 파일을 찾습니다. 이는 두 번째 패턴이 일치할 때 파일 이름을 다시 바꾸려고 시도하는 것을 방지하는 데 필요합니다. 이 작업은 실패하기 때문입니다. 마지막으로 파일이 포함된 디렉토리의 이름을 바꾸기 위해 쉘 매개변수 대체를 사용하는 대신 파일 자체의 이름만 바꿉니다.$pattern$replacement./files

작동하지 않는 것은 대소 문자를 구분하지 않는 일치 항목을 바꾸는 것입니다. 그러나 나는 그걸로 살아갈 수 있습니다.

답변4

기억해야 할 중요한 점은 디렉토리 트리를 탐색하는 과정이 느리므로 한 번만 수행한다는 것입니다. 우리가 가장 먼저 하고 싶은 일은 find트리의 디렉터리를 살펴보는 것입니다. 각 디렉터리에 대해 regular files그 아래의 모든 디렉터리를 찾습니다 (여기에서는 재귀가 없습니다). 그런 다음 성공 여부를 기록하는 동안 이러한 파일 이름에 이름 바꾸기 변환을 적용합니다. 성공하면 while 루프를 종료하여 다음 patt/repl이 파일에 적용되지 않도록 합니다.

tempd="`mktemp -d`" \
find . -type d -exec sh -c '
   cd "$1" && \
   for f in ./*
   do
      [ -f "$f" ] || continue
      while IFS=\; read -r patt repl
      do
         case $f in
            ./*"$patt"* )
               rename -v "s/$patt/$repl/g" "$f" 2>&1 | tee "$tempd/$f"
               case $(< "$tempf/$f") in "$f renamed "* ) break ;; esac ;;
         esac
      done < /tmp/patterns.csv
   done
' {} {} \;

관련 정보