观察:
$ ls
$ ls > list
$ cat list
list
这似乎表明ls
执行时重定向到文件list
已经开始并且list
文件已经创建。无论如何,这是一个足够好的解释,但问题是:我怎样才能防止这种情况发生?我期望发生的事情是ls
执行并将其输出转储到其中list
,这就是我想要的。
答案1
正如您所注意到的,该文件是在ls
运行之前创建的。这是由于 shell 处理其操作顺序的方式所致。为了做到
ls > file
外壳需要创造 file
然后设置 stdout 指向它并最终运行程序ls
。
所以你有一些选择。
- 在另一个目录(例如)中创建文件
/tmp
,然后将mv
其复制到最终目录 - 将其创建为隐藏文件 (
.file
) 并重命名 - 用于
grep
从输出中删除文件 - 欺骗 :-)
作弊会是这样的
x=$(ls) ; printf "%s\n" "$x" > file
这会导致 的输出ls
保存在变量中,然后我们将其写出。
答案2
输出文件是由 shell 在ls
开始之前创建的。您可以使用以下方法解决此问题tee
:
ls | tee list
为了彻底击败任何竞争条件,总有
ls | grep -vx 'list' > list
或者,如果您愿意,tee
也可以显示结果:
ls | grep -vx 'list' | tee list
然而,正如评论中指出的那样,当文件名包含奇怪的字符时,这样的事情经常会中断。 Unix 文件名通常可以包含除NUL
和之外的任何字符/
,因此解析 的输出ls
非常困难:
- 如果文件名以一个或多个
\n
. grep
当搜索词位于 之间时,过滤失败\n
。- 您可以使用 来分隔文件名,
NUL
而不是\n
使用find
,但是将其转换为类似于 的传统排序、换行符分隔输出的内容可能很困难ls
。 - 如果输出文件名已存在,则从列表中删除它可能不正确。
因此,唯一真正有效的方法是在其他地方创建输出文件,并将其移动到位。如果你永远不会使用ls -a
,那么这有效:
ls > .list && mv .list list
如果您可能正在使用ls -a
, then.list
可能会出现在您的输出中,但不再存在于目录中。因此,您将使用不同的目录,例如/tmp
存储中间结果。当然,如果你总是使用的话/tmp
就会遇到麻烦,所以你可以写一个脚本:
#!/bin/sh
OUTDIR='/tmp'
if [ "${PWD}" = '/tmp' ]; then
OUTDIR="${HOME}"
fi
ls > "${OUTDIR}/list" && mv "${OUTDIR}/list" list
不过,对于这项任务来说,这似乎过于复杂。
但问题的全部原因是 shell 在命令开始之前创建输出文件。我们可以考虑到这一点,然后让 shell 为我们列出文件。那我们根本就不需要了ls
!
printf '%s\n' * > list
这将一直有效,直到目录中的文件太多而无法放入参数列表中。
答案3
您可以使用moreutils
sponge
:
ls | sponge list
或者与zsh
:
cp =(ls) list
使用 GNU ls
:
ls -I list > list
(尽管如果之前调用过一个文件list
,则意味着它不会被列出)。
由于ls
无论如何输出都是排序的,您也可以使用(假设您的文件名不包含换行符):
ls | sort -o list
或者为了避免双重排序,如果您ls
支持-U
nsorted U
(注意某些ls
实现有 a-U
为其他东西):
ls -U | sort -o list
答案4
部分/大部分功劳归功于@StephenHarris...
echo "`ls`" > list
相当于
echo "$(ls)" > list