如何延迟 BASH`>` 的`redirection operator`
How to delay `redirection operator` of BASH `>`
首先我创建了 3 个文件:
$ touch alpha bravo carlos
然后我想将列表保存到文件中:
$ ls > info.txt
然而,我总是把我的 info.txt
放在里面:
$ cat info.txt
alpha
bravo
carlos
info.txt
看起来重定向运算符首先创建了我的 info.txt
。
在这种情况下,我的问题是。在创建 info.txt
之前如何保存我的文件列表?
主要问题是关于重定向运算符的。为什么它先行动,如何延迟它让我先完成我的任务?用上面的例子来回答。
一种简单的方法是将命令输出保存到一个变量中,如下所示:
ls_output="$(ls)"
然后使用以下任一命令将该变量的值写入文件:
printf '%s\n' "$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
。 . .这显然有不同的注意事项(例如,它需要访问才能写入不同的目录),但应该适用于大多数系统。
当您将命令的输出重定向到文件时,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
顺便说一句,可能don't use ls
in scripts.如果你想要当前目录中的文件列表
printf '%s\n' *
这样做。
一种方法是从 ls
输出中排除 info.txt
文件。
如果您可以将列表文件重命名为 .info.txt
那么它就很简单:
ls >.info.txt
默认情况下,ls
不列出名称以 .
开头的文件。
如果你不能重命名列表文件但你有 GNU ls
那么你可以使用:
ls --ignore=info.txt >info.txt
否则,您可以使用:
ls | grep -v '^info\.txt$' >info.txt
以上所有选项的优点是您可以在创建列表文件后安全地运行它们。
另一种通用方法是用一条命令捕获ls
的输出,然后用第二条命令将其保存到列表文件中。正如其他人指出的那样,临时文件和 shell 变量是捕获输出的两种特定方式。另一种方式,如果你有 moreutils package installed, is to use the sponge 实用程序:
ls | sponge info.txt
最后,请注意,如果 info.txt
包含纯 ls
输出,您可能无法从中可靠地提取文件列表。有关详细信息,请参阅 ParsingLs - Greg's Wiki。
首先我创建了 3 个文件:
$ touch alpha bravo carlos
然后我想将列表保存到文件中:
$ ls > info.txt
然而,我总是把我的 info.txt
放在里面:
$ cat info.txt alpha bravo carlos info.txt
看起来重定向运算符首先创建了我的 info.txt
。
在这种情况下,我的问题是。在创建 info.txt
之前如何保存我的文件列表?
主要问题是关于重定向运算符的。为什么它先行动,如何延迟它让我先完成我的任务?用上面的例子来回答。
一种简单的方法是将命令输出保存到一个变量中,如下所示:
ls_output="$(ls)"
然后使用以下任一命令将该变量的值写入文件:
printf '%s\n' "$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
。 . .这显然有不同的注意事项(例如,它需要访问才能写入不同的目录),但应该适用于大多数系统。
当您将命令的输出重定向到文件时,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
顺便说一句,可能don't use ls
in scripts.如果你想要当前目录中的文件列表
printf '%s\n' *
这样做。
一种方法是从 ls
输出中排除 info.txt
文件。
如果您可以将列表文件重命名为 .info.txt
那么它就很简单:
ls >.info.txt
默认情况下,ls
不列出名称以 .
开头的文件。
如果你不能重命名列表文件但你有 GNU ls
那么你可以使用:
ls --ignore=info.txt >info.txt
否则,您可以使用:
ls | grep -v '^info\.txt$' >info.txt
以上所有选项的优点是您可以在创建列表文件后安全地运行它们。
另一种通用方法是用一条命令捕获ls
的输出,然后用第二条命令将其保存到列表文件中。正如其他人指出的那样,临时文件和 shell 变量是捕获输出的两种特定方式。另一种方式,如果你有 moreutils package installed, is to use the sponge 实用程序:
ls | sponge info.txt
最后,请注意,如果 info.txt
包含纯 ls
输出,您可能无法从中可靠地提取文件列表。有关详细信息,请参阅 ParsingLs - Greg's Wiki。