如何递归遍历目录以删除具有特定扩展名的文件

Rol*_*and 146 bash

我需要递归遍历一个目录并删除扩展名为.pdf和.doc的所有文件,我正在设法循环遍历一个目录,但没有设法过滤带有上述文件扩展名的文件.

我的代码到目前为止

#/bin/sh

SEARCH_FOLDER="/tmp/*"

for f in $SEARCH_FOLDER
do
    if [ -d "$f" ]
    then
        for ff in $f/*
        do      
            echo "Processing $ff"
        done
    else
        echo "Processing file $f"
    fi
done
Run Code Online (Sandbox Code Playgroud)

我需要帮助才能完成代码,因为我无处可去.

Jam*_*ven 192

作为mouviciel答案的后续内容,您也可以将其作为for循环,而不是使用xargs.我经常发现xargs很麻烦,特别是如果我需要在每次迭代中做一些更复杂的事情.

for f in $(find /tmp -name '*.pdf' -or -name '*.doc'); do rm $f; done
Run Code Online (Sandbox Code Playgroud)

正如许多人所评论的那样,如果文件名中有空格,则会失败.您可以通过临时将IFS(内部字段分隔符)设置为换行符来解决此问题.如果\[?*文件名中有通配符,则也会失败.您可以通过暂时禁用通配符扩展(globbing)来解决此问题.

IFS=$'\n'; set -f
for f in $(find /tmp -name '*.pdf' -or -name '*.doc'); do rm "$f"; done
unset IFS; set +f
Run Code Online (Sandbox Code Playgroud)

如果您的文件名中有换行符,那么这也不起作用.你最好使用基于xargs的解决方案:

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -print0 | xargs -0 rm
Run Code Online (Sandbox Code Playgroud)

(此处必须使用转义括号才能-print0适用于这两个or条款.)

GNU和*BSD find也有一个-delete动作,如下所示:

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -delete
Run Code Online (Sandbox Code Playgroud)

  • 如果文件名中有空格(for循环在空格上分割查找结果),则无法按预期工作. (25认同)
  • 为什么这个答案有这么多的赞成? (7认同)
  • 你如何在空白处分裂?我正在尝试类似的东西,我有很多带有空格的目录,搞砸了这个循环. (3认同)
  • 因为这是一个非常有用的答案? (3认同)
  • @Matthew你的编辑根本没有修复任何东西:它实际上使命令_only工作,如果有一个唯一的找到file_.如果文件名中没有空格,制表符等,则至少使用此版本_works_.我回到旧版本.注意到明智可以解决'for f in $(find ...)`.**只是不要使用这种方法.** (2认同)
  • @DrewDormann 我的测试还表明“$(find...)”使事情变得更糟。我已经撤消了您的编辑,并进行了我自己的迟来的更新。 (2认同)

mou*_*iel 133

find 就是这样做的.

find /tmp -name '*.pdf' -or -name '*.doc' | xargs rm
Run Code Online (Sandbox Code Playgroud)

  • 应该总是使用`find ... -print0 | xargs -0 ...`,不是原始查找| xargs以避免包含换行符的文件名出现问题. (26认同)
  • 或者找到`-delete`选项. (18认同)
  • 使用没有选项的`xargs`几乎总是不好的建议,这也不例外.请改用"find ... -exec". (5认同)

小智 58

没有find:

for f in /tmp/* tmp/**/* ; do
  ...
done;
Run Code Online (Sandbox Code Playgroud)

/tmp/*是dir /tmp/**/*中的文件,是子文件夹中的文件.您可能需要启用globstar选项(shopt -s globstar).所以对于这个问题,代码应如下所示:

shopt -s globstar
for f in /tmp/*.pdf /tmp/*.doc tmp/**/*.pdf tmp/**/*.doc ; do
  rm "$f"
done
Run Code Online (Sandbox Code Playgroud)

请注意,这需要bash≥4.0(或zsh没有shopt -s globstar,或ksh set -o globstar而不是shopt -s globstar).此外,在bash <4.3中,它遍历到目录和目录的符号链接,这通常是不可取的.

  • 值得注意的是,globstar仅在Bash 4.0或更高版本中可用。这不是许多计算机上的默认版本。 (2认同)
  • `**` 是一个很好的扩展,但不能移植到 POSIX `sh`。(这个问题被标记为 [tag:bash],但很高兴指出,与这里的几个解决方案不同,这确实是仅 Bash 的。或者,它也适用于其他几个扩展 shell。) (2认同)

fal*_*tro 26

如果你想以递归方式做某事,我建议你使用递归(是的,你可以使用堆栈等来做,但是嘿).

recursiverm() {
  for d in *; do
    if [ -d "$d" ]; then
      (cd -- "$d" && recursiverm)
    fi
    rm -f *.pdf
    rm -f *.doc
  done
}

(cd /tmp; recursiverm)
Run Code Online (Sandbox Code Playgroud)

也就是说,find可能是一个更好的选择,正如已经提出的那样.


Eri*_*ang 15

以下是使用shell(bash)的示例:

#!/bin/bash

# loop & print a folder recusively,
print_folder_recurse() {
    for i in "$1"/*;do
        if [ -d "$i" ];then
            echo "dir: $i"
            print_folder_recurse "$i"
        elif [ -f "$i" ]; then
            echo "file: $i"
        fi
    done
}


# try get path from param
path=""
if [ -d "$1" ]; then
    path=$1;
else
    path="/tmp"
fi

echo "base path: $path"
print_folder_recurse $path
Run Code Online (Sandbox Code Playgroud)


Oli*_*rth 13

这不会直接回答您的问题,但您可以通过单行解决您的问题:

find /tmp \( -name "*.pdf" -o -name "*.doc" \) -type f -exec rm {} +
Run Code Online (Sandbox Code Playgroud)

某些版本的find(GNU,BSD)有一个-delete你可以使用而不是调用的动作rm:

find /tmp \( -name "*.pdf" -o -name "*.doc" \) -type f -delete
Run Code Online (Sandbox Code Playgroud)


TJR*_*TJR 7

这种方法很好地处理空间.

files="$(find -L "$dir" -type f)"
echo "Count: $(echo -n "$files" | wc -l)"
echo "$files" | while read file; do
  echo "$file"
done
Run Code Online (Sandbox Code Playgroud)

编辑,逐个修复

function count() {
    files="$(find -L "$1" -type f)";
    if [[ "$files" == "" ]]; then
        echo "No files";
        return 0;
    fi
    file_count=$(echo "$files" | wc -l)
    echo "Count: $file_count"
    echo "$files" | while read file; do
        echo "$file"
    done
}
Run Code Online (Sandbox Code Playgroud)


小智 7

对于 bash(从 4.0 版开始):

shopt -s globstar nullglob dotglob
echo **/*".ext"
Run Code Online (Sandbox Code Playgroud)

就这样。
尾随扩展名“.ext”用于选择具有该扩展名的文件(或目录)。

选项 globstar 激活 **(递归搜索)。
选项 nullglob 在不匹配任何文件/目录时删除 *。
选项 dotglob 包括以点开头的文件(隐藏文件)。

请注意,在 bash 4.3 之前,**/还会遍历到不需要的目录的符号链接。