使用 sed 替换 bash 脚本中的日期:幻像换行符错误

Replacing dates in bash script using sed: phantom newline error

所以我尝试使用 bash 中的 sed 对文档元数据中的日期进行简单的正则表达式替换。例如,假设我有输入文件 test.md 包含:

---
title: "I am a file"
date: December 1, 2021
---

Loren ipsum blah blah blah

我希望能够在 12 月 29 日 运行 一个 bash 脚本并获得一个输出文件

---
title: "I am a file"
date: December 29, 2021
---

Loren ipsum blah blah blah

这是我的第一次尝试:

#!/bin/bash

TODAY=$(date +'%B %d, %Y')
STARTBIT="date: "

FULLDATE="$STARTBIT$TODAY"

REGEX="s/date:\s.*\n/$FULLDATE/"

echo $REGEX # to make sure I'm getting what I think I'm getting

sed  -e $REGEX < test.md > output.md

但我得到以下输出:

s/date:\s.*\n/date: December 29, 2021/
sed: 1: "s/date:\s.*\n/date:
": unescaped newline inside substitute pattern

所以这有点令人困惑,第一行是我的回显模式,我在命令行中绝对看不到任何换行符。我也不太确定换行符应该在哪里??

然后我想,好吧,也许换行符附加到其中一个变量的末尾,并且由于某些 bash 愚蠢的原因,当我回显它时,换行符变得不可见。因此,基于 this prior SO answer,我只是进入并从所有内容的末尾删除换行符以确保。即:

#!/bin/bash

TODAY=$(date +'%B %d, %Y')
STARTBIT="date: "
CLEANSTARTBIT=${STARTBIT%%[[:space:]]}
CLEANTODAY=${TODAY%%[[:space:]]}

FULLDATE="$STARTBIT$TODAY"
CLEANFULLDATE=${FULLDATE%%[[:space:]]}

REGEX="s/date:\s.*\n/$CLEANFULLDATE/"
CLEANREGEX=${REGEX%%[[:space:]]}

echo $CLEANREGEX

sed  -e $CLEANREGEX < test.md > output.md

仍然得到完全相同的输出。但现在我真的很困惑。不可能有换行符偷偷溜进来...

帮忙??

奖金可能的问题:

  1. 我使用的是 macOS 附带的 sed 版本。天知道是什么版本。也许我应该尝试使用 GNU sed?

  2. 我真的不知道 sed 使用什么风格的正则表达式,或者 sed 是如何工作的......我基本上只是从我在 python 脚本,用于学习 purposes/because 我厌倦了调用 python 来进行我一直在做的这一点基本文本处理。哈,但我实际上知道 python 正则表达式...

使用 TODAY 变量

$ TODAY=$(date +'%B %d, %Y')

然后您可以使用 sed 将日期部分替换为当前日期

$ sed "s/\(date:\).*/ $TODAY/" test.md
---
title: I am a file
date: December 29, 2021
---

Loren ipsum blah blah blah

第一个问题:您需要双引号引用您的变量(例如 echo "$REGEX" 而不是 echo $REGEX)。如果没有双引号,变量的值将被拆分为“单词”,任何看起来像文件名通配符的单词都将扩展为匹配文件列表。你几乎不希望这些事情发生,所以你应该几乎总是用双引号引用变量。特别是这个命令:

sed  -e $REGEX < test.md > output.md

扩展为类似:

sed -e s/date:\s.*\n/date: December 29, 2021/

...和“s/date:\s.*\n/date:”、“December”、“29,”和“2021/”都被视为[的完全独立的参数=19=]。错误信息具有误导性;真正的错误是第一个是不完整的 sed 命令。

(如果您碰巧有任何文件匹配 s/date:\s.*\n/date——不太可能,但技术上可行——事情会变得更加愚蠢。)

第二个问题是,正如您所猜测的,您的正则表达式使用了错误的语法方言。 macOS 自带的版本不支持 \s shorthand,所以用 [[:space:]] 代替。此外,使用 \n 匹配行尾在 sed 的任何风格中都是无效的;使用 $ 代替(但你需要转义它,因为它在双引号中并且你不希望它启动一些扩展规则):

REGEX="s/date:[[:space:]].*$/$FULLDATE/"

从技术上讲,您也不需要 $。正则表达式匹配是贪婪的,所以如果它 可以 匹配到行尾 - 它可以 - 它 匹配到行尾这条线。

但是在模式的开头添加 ^ 是个好主意,将其固定在一行的开头。否则,它将匹配一行中的任何位置的“date:”。

第三,我建议改用小写或混合大小写的变量名。有一堆具有特殊含义的全大写名称,如果你不小心使用其中一个,它会产生奇怪的效果。

最后说明:使用 shellcheck.net -- 它会指出很多常见的脚本错误(例如没有使用双引号)。