使用 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
我仍然得到完全相同的输出。但现在我真的很困惑。不可能有换行符偷偷溜进来...
帮忙??
奖金可能的问题:
我使用的是 macOS 附带的 sed 版本。天知道是什么版本。也许我应该尝试使用 GNU sed?
我真的不知道 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 -- 它会指出很多常见的脚本错误(例如没有使用双引号)。
所以我尝试使用 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
我仍然得到完全相同的输出。但现在我真的很困惑。不可能有换行符偷偷溜进来...
帮忙??
奖金可能的问题:
我使用的是 macOS 附带的 sed 版本。天知道是什么版本。也许我应该尝试使用 GNU sed?
我真的不知道 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 -- 它会指出很多常见的脚本错误(例如没有使用双引号)。