首先我创建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。