引用文件名是否足以运行`xargs sudo rm -rf`?


10

我编写了一个脚本,该脚本删除了文件夹中除最后两个文件以外的所有文件:

#!/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作业执行。

理由如下:

  1. 使用获取所有文件的列表ls -1(这样我每行得到一个文件);

  2. 使用head -n -2; 从列表中删除最后两个。

  3. 由于ls打印相对路径,因此请使用xargs printf事物在文件夹路径之前加上前缀,并使其成为绝对路径;

  4. 将其发送给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我将无法使它正常工作。


3
您是否考虑过做类似的事情printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm
钢铁司机

可以创建带有/名称中字符的文件吗?我正在尝试在此处实现这一点
George Udosen

3
@George不,文件名不能包含斜杠。
wjandrea

因此,当OP说一个聪明的人时,我想知道...
George Udosen

6
仅仅是因为您要解析ls输出,所以即使使用引号,这也已经是一个写得不好的命令。ls我想,我也使用语言环境来进行排序,所以我看不出head删除后2个的目的是什么(除非您试图摆脱它,.并且..无论如何都不允许将哪个iirc用作参数rm。只需使用find /path/to/folder -type f delete。不,sudo如果您从cron运行
-cron

Answers:


10

在Linux中,任何字符都是构成字符的有效文件名,除了:

  • \0 (ASCII NUL):用于C中的字符串终止
  • / (正斜杠):用于路径分隔

因此,您可以想象,您的方法在许多情况下绝对行不通,例如,它可以处理\n文件名中的换行符()吗?(提示: )。

几点注意事项:

  • 不要解析ls; 使用专用工具(大多数情况下至少有一种)
  • 处理文件名时,请尝试利用几乎所有处理此类数据的GNU工具提供的NUL分隔输出
  • 配管时要小心,确保两个程序都能理解NUL分离
  • 每当您调用时xargs,看看是否可以摆脱find ... -exec; 在大多数情况下,你将被罚款只find单独

我认为这些可以帮助您暂时离开。steeldriver已在注释(printf -- '%s\0' /path/to/some/folder/* | head -zn -2 | xargs -0 rm)中提供了NUL分隔的想法,以此为起点。


感谢您的回答:)我想您应该引用Steeldriver的评论而不是仅仅提及(因为评论不是永久性的)。我也会研究一下find,谢谢你的建议。
Pedro A

不过,我有一个问题:我不理解您的意思是“您的方法在许多情况下都无法像您想象的那样肯定起作用”-“不起作用”是“不安全”还是“不安全”?因为“您的方法”是指我而不是恶意用户,并且您的先前声明对我有利,所以我感到困惑。
Pedro A

@PedroA你是 :)就像我说的那样,由于除了提到的两个字符以外所有字符都是有效的,因此您应该能够想象ls解析方法失败的许多情况,例如您是否考虑了文件名中的换行符?
heemayl

哦,文件名中的换行符...我没想到。如果您也不介意将其添加到您的答案中:)另外,很抱歉提出这个问题,但是该怎么head -z做?听起来很荒谬,但是我在CoreOS容器linux中也没有,也man没有info。。。在互联网上也找不到。我得到head: invalid option 'z'
佩德罗(Pedro)

@PedroA Newline只是一种情况,您可能已经猜到了很多这样的情况。您需要GNU head(GNU 附带coreutils)。这是在线版本:manpages.ubuntu.com/manpages/xenial/man1/head.1.html
heemayl

3

xargs 确实支持某些引号:单引号,双引号或反斜杠允许其接受任意参数¹,但其语法不同于类似Bourne的shell的引号语法。

ls在Ubuntu上找到的GNU实现没有与xargs输入格式兼容的任何引用模式。

ls --quoting-style=shell-always与ksh93,bash和zsh shell引用语法兼容,但是仅当lsshell 的输出在输出时由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

唯一有用的情况是当您想使用按mtime / atime / ctime或/ 排序文件的-t选项时。但是即使如此,您也可以使用:ls-S-Vzsh

files=(*(Nom))

例如通过的mtime文件(使用排序oL-S,并n-V)。

删除除两个最近修改的常规文件外的所有文件:

rm -f -- *(D.om[3,-1])

¹仍然存在一些长度限制(execve()在某些非GNU xargs实现中,并且在某些方面要低得多的任意限制),并且某些非GNU xargs实现会阻塞包含不构成有效字符的字节序列的输入。

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.