bash 如何递归遍历目录以删除具有某些扩展名的文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4638874/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 20:03:14  来源:igfitidea点击:

How to loop through a directory recursively to delete files with certain extensions

bash

提问by Elitmiar

I need to loop through a directory recursively and remove all files with extension .pdfand .doc. I'm managing to loop through a directory recursively but not managing to filter the files with the above mentioned file extensions.

我需要遍历目录递归,并删除扩展名的文件.pdf.doc。我设法递归地遍历目录,但没有设法过滤具有上述文件扩展名的文件。

My code so far

到目前为止我的代码

#/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

I need help to complete the code, since I'm not getting anywhere.

我需要帮助来完成代码,因为我无处可去。

采纳答案by mouviciel

findis just made for that.

find就是为此而生的。

find /tmp -name '*.pdf' -or -name '*.doc' | xargs rm

回答by James Scriven

As a followup to mouviciel's answer, you could also do this as a for loop, instead of using xargs. I often find xargs cumbersome, especially if I need to do something more complicated in each iteration.

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

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

As a number of people have commented, this will fail if there are spaces in filenames. You can work around this by temporarily setting the IFS (internal field seperator) to the newline character. This also fails if there are wildcard characters \[?*in the file names. You can work around that by temporarily disabling wildcard expansion (globbing).

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

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

If you have newlines in your filenames, then that won't work either. You're better off with an xargs based solution:

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

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -print0 | xargs -0 rm

(The escaped brackets are required here to have the -print0apply to both orclauses.)

(此处需要转义括号以-print0适用于两个or子句。)

GNU and *BSD find also has a -deleteaction, which would look like this:

GNU 和 *BSD find 也有一个-delete动作,看起来像这样:

find /tmp \( -name '*.pdf' -or -name '*.doc' \) -delete

回答by Tomek

Without find:

没有find

for f in /tmp/* tmp/**/* ; do
  ...
done;

/tmp/*are files in dir and /tmp/**/*are files in subfolders. It is possible that you have to enable globstar option (shopt -s globstar). So for the question the code should look like this:

/tmp/*是目录/tmp/**/*中的文件和子文件夹中的文件。您可能必须启用 globstar 选项 ( shopt -s globstar)。所以对于这个问题,代码应该是这样的:

shopt -s globstar
for f in /tmp/*.pdf /tmp/*.doc tmp/**/*.pdf tmp/**/*.doc ; do
  rm "$f"
done

Note that this requires bash ≥4.0 (or zsh without shopt -s globstar, or ksh with set -o globstarinstead of shopt -s globstar). Furthermore, in bash <4.3, this traverses symbolic links to directories as well as directories, which is usually not desirable.

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

回答by falstro

If you want to do something recursively, I suggest you use recursion (yes, you can do it using stacks and so on, but hey).

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

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

(cd /tmp; recursiverm)

That said, findis probably a better choice as has already been suggested.

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

回答by Eric Wang

Here is an example using shell (bash):

下面是一个使用 shell ( bash)的例子:

#!/bin/bash

# loop & print a folder recusively,
print_folder_recurse() {
    for i in ""/*;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 "" ]; then
    path=;
else
    path="/tmp"
fi

echo "base path: $path"
print_folder_recurse $path

回答by Oliver Charlesworth

This doesn't answer your question directly, but you can solve your problem with a one-liner:

这不会直接回答您的问题,但您可以使用单线解决您的问题:

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

Some versions of find (GNU, BSD) have a -deleteaction which you can use instead of calling rm:

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

find /tmp \( -name "*.pdf" -o -name "*.doc" \) -type f -delete

回答by TJR

This method handles spaces well.

这种方法可以很好地处理空格。

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

Edit, fixes off-by-one

编辑,一一修复

function count() {
    files="$(find -L "" -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
}

回答by TJR

For bash (since version 4.0):

对于 bash(从 4.0 版开始):

shopt -s globstar nullglob dotglob
echo **/*".ext"

That's all.
The trailing extension ".ext" there to select files (or dirs) with that extension.

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

Option globstar activates the ** (search recursivelly).
Option nullglob removes an * when it matches no file/dir.
Option dotglob includes files that start wit a dot (hidden files).

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

Beware that before bash 4.3, **/also traverses symbolic links to directories which is not desirable.

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

回答by K_3

The following function would recursively iterate through all the directories in the \home\ubuntudirectory( whole directory structure under ubuntu ) and apply the necessary checks in elseblock.

以下函数将递归遍历目录中的所有\home\ubuntu目录(ubuntu 下的整个目录结构)并在else块中应用必要的检查。

function check {
        for file in /*      
        do
        if [ -d "$file" ]
        then
                check $file                          
        else
               ##check for the file
               if [ $(head -c 4 "$file") = "%PDF" ]; then
                         rm -r $file
               fi
        fi
        done     
}
domain=/home/ubuntu
check $domain

回答by Zak

There is no reason to pipe the output of findinto another utility. findhas a -deleteflag built into it.

没有理由将 的输出通过管道传输find到另一个实用程序。find有一个-delete内置的标志。

find /tmp -name '*.pdf' -or -name '*.doc' -delete