ls会始终列出rm将删除的文件吗?


33

我觉得我应该确定的一点:如果我ls <something>rm <something>删除ls显示的完全相同的文件?在什么情况下rm可以删除ls未显示的文件?(这是在18.04 bash)

编辑:谢谢所有回答的人。我认为完整答案是所有答案的结合,因此我接受了投票最多的答案作为“答案”。

一路上我学到的意外的事情:

  • ls 在处理参数时并不像您想象的那样简单
  • 在Ubuntu的简单安装过程中,.bashrc别名 ls
  • 不要以破折号开头的文件命名,因为它们看起来像命令参数,而命名一个-r则要求它!

8
我有些惊讶,rm没有--dry-run国旗...
fkraiem

20
@Rinzwind为什么会find -deleterm呢?您说“这就是原因”,但是我完全不知道这是什么意思。还要注意,您的find调用将递归删除当前目录中的所有文件,在该目录中,rm将仅删除直接目录中的文件。也是-name *无人操作。总而言之,我对您的建议感到非常困惑……
marcelm

3
@marcelm我认为使用建议find是因为您可以运行它,查看所有文件,然后使用运行相同的命令-delete。既然您已经看到了的结果find,则删除的内容应该没有歧义(我实际上想以答案的形式听到有关此问题的更多详细信息)
Scribblemacher

5
@Scribblemacher “ ...您可以运行它,查看所有文件,然后使用-delete -来运行同一命令 -但是,这比running更好ls <filespec>,其次rm <filespec>(OP已经知道该怎么做)呢?
marcelm

12
@Rinzwind “这解决了choroba在ls之后和rm之前创建文件的瞬间的答案。” -不,不是。如果首先运行find ... -print以确认要删除的文件,然后运行,则find ... -delete仍将删除在两个命令之间创建的文件。如果同时使用-print-delete,则不会得到确认,只会显示有关已删除内容的事后报告(您也可以使用rm -v)。
marcelm

Answers:


40

同时,双方lsrm在其上传递给它们的参数上操作。

这些参数可以是一个简单的文件,所以ls file.extrm file.ext在同一文件进行操作和结果是明确的(列表中的文件/删除文件)。

如果相反,argument是目录,则ls directory列出目录的内容,而rm directory不能按原样工作(即rm,如果没有标志,则不能删除目录,而如果这样做rm -r directory,它将递归地删除目录下的所有文件directory 和目录本身)。

但是请记住,命令行参数可以进行shell扩展,因此,如果它们包含通配符,变量,其他命令的输出等,则不能始终保证将相同的参数传递给两个命令。

举一个极端的例子,想想ls $(rand).txtrm $(rand).txt,参数是“相同的”,但结果却大不相同!


3
还要考虑lsvs rm *“隐藏”(点)文件的位置,尽管即使如此,因为我没有编写,这也不是一个公平的比较ls *。但是rm它本身没有意义,所以整个东西实际上就是苹果和橙子。如果我正确理解的话,那么这就是您答案的关键,那就做好了:)
Lightness与Monica赛跑

5
关于lsvs的另一条评论rm -r:该命令ls <directory>不会在目录内显示隐藏文件,但rm -r <directory> 删除隐藏文件。
Daniel Wagner

1
@LightnessRacesinOrbit ls不会列出它们(除非别名为ls -a),rm *也不会删除它们(除非您已dotglob设置)。
19:30停止危害莫妮卡

20

如果您想到的是ls foo*.txtvs. rm foo*.txt,那么可以,它们将显示并删除相同的文件。外壳程序将扩展该glob,并将其传递给相关命令,然后这些命令将在列出的文件上运行。一列出他们,一删除他们。

明显的区别是,如果这些文件中的任何一个恰好是目录,ls则将列出其内容,但rm无法删除它。通常这不是问题,因为rm删除的内容少于所显示的内容ls

这里最大的问题来自运行ls *rm *包含以短划线开头文件名的目录中。它们将扩展为两个程序的命令行,就像您自己编写它们一样,ls-r意味着“反向排序顺序”,而rm-r意味着递归删除。如果您的子目录至少深两个级别,则差异很重要。(ls *将显示第一级目录的内容,但也会显示第一级以下的rm -r *所有内容。)

为了避免这种情况,请在允许的glob前面加上一个前导./以指示当前目录,和/或在a --glob(即rm ./*rm -- *)之前放一个信号以指示选项处理的结束。

对于这样的glob *.txt,这实际上不是问题,因为点是无效的选项字符,并且会导致错误(直到有人扩展实用程序以为其创造一个含义),但是./无论如何将它放在那里还是更安全的。


当然,如果您更改了外壳程序的globlob选项,或者在命令之间创建/移动/删除了文件,那么对于这两个命令,您也可能会获得不同的结果,但是我怀疑您是指上述任何一种情况。(安全地处理新文件/移动文件会非常麻烦。)


1
谢谢。这似乎突出显示了整个命令行语法的问题:如果您以破折号开头的文件名,您将在危险的水域中航行。在您忘记了-文件很久以后,谁知道您将来会使用什么命令。
B.坦纳(B.Tanner)

1
@ B.Tanner,是的。从某种意义上讲,问题在于命令行参数只是纯字符串。如果系统是今天设计的,则其中可能会有更多的结构,以便执行的程序可以知道参数是否应该作为选项标志。文件名的松散结构还带来了其他问题。dwheeler对此发表了一篇非常详尽的文章,但我必须警告阅读它会伤害人。(无论是从细节上,还是从可能会出问题的绝对糟糕中来。)
ilkkachu

3
我无法抗拒,您已经将它卖给了我,现在我要去阅读它,回想一下我设法在很多文件名的末尾得到一个回车符的回忆(试图看看是否可以构造一个也可以作为bash脚本运行的Windows批处理文件...我没看到一个即将到来的文件!)
B.Tanner

17

留下只有抛开什么壳行为,让我们关注rm,并ls能对付自己。至少一种ls显示rm无法删除内容的情况涉及目录权限,另一种是特殊目录...

文件夹权限

rm是对目录的一项操作,因为通过删除文件,您正在更改目录内容(或换句话说,列出目录条目,因为d ectorectory只不过是文件名和inode的列表)。这意味着您需要目录的写权限。即使您是文件的所有者,没有目录权限也无法删除文件。该反过来也一样rm可以消除可能由其他人,如果你是目录的拥有者所拥有,文件。

因此,您很可能具有对目录的读取和执行权限,例如,它可以使您遍历目录并查看目录内的内容 ls /bin/echo,但是rm /bin/echo除非您是的所有者/bin 或拥有的特权,否则您不能这样做sudo

您到处都会看到类似的情况。这是一种这样的情况:https : //superuser.com/a/331124/418028


特殊目录“。” 和“ ..”

另一个特殊情况是...目录。如果执行ls .ls ..,它将很乐意向您显示内容,但rm不允许对其进行“显示”:

$ rm -rf .
rm: refusing to remove '.' or '..' directory: skipping '.'

15

如果您键入ls *,然后rm *删除,则可能会删除比ls显示的文件更多的文件-这些文件可能是在的ls开始和结束之间的极短时间间隔内创建的rm


1
这是一种可能的情况/tmp,其中许多应用程序都可以创建临时文件,因此这两个命令中的总是可能*。但是,某些应用程序还会通过unlink()在打开文件句柄的同时对其进行匿名处理来使文件匿名化,因此它可能显示在文件中,ls *rm *可能无法捕获。
Sergiy Kolodyazhnyy

1
@OrangeDog您错过了重点。我们正在谈论种族状况
Sergiy Kolodyazhnyy

1
@OrangeDog因为我现在正在电话中,所以我需要验证这一点,但是即使显示内容和操作内容*之间存在差异,但问题仍然存在,因为目录内容的列表在这之间发生了变化。lsrm
Sergiy Kolodyazhnyy

1
即使只执行一个命令,在展开参数然后删除它们之间也存在竞争。
停止Harming Monica 18'Sep

1
@OrangeDog我可以同意。是的,因为通配符扩展可以在现有文件名上进行,所以在外壳程序扩展通配符与处理通配符的命令之间存在竞争条件,因此ls *实际上可以显示已经消失的文件名。
Sergiy Kolodyazhnyy

9

ls *并且rm *不负责扩展glob-由外壳程序完成,然后再将其传递给命令。

这意味着您可以对扩展的文件列表使用任何命令-因此,我将使用尽可能少的功能。

因此,更好的方法(或至少是另一种方法)是跳过中间人。

echo *将向您确切显示将传递给您的rm命令的内容。


2
谢谢,我喜欢在这种情况下使用echo代替ls的想法。
坦纳(B.Tanner)

3
printf "%s\n" *获得带有空格的文件名的明确视图。(或者换句话说,%q也要处理换行符和控制字符,以牺牲
难看的

4

怎么样:

$ mkdir what
$ cd what
$ mkdir -p huh/uhm ./-r
$ ls *
uhm
$ rm *
$ ls
-r
$ ls -R
.:
-r

./-r:

基本上,通配符扩展到以开头的东西-(或以手动输入的东西开头-但看起来有点像作弊的东西)可能由ls和解释不同rm


4

某些情况下,ls显示的内容并不能rm消除。一个相当极端但幸运的是,如果您传递的参数是指向目录的符号链接:ls将显示符号链接目录中的所有文件,同时rm将删除符号链接,而原始目录及其内容保持不变:

% ln -s $HOME some_link
% ls some_link    # Will display directory contents  
bin    lib    Desktop ...
% rm some_link
% ls $HOME
bin    lib    Desktop ...

1
ln -s $HOME some_link; ls some_link输出some_link@给我,但我已经ls别名为ls -F。显然,-F将行为更改为显示链接而不是取消引用链接。没想到。
marcelm

确实!ls -l例如,还以链接为目标,而不是目的地为目标……必须有方法检查链接本身。
亚历克西斯

1
添加选项的问题开辟了太多possibilities--我的答案是关于未经修改的行为lsrm
亚历克西斯

如果说ls some_link/,  ls -H some_linkls -L some_link,即使添加-F或,它也会列出链接目录-l。相反(排序),-d表示查看目录而不是目录内容;比较ls -l /tmpls -ld /tmp
G-Man说'Resstate Monica''Sep

当然,您可以添加更改行为的标志ls。您基本上在显示为什么枚举具有不同标志的行为不值得解决这个问题的麻烦……
Alexis

4

如果你只是ls代替ls -a,是rm可以删除你还没有看到隐藏的文件ls没有-a

范例:

根据 :

dir_test
├── .test
└── test2

ls dir_test :仅显示test2

ls -A dir_test :将显示test2 + .test

rm -r dir_test :将删除所有(.test + test2)

希望对您有所帮助。


你能举个例子吗?因为通常,rm *不会删除点文件。如果这样做,ls *还将显示它们。
marcelm

不,ls *不显示隐藏文件。
DevHugo

但是,是的,这有点令人困惑,我添加了一些示例。
DevHugo

1
ls -a将列出....testtest2。您可能需要将示例更改为使用ls -A,其中列出了 ...(即.testtest2以外的所有内容。
G-Man说'Resstate Monica''Sep

3

已经有很多好的答案,但是我想补充一些更深刻的见解。

问自己一个问题:ls如果您编写了多少个参数,

ls *

...?请注意,如果有任何可以扩展到的文件,则ls命令不会获取*as参数*。相反,shell会在调用命令之前先执行globbing,因此ls命令实际上会获取与globbing匹配的文件一样多的参数。要抑制滚动,请引用该参数。

对于任何命令都是如此:echo *vs echo '*'

有一个脚本,调用它countparams.sh来测试效果。它告诉您传递了多少参数并列出它们。

#!/bin/bash
echo "This script was given $# parameters."
arr=( "$@" )
for ((i=0;i<$#;i++)); do
        echo "Parameter $((i+1)): ${arr[$i]}"
done

使它可执行并运行./countparams.sh *。从其输出中学习!


1

如果两次目录的内容相同, glob两次都会以相同的方式扩展。


如果您确实要检查要删除的内容,请使用rm -i *.txt。在(尝试)删除文件之前,它将分别提示您输入每个文件。

这样可以保证在发生竞争的情况下是安全的:
        ls *.txt/创建了一个新文件/ rm *.txt
因为执行删除的同一程序会提示您输入每个文件。


对于正常使用而言,这太麻烦了,如果您别名rmrm -i,您会发现自己经常使用\rmrm -f。但是至少值得一提的是,有一种解决种族问题的方法。(它甚至可以移植到非GNU系统:POSIX rm(1)指定该-i选项。)

另一个选择是bash数组:to_remove=(*.txt),然后要求用户确认(也许在做完之后ls -ld -- "${to_remove[@]}"),然后 rm -- "${to_remove[@]}"。因此,全局扩展仅执行一次,并且列表逐字传递到rm

另一个实际可用的选项是GNU rm -I手册页),如果删除了4个以上的项目,它将提示您。(但不显示列表,仅显示总数。)我alias rm='rm -I'在桌面上使用。

这是一个很好的保护措施,可以防止匹配过大的半型模式导致胖手返回。但是ls通常在您拥有的目录中或在单用户系统上,以及在没有后台进程可以在那里异步创建新文件的情况下,最好使用first。为防止胖手指,请勿rm -rf /foo/bar/baz从左到右输入。 rm -rf /是特殊情况,但rm -rf /usr不是!省略-rf零件,或从开始ls,仅rm -rf在键入路径后添加零件。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.