이 질문은 내 또 다른 질문에서 비롯되었습니다.여기("쉘에서 상위 디렉토리의 기본 이름을 추출하는 방법”), 이는 “토끼 구멍”을 여는 것처럼 보입니다.유닉스 문자열 작업나를 위한. 그럼 다음은 보충 질문입니다.
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)
a
d
f.zip
a
b
d.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+glob
Stéphane의 의견에서 제안한 것처럼 연산자를 조정하는 것이 더 현명하고 강력할 수 있습니다.여기)미리:
IFS=$'\n'
set -f
파일 이름에 공백이 포함된 경우 첫 번째 줄은 필수이고, 파일 이름에 와일드카드가 포함된 경우 두 번째 줄은 필수입니다.
앞으로 "이상한" 행동으로 자신을 미치게 만들고 싶지 않다면 이전 설정으로 다시 전환하는 것을 잊지 마십시오...아직 이러한 설정을 사용자 정의하지 않았다고 가정할 때:
unset IFS
set +f