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
How to loop through a directory recursively to delete files with certain extensions
提问by Elitmiar
I need to loop through a directory recursively and remove all files with extension .pdf
and .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
find
is 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 -print0
apply to both or
clauses.)
(此处需要转义括号以-print0
适用于两个or
子句。)
GNU and *BSD find also has a -delete
action, 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 globstar
instead 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, find
is 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 -delete
action 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\ubuntu
directory( whole directory structure under ubuntu ) and apply the necessary checks in else
block.
以下函数将递归遍历目录中的所有\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 find
into another utility. find
has a -delete
flag built into it.
没有理由将 的输出通过管道传输find
到另一个实用程序。find
有一个-delete
内置的标志。
find /tmp -name '*.pdf' -or -name '*.doc' -delete