Bash에서 경로 이름의 패턴 일치

Bash에서 경로 이름의 패턴 일치

디렉토리 내의 하위 디렉토리 목록을 작업하고 싶습니다. 고려하다:

for x in x86-headers/*/C/populate.sh; do echo $x; done

이것은

x86-headers/elf/C/populate.sh
x86-headers/gl/C/populate.sh
x86-headers/gmp/C/populate.sh
x86-headers/gnome2/C/populate.sh
x86-headers/gtk2/C/populate.sh
x86-headers/jni/C/populate.sh
x86-headers/libc/C/populate.sh

하지만 경로의 두 번째 부분 등에 해당하는 값을 원합니다 elf. gl나는 선행을 제거하는 방법을 알고 있습니다 x86-headers.

for x in x86-headers/*/C/populate.sh; do i=${x##x86-headers/}; echo $i; done

이것은 만든다

elf/C/populate.sh
gl/C/populate.sh
gmp/C/populate.sh
gnome2/C/populate.sh
gtk2/C/populate.sh
jni/C/populate.sh
libc/C/populate.sh

또한 경로에서 후속 용어를 제거하는 방법도 알고 있습니다. 즉, 한 수준을 낮춥니다.

cd x86-headers
for x in */C/populate.sh; do i=${x%%/*}; echo $i; done

주어진

elf
gl
gmp
gnome2
gtk2
jni
libc

그러나 이들을 결합하려고 하면 작동하지 않습니다. 즉

for x in x86-headers/*/C/populate.sh; do i=${${x##x86-headers}%%/*}; echo $i; done

주어진

bash: ${${x##x86-headers}%%/*}: bad substitution

의심할 바 없이 이것은 잘못된 구문이지만 올바른 구문을 모르겠습니다. 물론 이보다 더 좋은 방법이 있을 수도 있습니다. Python을 사용하는 경우 분할을 사용하여 /각 경로를 목록으로 나눈 다음 두 번째 요소를 선택하지만 bash에서 이 작업을 수행하는 방법을 모르겠습니다.

편집: 답변해 주셔서 감사합니다. 또한 이 이식성이 가능한지, 그렇다면 어떻게 할 수 있는지도 물어봐야 합니다.

답변1

(또는 POSIXly) 와 결합할 수 없으며 bash두 단계로 수행해야 합니다.

i=${x#*/}; i=${i%%/*}

모든 POSIX 쉘로 이식 가능합니다. Bourne Shell의 이식성이 필요한 경우(그러나 질문에 태그를 지정하는 이유는 무엇입니까?)/세게 때리다그래서? ) Solaris로 포팅하고 그곳의 /bin/sh표준을 강제로 사용 sh하거나 20년 된 시스템으로 포팅하는 경우) 이 방법을 사용할 수 있습니다(이 방법은 POSIX 셸에서도 작동합니다):

IFS=/; set -f
set x $x
i=$3

(위의 경우는 변수를 따옴표로 묶지 않은 상태로 두는 것이 의미가 있는 드문 경우 중 하나입니다.)

기록을 위해 zsh를 사용합니다.

print -rl -- x86-headers/*/C/populate.sh(:h:h:t)

(이것모두시간머리시간파일의 시작 부분).

또는 귀하의 python방법에 따라:

x=elf/C/populate.sh
i=${${(s:/:)x}[2]}

답변2

매개변수 확장에서는 표현식 중첩이 허용되지 않으므로 두 단계로 수행하고 할당을 수행해야 합니다.

i=${x##x86-headers/}
i=${i%%/*}

여기서 배열을 사용할 수 있는 옵션이 있지만 위의 PE보다 더 번거롭다고 생각합니다.

IFS=/
set -f # Disable globbing in case unquoted $x has globs in it
i=( $x )
set +f
echo "${i[1]}"

그런 다음 외부 명령을 사용하는 세 번째 옵션이 있습니다. 이는 일반적으로 짧은 파일 목록의 경우 효율성이 가장 낮은 옵션이지만 파일 수가 증가함에 따라 가장 잘 확장됩니다.

# For clarity, assume no names have linebreaks in them
find . -name populate.sh | cut -d / -f 3

답변3

regex="x86-headers/([^/]*)/.*"
for f in x86-headers/*/C/populate.sh; do [[ $f =~ $regex ]] && echo "${BASH_REMATCH[1]}"; done
elf
gl
gmp
gnome2
gtk2
jni
libc

답변4

이식 가능한 패턴 일치 경로 이름을 위해 IFS쉘 변수를 로 설정한 /다음 쉘 매개변수 확장을 사용할 수 있습니다.

하위 쉘에서 이 모든 작업을 수행하면 상위 쉘의 환경을 그대로 유지하는 데 도움이 됩니다!

# cf. "The real Bourne shell problem",
# http://utcc.utoronto.ca/~cks/space/blog/programming/BourneShellLists

paths='
x86-headers/elf/C/populate.sh
x86-headers/gl/C/populate.sh
x86-headers/gmp/C/populate.sh
x86-headers/gnome2/C/populate.sh
x86-headers/gtk2/C/populate.sh
x86-headers/jni/C/populate.sh
x86-headers/libc/C/populate.sh
'

IFS='
'

# version 1
for x in $paths; do 
   ( IFS='/'; set -- ${x}; echo "$2" ); 
done


# version 2
set -- ${paths}
for x in $@; do 
   ( IFS='/'; set -- ${x}; echo "$2" ); 
done

관련 정보