shell pipeline 的威力如此之大,有时让我感到失望。
例子
就像一个例如,管道
echo abc > file.txt
cat file.txt | sed 's/a/1/' > file.txt
给我一个空的file.txt
。意识到 shell 可能>
首先调用,我做了一个更改:
echo abc > file.txt
{cat file.txt | sed 's/a/1/'} > file.txt
另一个空文件再次让我感到惊讶file.txt
。一个最终有效的丑陋方法是
echo abc > file.txt
echo $(cat file.txt | sed 's/a/1') > file.txt
这会强制 shell 首先运行子 shell,然后重定向。
问题
虽然我知道更好的做法sed
,它可以让您摆脱echo
,,cat
......grep
等等,但我在这里好奇的是彻底学习 shell 的语法。这个问题不是关于如何解决上述特定问题。
Q1(编辑:题外话)有没有好的资源可以让我学习语法?
我担心不同的 shell 可能有不同的语法,所以
Q2我可以让 shell 详细起来,并在每次运行命令时清楚地看到它到底在做什么吗?
Q3(编辑:题外话)关于良好实践的任何其他建议?谢谢你!
答案1
考虑3.1.1 外壳操作,特别是事情完成的顺序:
- 重定向已处理前命令实际上被执行了,但是
- 扩展是在重定向之前处理的。
这意味着对于cat file > file
,输出重定向(截断文件)发生在cat
生成之前,cat
现在有一个空文件可供使用。
但echo "$(cat file)" > file
正如您所期望的那样,因为命令替换是一个外壳膨胀,这发生在重定向之前。
典型的建议是
cat file > tmpfile && mv tmpfile file
您可以使用mktemp
这里。
或者安装moreutils
包并使用sponge
cat file | sponge file
尽管要解决您正在使用的特定命令,请将
cat file.txt | sed 's/a/1/' > file.txt
与(假设 GNU sed)
sed -i 's/a/1/' file.txt
答案2
您已成功找到您不应该做的事情之一:-) 切勿重定向到您正在处理的文件!
A1:学习 shell 语法的一个很好的资源是绝对的 bash 脚本编写指南,国际海事组织
A2:对于 bash 脚本,您可以使用set +x
更详细的输出,但我不知道如何在“在提示符下运行”级别实现相同的目的。
A3:添加[solved]
到您的搜索词中。为您找到问题的解决方案,而不是更多您已经知道的问题。
答案3
解决问题的一种方法是准确了解发生了什么顺序重定向、扩展、子 shell 等,以便您可以在发生此类错误之前捕获它。
我不推荐这个。有时您仍然会搞砸,代价可能是丢失数据。
最好是不是相信您有能力保持所有这些正确性并编写更多防白痴代码。 有时我们都是白痴。
所以
cp file.txt file.txt.old
cat file.txt.old | sed 's/a/1/' > file.txt
这更简单,因为除了第 1 行发生在第 2 行之前之外,不依赖于事件顺序。
作为一个额外的好处,它可以防止其他当您根本不应该运行此脚本时,会出现错误。
这种做法的代价是.old
文件到处都是。这是一份保险费,您会很高兴在需要的那天支付了它。盘很便宜。