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