"find"와 함께 사용될 때 "dirname"의 일부를 올바르게 추출하는 방법은 무엇입니까?

"find"와 함께 사용될 때 "dirname"의 일부를 올바르게 추출하는 방법은 무엇입니까?

이 질문은 내 또 다른 질문에서 비롯되었습니다.여기("쉘에서 상위 디렉토리의 기본 이름을 추출하는 방법”), 이는 “토끼 구멍”을 여는 것처럼 보입니다.유닉스 문자열 작업나를 위한. 그럼 다음은 보충 질문입니다.

dirname결과에서 다양한 부분("레벨")을 추출하는 올바른 방법은 무엇입니까 find?

다음과 같은 계층 구조가 있다고 가정해 보겠습니다.

DE_AT/adventure/motovun/300x250/A2_300x250.zip

다음과 같이 파일을 "찾습니다".

find . -name "*.zip" 

쉘 결과 실행 find:

-exec sh -c '' {} \;

전체 경로의 각 부분을 어떻게 추출합니까? 얻는 방법:

  • DE_AT
  • 모험
  • 모토분
  • 300x250
  • A2_300x250.zip

내가 지금까지 알고 있는 것은 다음과 같습니다.

basename "$1" # gets me: A2_300x250.zip
dirname "$1"  # gets me: ./DE_AT/adventure/motovun/300x250

이 .zip 파일의 이름을 다음으로 바꿔야 하기 때문에 이것을 묻는 것입니다.someString_DE_AT_motovun+A2_300x250.zip.

나는 다음과 같이 끔찍한 Franken 솔루션을 생각해 냈습니다.

find . -name "*.zip" -exec sh -c '
    mv "$0" "myString_$(basename $(dirname $(dirname \
    $(dirname "$0")_...+$(basename "$0")"
' {} \;

이것이 정확할 방법이 없기 때문에 나는 이것을 시도하고 싶지도 않습니다.

답변1

연산자를 사용할 수 있습니다 split+glob.

find . -name '*.zip' -exec sh -c '
   IFS=/ # split on /
   set -f # disable glob
   for file do
     set -- $file # invoke split+glob, store in positional parameters
     # now the path components are in $1, $2...
     mv -i -- "$file" "someString_${2}_${4}+${6}"
   done' sh {} +

$1있을 겁니다 .. $2 DE_AT잠깐만요. 마지막 매개변수를 얻으려면 다음과 같은 것이 필요하기 때문에 까다로워집니다.

eval "last=\${$#}"

zsh예를 들어 적절한 분할 연산자 및 배열을 사용하여 다른 쉘을 사용하는 것이 더 쉬울 수 있습니다 .

find . -name '*.zip' -exec zsh -c '
   for file do
     components=(${(s:/:)file})
     printf "Last component: %s\n" $components[-1]
     mv -i -- "$file" "someString_$components[2]_$components[-3]+$components[-1]"
   done' zsh {} +

이를 통해 zsh다음을 사용할 수도 있습니다.zmv일괄 이름 바꾸기 도구:

autoload zmv # best in ~/.zshrc
zmv -n '([^/]#)/**/(*)/*/(*.zip)' 'someString_${1}_${2}+$3'

이 섹션은 모든 수준(레벨 0 포함)의 하위 디렉터리와 일치 하므로 교체를 위해 **///로 들어가는 캡처 문자열( // , // )과 일치하여 위의 배열 메서드와 유사한 동작을 제공합니다 .(a)/b/c/(d)/e/(f.zip)(a)/(b)/c/(d.zip)adf.zipabd.zip$1$2$3$components

[^/]#중 일부는 /가 아닌 모든 시퀀스와 일치하는 #정규식 연산자와 같습니다 . *glob의 경우, a를 확장할 수 없는 것과 동일하게 작동 *하지만, glob을 확장한 후 결과 파일에서 패턴 일치를 사용하여 교체할 부분을 추출하고 대신 너무 많은 부분과 일치하도록 a를 확장합니다.*/zmv*/(*)([^/]#)

답변2

사용은 엄격한 요구사항에 불과 find합니까 ? exec차라리 결과를 반복하여 find다음과 같은 문자열 조작 친화적인 도구와 결합하고 싶습니다 awk .

for ii in $(find . -name "*.zip")
do
    mv $ii $(echo $ii|awk -F/ '{print "someString_" $2 "_" $4 "+" $6}')
done

mv( echo mv테스트 목적으로 교체되었습니다.)

참고: 옵션을 공백과 탭 대신 구분 기호로 설정하십시오 -F/.awk/

고쳐 쓰다

split+globStéphane의 의견에서 제안한 것처럼 연산자를 조정하는 것이 더 현명하고 강력할 수 있습니다.여기)미리:

IFS=$'\n'
set -f

파일 이름에 공백이 포함된 경우 첫 번째 줄은 필수이고, 파일 이름에 와일드카드가 포함된 경우 두 번째 줄은 필수입니다.

앞으로 "이상한" 행동으로 자신을 미치게 만들고 싶지 않다면 이전 설정으로 다시 전환하는 것을 잊지 마십시오...아직 이러한 설정을 사용자 정의하지 않았다고 가정할 때:

unset IFS
set +f

관련 정보