bashのパス名のパターンマッチング


10

ディレクトリ内のサブディレクトリのリストを操作したい。検討してください:

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

しかし、私は対応して経路の第2の部分に、という値が欲しい 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

また、パスの後半の用語を取り除く方法も知っています。つまり、1レベル下がっています。

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を使用している場合は、split on /を使用して各パスをリストに分割し、2番目の要素を選択しますが、bashでそれを行う方法がわかりません。

編集:答えてくれてありがとう。私も尋ねるべきだったのですが、これをポータブルに行うことは可能ですか?そうであれば、どのようにですか?

回答:


9

それらをbash(またはPOSIXly)と組み合わせることはできません。2つのステップで行う必要があります。

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

これは、どのPOSIXシェルにも移植可能です。Bourneシェルへの移植性が必要な場合(しかし、なぜ/ bashにタグを付けるのですか?)、Solarisへの移植/bin/shで、標準の代わりに使用することを余儀なくされた場合sh、または20年前のシステムへの移植が必要になった場合) 、このアプローチを使用できます(POSIXシェルでも同様に機能します)。

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

(上記は、変数を引用符で囲まないままにすることが理にかなっている非常にまれなケースの1つです)。

参考までに、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#*/}; i=${i%%/*}はオプションですよね?
Dimitre Radoulov 2013

3
@DimitreRadoulovこれはオプションですが、古いバージョンのash/のdashような一部のシェルは、割り当てを右から左に実行し(これがBourneシェルの動作でした)、この場合問題を引き起こします。
ステファンChazelas

7

パラメーターの展開では式をネストすることはできないため、割り当てを使用して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]}"

次に、外部コマンドを使用する3番目のオプションがあります。ファイルのリストが短い場合、これは通常、最も効率の悪いオプションですが、ファイルの数が増えるにつれて、最適にスケーリングされます。

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

ただし、すべてのシェルに当てはまるわけではなく、zsh入れ子にすることができます。
ステファンChazelas

3
@StephaneChazelas結構ですが、この質問にはタグが付けられていbashます。
小次郎2013

2
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

2

構成を次のように使用する場合は十分注意してください。

# 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 *。この場合、それはおかしいだけですが、rootであり、サブディレクトリをエコーするのではなく削除したい場合は、最悪の場合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

0

パス名で移植性のあるパターンマッチングを行うには、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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.