다음과 같은 파일 구조가 있습니다.
- 일부 디렉토리
- 일부 file.txt
- 여기에 또 다른 파일이 있습니다.log
- 또 다른 파일.mp3
- 다른 디렉토리
- 다른 file.txt로
- 루트 level.txt의 파일
- 루트 level.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
-ing프로세스 후에도 파일이 아직 남아 있는지 여부를 기록해 둡니다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
' {} {} \;