다음과 같은 파일 구조가 있습니다.
- 일부 디렉토리
- 일부파일.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
' {} {} \;