[unix] 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

의심 할 여지없이 이것이 잘못된 구문이지만 올바른 구문을 모르겠습니다. 물론 더 좋은 방법이있을 수 있습니다. 파이썬을 사용하고 있다면 split on /을 사용 하여 각 경로를 목록으로 나누고 두 번째 요소를 선택하지만 bash에서 어떻게 해야하는지 모르겠습니다.

편집 : 답변 주셔서 감사합니다. 나도 물었을 것입니다.이를 이식 가능하게 할 수 있습니까? 그렇다면 어떻게합니까?



답변

그것들을 bash(또는 POSIXly) 와 결합 할 수 없으므로 두 단계로 수행해야합니다.

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

POSIX 쉘에 이식 가능합니다. Bourne 쉘로 이식성이 필요한 경우 (그러나 왜 / bash에 태그를 추가해야 합니까?) 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)

합니다 ( t에서 의 AIL 시간 의 EAD 시간은 파일의 EAD).

또는 당신의 python접근 방식 :

x=elf/C/populate.sh
i=${${(s:/:)x}[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


답변

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


답변

다음과 같이 구문을 사용하여 매우주의하십시오.

# What happen if no files match ?
for x in x86-headers/*/C/populate.sh; do
  x=${x##x86-headers/}
  x=${x%%/*}
  echo $x
done

당신이 결국 할 수있는 것처럼 echo *. 이 경우에, 그것은 재미 있지만, 당신이 루트라면, 당신 CWD은입니다. /그리고 당신은 /tmp그것들을 에코하는 대신에 몇몇 서브 디렉토리를 삭제하고 싶을 때 훨씬 최악 일 수 있습니다!

bash에서 수정하려면 다음을 수행하십시오.

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

주목 shopt -u nullglob루프 후 기본 bash는 동작을 복원하는 말.

보다 휴대용 솔루션은 다음과 같습니다.

for x in x86-headers/*/C/populate.sh; do
  [ -e $x ] || break
  x=${x##x86-headers/}
  x=${x%%/*}
  echo $x
done

( ##%%매개 변수 대체는 ksh88 에서 유효합니다 ).

nullglob 에 대한 자세한 내용은 이 링크를 참조 하십시오 .

이름에 공백이있는 중간 디렉토리가 있으면 위의 3 가지 솔루션이 실패합니다. 이 문제를 해결하려면 변수 대체에 큰 따옴표를 사용하십시오.

for x in x86-headers/*/C/populate.sh; do
  [ -e "$x" ] || break
  x="${x##x86-headers/}"
  x="${x%%/*}"
  echo "$x"
done


답변

경로 이름에서 패턴 일치를 수행하기 위해 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


답변