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

但我想要与路径的第二部分相对应的值, elfgl等。我知道如何去掉前导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

但是,尝试将这些结合起来是行不通的。 IE

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

给出

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

毫无疑问这是不正确的语法,但我不知道正确的语法。当然可能有更好的方法来做到这一点。如果我使用 Python,我会使用 split on/将每个路径分解为一个列表,然后选择第二个元素,但我不知道如何在 bash 中执行此操作。

编辑:感谢您的回答。我还应该问,是否可以便携地做到这一点,如果可以,如何实现?

答案1

你不能将它们与bash(或 POSIXly)结合起来,你必须分两步完成。

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

它可以移植到任何 POSIX shell。如果您需要 Bourne shell 的可移植性(但为什么要标记您的问题/bash那么?)如果您要移植到 Solaris 并被迫使用那里的/bin/sh标准sh,或者移植到 20 年前的系统),您可以使用此方法(该方法也适用于 POSIX shell):

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

(上面是极少数情况之一,将变量不加引号是有意义的)。

仅供记录,使用 zsh:

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

(这t所有的H的头H文件的开头)。

或者对于你的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

要以可移植方式对路径名进行模式匹配,您可以将IFSshell 变量设置为/,然后使用 shell 参数扩展。

在子 shell 中执行所有这些操作有助于保持父 shell 的环境不变!

# 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

相关内容