当我将 ls 的输出重定向到文件时,文件名包含在该文件中。我怎样才能避免这种情况?

当我将 ls 的输出重定向到文件时,文件名包含在该文件中。我怎样才能避免这种情况?

观察:

$ ls
$ ls > list
$ cat list
list

这似乎表明ls执行时重定向到文件list已经开始并且list文件已经创建。无论如何,这是一个足够好的解释,但问题是:我怎样才能防止这种情况发生?我期望发生的事情是ls执行并将其输出转储到其中list,这就是我想要的。

答案1

正如您所注意到的,该文件是在ls运行之前创建的。这是由于 shell 处理其操作顺序的方式所致。为了做到

ls > file

外壳需要创造 file然后设置 stdout 指向它并最终运行程序ls

所以你有一些选择。

  1. 在另一个目录(例如)中创建文件/tmp,然后将mv其复制到最终目录
  2. 将其创建为隐藏文件 ( .file) 并重命名
  3. 用于grep从输出中删除文件
  4. 欺骗 :-)

作弊会是这样的

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支持-Unsorted U(注意某些ls实现有 a-U为其他东西):

ls -U | sort -o list

答案4

部分/大部分功劳归功于@StephenHarris...

echo "`ls`" > list

相当于

echo "$(ls)" > list

相关内容