最近,在一次工作面试中,有人问我:“如何在文件系统的所有文件夹中创建一个零大小文件(我认为这意味着一个空文件)?”我觉得这个问题有点奇怪,我想到了一个循环来列出所有目录并使用,touch
或者转到根目录并使用touch
递归选项。你有什么想法吗?
答案1
那可能是……
find . -type d -exec touch {}/emptyfile \;
-type d
表示“目录”exec
执行该命令touch
并创建一个名为“emptyfile”的文件- 替换
{}
找到的结果find
。 是/
使其成为有效路径+文件名,转义的 ; 是关闭命令(否则它将变为“emptyfile;”)
结果 ...
rinzwind@schijfwereld:~/t$ mkdir 1 2 3 4 5 6 7 8 9 10
rinzwind@schijfwereld:~/t$ ls -ltr */*
ls: cannot access '*/*': No such file or directory
rinzwind@schijfwereld:~/t$ find . -type d -exec touch {}/emptyfile \;
rinzwind@schijfwereld:~/t$ ls -ltr */*
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 5/emptyfile
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 9/emptyfile
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 1/emptyfile
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 8/emptyfile
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 3/emptyfile
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 2/emptyfile
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 10/emptyfile
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 7/emptyfile
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 6/emptyfile
-rw-rw-r-- 1 rinzwind rinzwind 0 apr 16 19:57 4/emptyfile
我的方法可行,但 Peter Cordes 的答案更好:)
答案2
为了有效地做到这一点,您需要避免touch
为每个要创建的文件生成一个新进程。
这是其优点的一部分xargs
,将参数分批处理成尽可能大的块,同时又足够小以适合单个进程的命令行。(现代 Linux 有相当大的限制,因此这几乎不再是一个问题,xargs 很少需要实际多次运行您的命令来处理所有参数,但它也可以避免文件名受到 shell 分词的影响,就像foo $(bar)
. 中那样)
我们可以使用 find 自身-printf
将附加内容格式化到每个目录路径上,特别是我们想要的文件名。xargs -0
使用'\0'
字节作为分隔符,因此对于任意文件名(甚至包括换行符)都是安全的。如果您没有使用自定义 printf 格式,则可以使用它-print0
来打印 0 分隔的路径。
find . -xdev -type d -printf '%p/empty\0' | xargs -0 echo touch
(在测试目录中,打印touch ./empty ./2/empty ./1/empty
,而不是touch ./empty
, touch ./1/empty
等等,所以它为多个文件运行一个touch
。)
mktemp
只接受一个模板,但如果我们想要一些命名的随机性可以减少意外触碰现有文件的机会,您可以这样做。
find . -xdev -type d -printf "%p/empty.$RANDOM\0" | xargs -0 echo touch
请注意,这是相同的每个目录中都有 15 位随机数,因为"$RANDOM"
在 find 开始之前,bash 会对其进行一次扩展。您可以使用$(date +%s).$RANDOM
或任何您想要的作为文件名的一部分。
对于 SSD 或 tmpfs,CPU 可能是瓶颈。或者如果你很幸运,磁盘上的元数据 I/O 恰好是连续的,因为你接触的是每一个目录(并分配一堆新 inode),即使是旋转磁盘也可能保持得相当不错。尽管您可能没有按照目录在磁盘上的排列顺序来触碰它们。
无论如何,没有必要浪费大量的 CPU 时间来启动一些应该受 I/O 限制的进程。
无效的方法:
find -exec touch {} +
批处理参数,但是当其本身不存在-exec touch {}/empty +
时拒绝工作。{}
xargs -I {} echo touch {}/emptyfile
暗示-L 1
(每次调用命令时仅处理一行输入,无论是实际行还是以 0 分隔的字符串xargs -0
)。因此,如果我们想利用 xargs 进行批处理参数,则不能使用它来修改每个参数。
答案3
find /mountpoint -xdev -type d -exec mktemp -p {} \;
一个非常明显的方面是您可能需要或不需要 root 访问权限来实际在 下创建文件/mountpoint
。
有两个不太明显的方面:
你说“在文件系统的所有文件夹中”,所以我们从特定的开始
/mountpoint
,不进入其他文件系统(-xdev
)。如果在树中更深层安装了其他文件系统,例如
/mountpoint/foo/another/mntpoint
,-xdev
则将阻止我们进入它们。但这些文件系统可能会掩盖属于相关文件系统的整个子树。在最好的情况下,安装在的文件系统会掩盖相关文件系统的/mountpoint/foo/another/mntpoint
空mntpoint
目录。因此,我们无法轻松访问“文件系统的所有文件夹”。有了 root 访问权限,我们可以
mount --bind /mountpoint /somewhere/else
事先进行操作。使用--bind
(与 相对--rbind
,请参阅man 8 mount
)mntpoint
深度/somewhere/else
不会从 复制子挂载/mountpoint/foo/another/mntpoint
。这样,我们就可以访问mntpoint
属于相关文件系统的 。这还不够。如果所讨论的文件系统是 Btrfs,那么可能
/mountpoint
允许访问某些子卷,但不能访问整个文件系统(比较这个问题)。一般而言,任何已挂载文件系统的子树都可以绑定挂载到另一个目录。卸载原始挂载点后,另一个目录将允许访问文件系统的一部分。我们的目录
/mountpoint
可能首先是“其他目录”,因此可能无法访问整个文件系统。您事先并不知道这一点。结论是:如果短语严格来说是“文件系统的所有文件夹”(而不是“所有子目录”,后者很简单),那么您需要确保不会遗漏文件系统的任何部分。然后才使用
find …
此答案开头给出的命令。带有
touch emptyfile
或 so 的解决方案不一定“创建一个零大小文件”。如果面试官已经emptyfile
在其中一个目录中创建了一个非空文件怎么办?陷阱!如果emptyfile
存在非空文件,则touch
不会创建它,文件也不会为空。实际上,您将无法在带有陷阱的目录中“创建一个零大小文件”。这就是我使用的原因mktemp
。该工具将努力真正创建一个新的空常规文件。
答案4
这将创建一个唯一名称每个目录中都有空文件开始与和降序从当前目录包括隐藏目录如果你想。
第一的,使用 获取目录列表tree
。
然后,将它们传递给xargs
如下内容:
tree --noreport -dfi | xargs -L 1 -I {} echo touch {}/emptyfile_"$(date +%s)"
或者,像while
这样循环:
tree --noreport -dfi | \
while read -r d; do echo touch "$d"/emptyfile_"$(date +%s)"; done
或者,甚至循环for
(如果目录名称不包含空格)如下所示:
for d in $(tree --noreport -dfi); do echo touch "$d"/emptyfile_"$(date +%s)"; done
echo
是为了防止测试时意外创建文件。当对输出满意时,删除echo
创建的文件。--noreport
省略在树列表末尾打印文件和目录报告。-dfi
仅列出目录,打印每个目录的完整路径前缀,并且使树不打印缩进行。使用
-dfia
而不是-dfi
包括隐藏目录也一样。"$(date +%s)"
将当前时间戳附加到文件名,使其与每个目录中的现有文件不同。请注意,如果您需要固定文件名,emptyfile_1618679443
可以将其更改为随机数。67639871206723
xargs -L 1 -I {}
每次读取一行输入并将其分配给{}
。