为什么重定向输出有时会产生一个空文件?

Stu*_*ent 4 shell pipe io-redirection

shell 管道的威力如此之大,有时让我失望。

例子

只是作为一个例子,管道

echo abc > file.txt
cat file.txt | sed 's/a/1/' > file.txt
Run Code Online (Sandbox Code Playgroud)

给我一个空的file.txt。意识到 shell 可能>首先调用,我做了一个改变:

echo abc > file.txt
{cat file.txt | sed 's/a/1/'} > file.txt
Run Code Online (Sandbox Code Playgroud)

另一个空文件再次让我感到惊讶file.txt。一种最终有效的丑陋方式是

echo abc > file.txt
echo $(cat file.txt | sed 's/a/1') > file.txt
Run Code Online (Sandbox Code Playgroud)

这会强制 shell 首先运行子 shell,然后重定向。

虽然我知道更好的实践sed,它可以让你摆脱echo, cat, grep.. 等,但我在这里很好奇的是完全学习 shell 的语法。这个问题不是关于如何解决上面的特定问题。

Q1(编辑:题外话)有没有好的资源可以让我学习语法?

恐怕不同的外壳可能有不同的语法,所以

Q2我可以使 shell 变得冗长,并且每次运行命令时都能清楚地看到它在做什么吗?

Q3(编辑:题外话)关于良好实践的任何其他建议?谢谢!

gle*_*man 12

考虑3.1.1 Shell Operation,特别是做事情的顺序:

  • 实际执行命令之前处理重定向,但是
  • 在重定向之前处理扩展。

这意味着 for cat file > file,输出重定向(截断文件)在cat产生之前发生,cat现在有一个空文件可以使用。

但是会echo "$(cat file)" > file执行您期望的操作,因为命令替换Shell Expansion,并且发生在重定向之前。

典型的建议是做

cat file > tmpfile && mv tmpfile file
Run Code Online (Sandbox Code Playgroud)

你可以mktemp在这里使用。

或者安装moreutils包并使用sponge

cat file | sponge file
Run Code Online (Sandbox Code Playgroud)

尽管要解决您正在使用的特定命令,请替换

cat file.txt | sed 's/a/1/' > file.txt
Run Code Online (Sandbox Code Playgroud)

与(假设 GNU sed)

sed -i 's/a/1/' file.txt
Run Code Online (Sandbox Code Playgroud)

  • `echo` 可以满足您的期望,如果您期望 `echo` 可以有截然不同的行为 (https://unix.stackexchange.com/questions/65803)。最短的例子是`file` 只包含字符串`-n`。 (2认同)

mar*_*raf 5

您已经成功找到了您不应该做的事情之一 :-) 永远不要重定向到您正在处理的文件!

A1:学习 shell 语法的好资源是绝对的 bash 脚本指南,IMO

A2:对于 bash 脚本,您可以使用set +x更详细的输出,但我不知道如何在“在提示符下运行”级别实现相同的输出。

A3:添加[solved]到您的搜索词。为您找到问题的解决方案,而不是更多您已经知道的问题。