BASHは私の正規表現を好まない

BASHは私の正規表現を好まない

ファイルが変更された 2 桁の月と 2 桁の年を取得しようとしていますが、うまくいきません。

modified=$(stat -c %y "$line"); 
# modified="2018-08-22 14:39:36.400469308 -0400"
if [[ $modified =~ ".{2}(\d{2})-(\d{2})" ]]; then
    echo ${BASH_REMATCH[0]}
    echo ${BASH_REMATCH[1]
fi

何が間違っているのでしょうか?

答え1

まず、引用符は正規表現内の特殊文字の意味を抑制します(オンラインマニュアル):

追加の二項演算子=~が使用可能です。... パターンの任意の部分を引用符で囲むと、引用符で囲んだ部分が文字列として一致するように強制できます。... 正規表現の文法にとって特別な文字を一致させたい場合は、その特別な意味を削除するために引用符で囲む必要があります。

マニュアルではさらに、シェルの解析と正規表現の構文の衝突を防ぐために、正規表現を変数に入れることを推奨しています。

2 番目は、\dあなたが考えていることではなく、単にリテラル に一致するだけですd

また、には${BASH_REMATCH[0]}一致する文字列全体が含まれ、インデックス1以降にはキャプチャされたグループが含まれることに注意してください。

また、4桁の年を使用することを強くお勧めします。

modified=$(stat -c %y "$file")
re='^([0-9]{4})-([0-9]{2})'
if [[ $modified =~ $re ]]; then
    echo "year:  ${BASH_REMATCH[1]}"
    echo "month: ${BASH_REMATCH[2]}"
else
    echo "invalid timestamp"
fi

今日変更されたファイルの場合、 およびyear: 2018となりますmonth: 08。先頭にゼロがある数値は、シェルやその他のユーティリティによって 8 進数として扱われることに注意してください。

(1900 年代の日付を扱う必要がある場合、4 桁の年であれば問題が少なく、月日ではなく年として認識しやすくなります。)

答え2

これには正規表現は必要ありません:

$ touch -t 197001010000 myfile
$ ls -l myfile
-rw-rw-r-- 1 jackman jackman 0 Jan  1  1970 myfile
$ IFS='-' read -r year month _rest < <(stat -c %y myfile)
$ echo "$year:${year#??}"$month"
1970:70:01

答え3

代わりに、GNU を使用するとdate、次の操作を実行できます。

eval "$(date -r "$file" +'year=%Y month=%-m day=%-d')"

変更時刻の年、月、日の要素をそれぞれ$year$monthおよびに格納します$day(10 進整数として、先頭のゼロが必要な場合は、およびの を削除します。2 桁の年については、も参照してください-) 。%-m%-d%y

(GNU とは異なりstat、シンボリックリンク タイプのファイルの場合、シンボリックリンク自体の変更時刻ではなく、シンボリックリンクのターゲットの変更時刻が考慮されることに注意してください。GNU ではstat、 を使用しますstat -L)。

関連情報