我编写了一个脚本,可以删除文件夹中除最后两个文件之外的所有文件:
#!/bin/bash
ls -1 --quoting-style=shell-always /path/to/some/folder \
| head -n -2 \
| xargs printf -- "'/path/to/some/folder/%s'\n" \
| xargs sudo rm -rf
该脚本将作为 cron 作业每天执行。
理由如下:
使用获取所有文件的列表
ls -1
(以便我每行获取一个文件);head -n -2
使用;从列表中删除最后两个因为
ls
打印相对路径,所以使用该xargs printf
东西在文件夹路径前面添加并使其成为绝对路径;将它们发送至
sudo rm -rf
使用xargs
。
每个人都有权访问此文件夹,因此任何人都可以创建和删除此文件夹中的任何文件。
问题是: sudo rm -rf
很可怕。xargs sudo rm -rf
非常可怕。
我想确保没有人能够通过创建要删除的巧妙文件(无论是意外还是故意)来损坏其他文件夹/系统。我不知道,比如以下巧妙的东西:
file with / spaces.txt
这可能会导致超级可怕的后果sudo rm -rf /
。
编辑:我的错误,文件名不能包含/
,所以这个特定问题不会发生,但是否存在其他风险的问题仍然存在。
这就是我使用的原因--quoting-style=shell-always
,这应该可以防止任何带有空格的文件的技巧。但现在我想知道是否有人可以巧妙地使用空格和也许是文件名中的引号。
我的脚本安全吗?
注意:我需要,sudo
因为我正在远程访问该文件夹(从使用映射的网络驱动器mount
),并且如果没有 sudo,我就无法使其工作。
答案1
在 Linux 中,任何字符都是有效的文件名构成字符,除了:
\0
(ASCII NUL):用于 C 语言中的字符串终止/
(正斜杠):用于路径分隔
因此,您可以想象,您的方法在很多情况下肯定不会起作用,例如它是否处理\n
文件名中的换行符()?(暗示: 不)。
几点说明:
- 不要解析
ls
;使用专用工具(大多数用例至少有一个) - 处理文件名时,尽量利用几乎所有处理此类数据的 GNU 工具提供的 NUL 分隔输出
- 管道传输时要小心,确保两个程序都能理解 NUL 分隔
- 无论何时调用
xargs
,看看你是否可以逃脱find ... -exec
;在大多数情况下,你只需要find
一个人就可以了
我想这些现在能帮助你了。钢铁司机已经在注释中提供了 NUL 分离的想法 ( printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm
),请以此为起点。
答案2
xargs
确实支持一些引用:使用单引号,双引号或反斜杠,它可以接受任意参数¹,但其语法与 Bourne 类 shell 的引用语法不同。
Ubuntu 上的GNU 实现ls
没有任何与xargs
输入格式兼容的引用模式。
它ls --quoting-style=shell-always
与 ksh93、bash 和 zsh shell 引用语法兼容,但前提是ls
shell 以ls
与输出时相同的语言环境来解释输出。此外,应避免使用某些语言环境,例如使用 BIG5、BIG5-HKSCS、GBK 或 GB18030 的语言环境。
因此,使用这些 shell,你实际上可以执行以下操作:
typeset -a files
eval "files=($(ls --quoting-style=shell-always))"
xargs -r0a <(printf '%s\0' "${files[@]:0:3}") ...
但这相比之下没有什么优势:
files=(*(N)) # zsh
files=(~(N)*) # ksh93
shopt -s nullglob; files=(*) # bash
它唯一有用的情况是当你想使用-t
选项按 mtime/atime/ctime 或/ls
对文件进行排序时。但即便如此,你最好还是使用:-S
-V
zsh
files=(*(Nom))
例如按 mtime 对文件进行排序(使用oL
for-S
和n
for -V
)。
要删除除最近修改的两个常规文件之外的所有文件:
rm -f -- *(D.om[3,-1])
¹ 仍然存在一些长度限制(由execve()
一些非 GNUxargs
实现中的任意低得多的限制),并且一些非 GNUxargs
实现将阻止包含不形成有效字符的字节序列的输入。