我觉得在这里问这个问题就像一袋锤子一样愚蠢,但这已经是漫长的一天了,我根本不知道我在这里做错了什么。
我有一个文件;我们就这样称呼它textfile.txt
。其内容(在这个具有代表性但人为的示例中)如下:
文件内容
SOME=1
TRIED="THEIR BEST AT"
INSERTING='A VALUE'
BE=4
现在,假设我有两个变量$KEY
和$VAL
,代表,哦,我们会说我想要在文件中更新的键和值(旁白:我可以保证两者都不会包含引号 ["] 或撇号 [']):
新价值观
KEY="TRIED"
VAL="THEIR HAND AT"
好的,太好了。所以现在我触发了看起来应该是一个沼泽标准正则表达式替换(请注意,我试图将替换的值包含在任何内容中)选修的目前它已包围它的分隔符):
sed -E "s/$KEY=([\"']?).*([\"']?)/$KEY=\1$VAL\2/g" textfile.txt > textfile.txt
预期结果
SOME=1
TRIED="THEIR HAND AT"
INSERTING='A VALUE'
BE=4
实际结果
(一个空文件)
好吧,好吧,那么我的目标是一个新文件而不是我正在读取的文件怎么样?
sed -E "s/$KEY=([\"']?).*([\"']?)/$KEY=\1$VAL\2/g" textfile.txt > textfile2.txt
新结果
SOME=1
TRIED="THEIR HAND AT
INSERTING='A VALUE'
BE=4
首都!
AT
...除了现在值周围缺少第二个分隔符(双引号尾随)。我可以重复使用\1
,这很有效,但我觉得这很脆弱,不知道我在哪里丢球。
所以...问题:
- 为什么我的第一次尝试会彻底清除文件?
- 为什么我的第二个省略了后一个分隔符?
请注意,我完全不接受这种方法,并且可以选择另一条路线,但如果有人也可以解释这两点,我将非常感激。如果你昨天问我的话,我会发誓我知道 RegEx,但它一直是一个分钟因为我在 Shell 中使用过它。
我正在 Ventura 13.0 上的 2021 MacBook M Chip Pro 上运行 GNU bash 版本 5.2.12(如果有帮助的话)。
答案1
答案2
好吧,这需要一些挖掘。感谢 don_crissti(指出了清除输出的合理理由)和 Gilles Quenot(让我进入了一个非常难的兔子洞去尝试了解sponge
)。
然而最终的最终结果如下:
文件的内容被清除,因为这正是使用
>
/>>
重定向时所期望的行为。重定向首先在命令的其余部分之前触发,因此我正在擦除我要读取的文件以进行清理(通过将任何内容重定向到该文件),然后无法将命令的结果写入该文件(因为正在读取的文件现在都是空的,并且重定向已经发生了)。在这种情况下,修复方法是简单地创建一个临时文件,然后立即用它覆盖源文件:
sed -E "s/$KEY=([\"']?).*([\"']?)/$KEY=\1$VAL\2/g" textfile.txt > textfile.tmp && mv textfile.tmp textfile.txt
这是 MacOS 使用 GNU 中不太健壮的二进制版本的一个不幸后果
sed
,虽然人们确实可以获得brew install gsed
GNU 版本,但它无法安全地取代基本版本,这使得可移植性和面向未来的能力变得粗略。...但这仍然给我留下了“消失的字符串定界符案例”的沉重负担。然而,在我的调查过程中,我偶然发现了一个关于在该工具的达尔文版本的正则表达式中使用扩展变量的有趣事实
sed
:我必须将变量名称包装在扩展中,转义$
's。所以,而不是
sed -E "s/$KEY=([\"']?)
[...]等。必要的语法是sed -E "s/\$(KEY)=([\"']?)
[...]等..进一步的探索最终证明,如果我简单地转义
$
每个变量,如下所示:sed -E "s/\$KEY=([\"']?)
[...]等。...我也会得到想要的结果,但是一个比我更咸的 Linux 老手暗示
$(VARIABLE)
在这种情况下语法更安全(尽管我不知道为什么)。
希望这篇后续文章可以帮助其他人,如果他们发现自己也遇到同样的困难,并再次感谢 don_crissti 和 Gilles Quenot 为我指明了正确的方向! <3