我有一些以\n
字符命名的文件夹。
例如:
$ ls
''$'\n''Test'
那是指一个带有测试名称且名称前有一个空行的文件夹。
因此,当我在其父目录中运行一些类似的脚本时:
while IFS= read -r d; do
rmdir $d
done < <(find * -type d)
表明:
rmdir: failed to remove '': No such file or directory
rmdir: failed to remove 'Test': No such file or directory
因为它运行两次,一次在\n
,另一次在Test
,因为文件夹名称有两行。
那么我该如何解决这个问题,使得脚本知道\nTest
只是一个文件夹?
答案1
你那里只有一个命令,所以find
用-exec
标志调用就足够了rmdir
:
find -depth -type d -exec rmdir {} \;
或者使用-delete
中的选项 find -type d -delete
,但它不适用于非空目录。为此,您还需要-empty
标志。还请注意,-delete
暗示-depth
可以跳过。因此,另一个可行的替代方案是将所有内容保留为一个过程:
find -type d -empty -delete
如果目录不为空,则使用rm -rf {} \;
。为了仅隔离\n
文件名中包含的目录,我们可以结合 bash 的ANSI-C 引用 $'...'
带-name
选项:
find -type d -name $'*\n*' -empty -delete
POSIX-ly,我们可以这样处理:
find -depth -type d -name "$(printf '*\n*' )" -exec rmdir {} \;
值得一提的是,如果您的目标是删除目录,那么-delete
就足够了,但是如果您想在目录上执行命令,那么-exec
是最合适的。
也可以看看
答案2
您可以使用 shell globs 代替find
:
for d in */ ; do
rmdir "$d"
done
shell glob*/
匹配当前目录中的所有文件夹。此 for 循环构造会自动处理正确的分词。
请注意,根据您的 shell 选项,这可能会忽略隐藏文件夹(名称以 . 开头)。可以使用命令 更改该行为以匹配当前会话的所有文件shopt -s dotglob
。
也不要忘记始终引用你的变量。
答案3
到目前为止写的两个答案都是rmdir
每个目录调用一次,但是由于rmdir
可以接受多个参数,我想知道:有没有更有效的方法?
你可以简单地做
rmdir */
这绝对是最简单、最有效的方式,但在以下情况下可能会抛出错误许多目录(参见gnome-terminal 中命令行参数的最大长度是多少?)。如果您希望此方法以递归方式工作,请启用globstar
shell 选项shopt -s globstar
并使用**/*/
而不是*/
。
使用 GNU find
(如果我们不只是想使用-delete
),我们可以这样做
find -depth -type d -exec rmdir {} +
xargs
它以“与构建命令行相同的方式”构建命令行( man find
)。如果您不想让它以递归方式工作,-depth
请替换 。-maxdepth 1
第三个,也是我认为最精彩的方法,由steeldriver在这个答案:
printf '%s\0' */ | xargs -0 rmdir
这使用 shell 内置命令printf
来构建一个零分隔的参数列表,然后按需要将该列表通过管道传输到xargs
哪个调用。您可以使用and而不是像上面那样使其递归工作。rmdir
shopt -s globstar
**/*/
*/