如何延迟 BASH ">"的"重定向运算符"



首先我创建3个文件:

$ touch alpha bravo carlos

然后我想将列表保存到一个文件:

$ ls > info.txt

然而,我总是把我的info.txt放在里面:

$cat info.txt阿尔法布拉沃卡洛斯info.txt

看起来重定向操作符首先创建了我的info.txt

在这种情况下,我的问题是。如何在首先创建info.txt之前保存文件列表?


主要问题是关于重定向运算符。为什么它会先行动,如何推迟它,让我先完成任务?使用上面的例子来回答问题。

当您将命令的输出重定向到一个文件时,shell会打开一个指向目标文件的文件句柄,然后在其标准输出连接到此文件句柄的子进程中运行该命令。无法更改此顺序,但如果不希望ls输出包含新文件,则可以重定向到其他目录中的文件。

ls >/tmp/info.txt
mv /tmp/info.txt ./

在生产脚本中,您应该确保文件名是唯一的且不可预测的。

t=$(mktemp -t lstemp.XXXXXXXXXX) || exit
trap 'rm -f "$t"' INT HUP 
ls >"$t"
mv "$t" ./info.txt

或者,将输出捕获到一个变量中,然后将该变量写入一个文件。

files=$(ls)
echo "$files" >info.txt

顺便说一句,可能不要在脚本中使用ls。如果您想要当前目录中的文件列表

printf '%sn' *

这样做。

一种简单的方法是将命令输出保存到一个变量中,如下所示:

ls_output="$(ls)"

然后使用以下任何命令将该变量的值写入文件:

printf '%sn' "$ls_output" > info.txt
cat <<< "$ls_output" > info.txt
echo "$ls_output" > info.txt

这种方法的一些注意事项:

  • Bash变量不能包含空字节。如果命令的输出包含一个空字节,则该字节及其后的所有内容都将被丢弃。
    • 不过,在ls的特定情况下,这不应该是一个问题,因为ls的输出永远不应该包含空字节
  • $(...)删除尾随换行符。上面的方法通过在创建info.txt时添加一条换行来弥补这一点,但如果命令输出以多条换行结束,那么上面的方法将有效地将它们折叠成一条换行。
    • ls的特定情况下,如果文件名以换行符结尾,就会发生这种情况——这很不寻常,不太可能是故意的,但仍然是可能的
  • 由于上面在创建info.txt时添加了一条换行符,因此即使命令输出没有以换行符结束,它也会在那里放置一条换行。
    • ls的特定情况下,这不应该是一个问题,因为ls的输出应该总是以换行符结束

如果您想避免上述问题,另一种方法是将命令输出保存到其他目录中的临时文件,然后将其移动到正确的位置;例如:

tmpfile="$(mktemp)"
ls > "$tmpfile"
mv -- "$tmpfile" info.txt

它显然有不同的注意事项(例如,它需要访问不同的目录(,但应该适用于大多数系统。

执行所需操作的一种方法是从ls输出中排除info.txt文件。

如果您可以将列表文件重命名为.info.txt,那么它就像:一样简单

ls >.info.txt

默认情况下,ls不会列出名称以.开头的文件。

如果你不能重命名列表文件,但你有GNUls,那么你可以使用:

ls --ignore=info.txt >info.txt

如果失败,您可以使用:

ls | grep -v '^info.txt$' >info.txt

以上所有选项的优点是,您可以在创建列表文件后安全地运行它们。

另一种通用方法是用一个命令捕获ls的输出,并用第二个命令将其保存到列表文件中。正如其他人所指出的,临时文件和shell变量是捕获输出的两种特定方式。另一种方法,如果你已经安装了moreutils包,那就是使用海绵实用程序:

ls | sponge info.txt

最后,请注意,如果info.txt包含纯ls输出,则可能无法可靠地从中提取文件列表。有关详细信息,请参阅解析Ls-Greg的Wiki。

最新更新