我有一个 10+ TB NFS /home,可容纳 100 多个用户以及公共文件夹。我需要更改其中 10 个用户的用户名和 UID。起初,我想运行以下命令:
find /home -uid 812 -exec chown NEWUSER {} \;
现在,问题是它会遍历我所有的 10TB 一次,并将它找到的 uid 812 的任何文件更改为 NEWUSER,这就是我想要的。但这将需要相当长的时间,并且只会为该用户完成;然后我将不得不为其他 9 个用户再次运行该命令,将其从相当长的时间变成相当长的时间 * 9。
除了我不喜欢脚本这一事实之外,我想脚本在这里会是一个朋友,但我不知道从哪里开始。我想使用该find
命令并检查 /home 中的所有文件。然后:
IF FILEOWNER IS 813 then NEWOWNER IS NEWUSER1
IF FILEOWNER IS 814 then NEWOWNER IS NEWUSER2
... 等等。
您是否认为有一种方法可以做到这一点,这样我就不必扫描 10 GB 的数据 10 次——只需扫描一次?
答案1
做整件事使用find
。
find /home ! -type l \( \
-uid 812 -exec chown NEWUSER {} + \
-o -uid 813 -exec chown ANOTHER {} + \
-o -uid 814 -exec chown SOMEONE {} + \
-o -uid 815 -exec chown SOMEGUY {} + \)
一次目录结构遍历。一切chown
都完成了。
我们排除符号链接,否则将chown
应用于符号链接的目标。在某些系统上,您可以使用 更改符号链接的所有者chown -h
。在这些上,您可以添加-h
和删除! -type l
.
请注意,如果任何文件是setuid
,这都会搞砸。你也可以处理这个使用find
。
find
的业务是评估表达式,而不是定位文件。是的,find
当然可以找到文件;但这实际上只是一个副作用。
答案2
首先,您不需要实际扫描整个 10TB,只需扫描目录结构,因此进行基本查找并不像听起来那么糟糕。
其次,如果您执行基本操作并同时启动所有十个,由于读取缓存,您可能会获得更好的性能。
或者你可以这样做 find /home -uid 812 -o uid 813 -o
......-printf '%U:%p\0'| sed -zre 's!["$]!\\&!g' -e 's/^812:(.*)$/chown NEWUSER1 "\1"/'e -e 's/^813
我想我发现了主要的问题,但 sed eval 总是有风险的。如果您不使用 GNU sed,您可以将输出通过管道传输到 shell。
这是一个更干净的版本,使用 gnu 工具更好地处理引用。
find /home -uid 812 -o uid 813 -o ` ... ` -printf '%U:' -print0| \
sed -zre "s/'/'\''/g" \
-e 's/^812:(.*)$/chown NEWUSER1 '"'\1'/e" \
-e 's/^813:(.*)$/chown NEWUSER2 '"'\1'/e" \
...
这些工作方式:
查找的第一部分是一堆 ORed uid 检查,主要内容是 printf(或第二个版本中的 printf 和 print0),它打印数字 uid、冒号、带有 c 转义的文件名(第二个版本中没有转义)版本)和每个文件的空值。然后 sed 使用空分隔符和扩展正则表达式引用美元符号和双引号,然后每个用户使用一个表达式匹配 uid 和冒号分隔文件名创建一个 shell 命令行来执行 chown 并使用 shell 对其进行评估。我可能应该多花几分钟来验证 printf 上的转义列表,但如果您在 s 命令上没有 e 标志的情况下进行测试,则足以开始。在第二个版本中,唯一转义的字符是单引号。不转义的原因是,在严格定义的标头之后,允许除 null 之外的所有字符在没有特殊含义的情况下通过,我们不必删除转义。