find 및 awk를 사용하여 tcsh 쉘 스크립트에서 파일 이름의 느낌표를 참조하는 방법은 무엇입니까?

find 및 awk를 사용하여 tcsh 쉘 스크립트에서 파일 이름의 느낌표를 참조하는 방법은 무엇입니까?

파일 이름에서 공백 문자 " ", 느낌표 " !" 및 달러 기호 " $"를 검색하고 각 문자를 밑줄 " _"로 바꾸는 스크립트가 있습니다. 그러나 느낌표가 있는 파일 이름은 전혀 처리하지 않습니다. 현재 폴더와 모든 하위 폴더에서 이름을 바꾸려는 파일을 특정 깊이까지 검색합니다(여기서는 별로 관련이 없음). 폴더 이름도 같은 방식으로 변경해야 하며, 이것이 바로 딥 인터리브가 작동하는 이유입니다.

필수 이름 바꾸기 구성표는 다음과 같습니다.

This is cool!.txt     -->    This_is_cool_.txt
Thank$.log            -->    Thank_.log
Foo 1/Bar 2/a.txt     -->    Foo_1/Bar_2/a.txt

스크립트는 다음과 같습니다.

#!/bin/tcsh -f
  
foreach n ( 1 2 3 4 5 6 7 8 )
    find . -mindepth $n -maxdepth $n -name '*[ $\!]*' | fgrep -v \" | \
    awk '{printf "mv -i -- \"%s\" \"%s\"\n", gensub("!","\\\\\!","g",$0), gensub(" ","_","g",gensub("\\$","\\\\$","g",gensub("!","\\\\\!","g",$0)))}' | tcsh -cf
end 

\두 개의 느낌표 앞의 백슬래시 '' 수를 변경했지만 효과가 없습니다. 오류 메시지는 다음과 같습니다(백슬래시 개수가 짝수).

awk: cmd. line:1: warning: escape sequence `\!' treated as plain `!'

아니면 (분명히) 대화형 셸을 여는 셸 스크립트인가요? ! 때로는 아무 일도 일어나지 않습니다( mv첫 번째 인수에 백슬래시가 너무 많아 파일을 찾을 수 없는 경우인 것 같습니다 ).

추신: 보시다시피, "큰따옴표 " "가 있는 파일도 작동시킬 수 없기 때문에 제외했습니다. 이러한 문제를 해결하는 방법도 제안해 주시면 더욱 좋습니다!

답변1

mv명령을 합성하여 awk셸에 파이프하는 것보다 직접 호출하는 것이 더 쉽습니다.

#!/bin/bash
#
find -mindepth 1 -maxdepth 8 -type f -name '*[ !$]*' -execdir
    bash -c 'for f in "$@"; do echo mv -- "$f" "${f//[ !$]/_}"; done' _ {} +

이 버전은 파일 이름을 바꾸지만 디렉터리 이름은 바꾸지 않습니다(질문에 언급된 대로). 실제로 디렉터리 이름도 바꾸려면 이 변형을 사용하세요.

find -mindepth 1 -maxdepth 8 -depth -name '*[ !$]*' -execdir
    bash -c 'for f in "$@"; do echo mv -- "$f" "${f//[ !$]/_}"; done' _ {} +

두 경우 모두 echo명령이 기대한 대로 수행된다는 점에 만족하면 명령을 제거하십시오. 대화형 이름 바꾸기 에 사용됩니다 mv -i. 밑줄은 bash -c '...' _ {}서브쉘에서 실행되는 코드의 "이름"이 됩니다. 이는 매핑되어 $0코드 오류(있는 경우)를 보고하는 데 사용되므로 bash거기에 넣을 수 있습니다. 심지어 subshell단지 레이블일 뿐입니다.

답변2

이 스크립트는 깊이를 제한하지 않고 find모든 문자 $!"'_.

질문에는 특정 버전의 운영 체제나 도구에 대한 정보가 포함되어 있지 않기 때문에 POSIX 사양에만 의존하고 GNU와 같은 특정 버전은 요구하지 않으려고 노력했습니다.

#!/bin/sh
find . -depth -name '*[ $!"]*' | while IFS= read -r file
do
   dir="${file%/*}"
   newname="$(echo "${file##*/}" | tr ' $!"' '____')"
   echo mv "$file" "$dir/$newname"
done

이 스크립트는 가볍게 테스트되었습니다. 올바른 mv명령이 인쇄되면 제거하십시오 echo.

설명하다:

find . -depth...파일이 디렉토리 앞에 인쇄되는지 확인하십시오(둘 다 대체할 문자를 포함하는 경우). 이런 식으로 먼저 파일 이름을 바꾼 다음 상위 디렉터리의 이름을 바꿉니다.

"`로 구분된 상위 디렉터리 이름은 dir="${file%/*}변경되지 않습니다.

파일 이름(또는 마지막 디렉터리 이름)이 추출되어 newname="$(echo "${file##*/}" | tr ' $!"' '____')".


추가 개선

파일 이름의 다른 "이상한" 문자를 처리하려면 아래와 find같이 작업에서 직접 루프 본문의 코드를 호출하는 것이 좋습니다.-exec로에마~의답변.

find . -depth -name '*[ $!"]*' -exec sh -c 'for file in "$@" ; do dir="${file%/*}" ; newname="$(echo "${file##*/}" | tr '\'' $!"'\'' "____")" ; echo mv "$file" "$dir/$newname"; done' _ {} +

당신은 또한 볼 수 있습니다https://mywiki.wooledge.org/BashFAQ/020

다른 답변에서 as를 사용하면 -execdir파일 이름과 상위 디렉터리가 분리되는 것을 방지할 수 있지만 POSIX는 이 작업을 정의하지 않습니다.

echo... 조합 과 같은 쉘로 교체할 수 있습니다 .|tr"${newname//[ $!\"]/_}"

관련 정보