次の問題があります。文字列から角かっこ(角かっこを含むまたは除く)内のテキストを抽出したいと思います。私の文字列は次のとおりです
STR="[1] [2][345] [678 9] foo bar"
私はもともとbash正規表現とBASH_REMATCHを使用したかったのです。結局、次のコードを使用しました。
regex='\[([^\]]*)\](.*)'
MATCHES=()
STR="[1] [2][345] [678 9] foo bar"
while [[ -n $STR && $STR =~ $regex ]];
do
MATCHES+=("${BASH_REMATCH[1]}")
STR=${BASH_REMATCH[2]}
echo -e "matches: ${BASH_REMATCH[1]} -> ${BASH_REMATCH[2]}"
done
[345]
この方法はうまくいきますが、問題は角かっこ内の1文字だけをキャプチャすることです3
。
なぜこれが起こるのか理解できず、結局grepとPCREを使うようになりました。私の現在のソリューションは
regex="\[[^\]]*?\]"
if [[ $(grep -o '\[.*\]' <<< $STR) ]];
then
MATCHES=$(grep -oP "$regex" <<< $STR)
else
echo "No special flags provided."
exit 0
fi
その後、forループに進みます。
for arg in $MATCHES;
do
echo $arg
done
問題は、私が望むようにフィールドを分離しないことです。正しい区切り文字を見つけるためにhexdumpを使用しました。
hexdump -C <<< $MATCHES
驚いたことに、区切り記号は0a
16進数でLFであることがわかりました。 forループが分割にIFSを使用していることがわかっているため、これは問題ではありません。それからIFS=$'\n'
。 (もう一度)驚くべきことに、0a0a
hexdumpによるとIFSの値は再び。だからそれは動作しません。次にIFSの値をに設定しIFS=''
(これが3番目に驚きました)、値をに設定しました0a
。しかし、それも動作しません。 forループは動作を変更しません。たぶん私のスクリプトはIFSの範囲を正しく設定していませんか?
私の質問は次のとおりです。
1)元のbash専用の正規表現方法が機能しないのはなぜですか?なぜ1文字だけをキャプチャするのですか? regex101 dot comは期待される動作を示していますが、再びbash正規表現パターンを提供しません。
2)IFSセットが期待どおりに機能しないのはなぜですか?空白に設定しても「追加」LFを追加します。
3) IFS が for ループに影響しないのはなぜですか?
4)元の問題を解決するより簡単な方法はありますか(各括弧のペアを繰り返すことができるように、[foo] [bar] [foo bar]
このような文字列から抽出)。[foo] [bar] 1 asdf[foo bar]
ボーナス質問!
B)変数または式を引用符または二重引用符で囲む必要があるときに混乱します。ワイルドカードとパラメータ拡張について少し読みました。どんな提案がありますか?
答え1
を含まない空でない文字列を一致させるには、]
を使用します[^]]+
。
[^\]]*
will match non\
の後に 0 以上が来るものを使用します]
。これが正しい1
文字列と他の文字列が解析できなかった理由です2
。
このIFS
変数は最初のコードでは機能しません。内部変数には[[ ... ]]
二重引用符は必要ありません。
配列の個々の要素を印刷するには、次のようにします。
printf '%s\n' "${MATCHES[@]}"
または
for elem in "${MATCHES[@]}"; do
printf '%s\n' "$elem"
done
単に$MATCHES
配列の最初の要素に展開されます(そして値にトークン化とファイル名のグロービングを適用します)。