shell脚本实现查找文件夹下重复的文件,并提供删除功能
Windows下有软件FindDupFile,可以搜索指定目录及其下子目录,列出所有内容完全相同的文件(文件名可能不同),然后由用户选择删除重复的文件。
然而shell脚本却可以使用几行的命令完成与此相同的工作,借助windows下的shell脚本工具Cygwin,可以实现扫描Windows下的目录,原理简述如下:
1.首先借助find命令扫描文件夹下类型为普通文件的所有文件,find命令的输出是一行一个文件
2.对find找到的所有文件进行MD5校验,校验命令为md5sum files,输出文本格式为:MD5SUM *file
3.内容的文件的md5校验值是相同的,所有对MD5SUM校验值进行相同值查询,因此使用awk的关联数组,将不相同的文件输出
4.对awk输出重复的文件表进行删除。
shell脚本如下:
#!/bin/bash - #查找文件夹下相同的文件
#Usage: dupfile.sh [-ds] dirs
# dirs 请用单引号引起来 del=0
silent=0 trap "" PIPE #参数处理
while test $# -gt 0
do
case $1 in
-d | --delete)
del=1
;;
-s | --silent)
silent=1
;;
-*)
break
;;
*)
break
;;
esac
shift
done if [ $# -eq 0 ]
then
echo "Usage: dupfile.sh [-ds] dirs" >&2
exit 0
fi #find查找所有文件并进行MD5校验,
# md5sum对二进制文件输出为 MD5SUM *file
# awk使用关联数组处理相同的md5值并按照格式输出,使用DEL传递参数
# tee命令将管道拷贝一份到进程替换,另一份到stdout
find "$@" -type f -exec md5sum {} + |
awk -v FS="*" -v DEL=$del -v SLT=$silent '
{
if($1 in md5)
md5[$1] = md5[$1] "*" $2
else
md5[$1] = $2
}
END{
for(key in md5)
{
if(DEL==0) K++
n = split(md5[key], files, "*")
if(SLT==1 && n==1) continue
if(DEL==0)
printf("%-*s %s\n", length(key), key, files[n])
for(n-- ;n>0 ; n--)
{
K++
if(DEL==0)
printf("%-*s %s\n", length(key), "", files[n])
else
printf("\"%s\"\n", files[n])
}
}
K = K>0 ? K : 0
print "Total: " K " files"
}' |
tee >(
if [ "$del" -eq 1 ]
then
xargs rm -f
else
tee >/dev/null
fi
)
看看用Cygwin在Windows下的测试结果: (注意将列出的所有目录加单引号)
首先,我在E盘test目录下创建10个临时文件,文件都为空,测试结果如下:
hp@hp-PC ~
$ (cd 'E:/test'; i=0; while [ "$i" -lt 10 ] ; do mktemp "./XXXXXX" ; i=$((i+1)) ; done)
./pOsdFm
./5tndDZ
./wjSDR2
./oFrSaG
./7zlZcA
./9sNmEo
./UVDQLR
./qZdDNI
./iwfYdn
./IP52BK hp@hp-PC ~
$ ./dupfile.sh 'E:/test'
cygwin warning:
MS-DOS style path detected: E:/test
Preferred POSIX equivalent is: /cygdrive/e/test
CYGWIN environment variable option "nodosfilewarning" turns off this warning.
Consult the user's guide for more details about POSIX paths:
http://cygwin.com/cygwin-ug-net/using.html#using-pathnames
d41d8cd98f00b204e9800998ecf8427e E:/test/wjSDR2
E:/test/UVDQLR
E:/test/qZdDNI
E:/test/pOsdFm
E:/test/oFrSaG
E:/test/iwfYdn
E:/test/IP52BK
E:/test/9sNmEo
E:/test/7zlZcA
E:/test/5tndDZ
Total: 10 files hp@hp-PC ~
$
从结果中看出,已经完全的将10个重复的空文件完全查找出来。
使用-d选项,则可以删除重复的,只保留其中的一个。
hp@hp-PC ~
$ (cd 'E:/test' ; ls ;)
5tndDZ 7zlZcA 9sNmEo IP52BK iwfYdn oFrSaG pOsdFm qZdDNI UVDQLR wjSDR2 hp@hp-PC ~
$ ./dupfile.sh -d 'E:/test'
"E:/test/UVDQLR"
"E:/test/qZdDNI"
"E:/test/pOsdFm"
"E:/test/oFrSaG"
"E:/test/iwfYdn"
"E:/test/IP52BK"
"E:/test/9sNmEo"
"E:/test/7zlZcA"
"E:/test/5tndDZ"
Total: 9 files hp@hp-PC ~
$ (cd 'E:/test' ; ls ;)
wjSDR2
再测试一下不相同的情况,我在E:/test目录下,创建10个不相同的文件,每个文件内包含自己的文件名:
hp@hp-PC ~
$ (cd 'E:/test'; i=0; while [ "$i" -lt 10 ] ; do name=`mktemp "./XXXXXX"` ; echo "$name" > "$name" ; i=$((i+1)) ; done) hp@hp-PC ~
$ ./dupfile.sh 'E:/test'
26288eeea00c650ae612dcf5b0efa5ab E:/test/wBMk5c
6f28a86738b227553b116914befd7b55 E:/test/lOT1Yd
1bc7d2563796cf63c7f0d68affb190ef E:/test/haDFBY
679bc5d2d3e17761185ed82d43fd7d4a E:/test/CHOmSd
aaefa81fafd87bf3c3378ef02e22ef5a E:/test/yZ635B
e13b59ae07a9fd0e0a8095701c4003b2 E:/test/QdWtsN
ae2eabc1232111d9f12190b3a62d60bf E:/test/13PWOU
621d060c7c313f06eff1ce21a6b25f0c E:/test/sql7KV
7108830bbf019166d4af9a10030384f3 E:/test/lxGd5y
6dc2d2815a69f8a754503f1301e1cbcb E:/test/efUo7c
Total: 10 files
看到,列出的全部是不相同的文件,当文件数目比较多的时候,可以使用-s选项,静默输出,只列出重复的文件,不重复的文件则不列出。
最后,有个耐人寻味的问题,我在测试中发现,dupfile程序最后的管道末端的 tee >(....) 命令会莫名奇妙的终止,脚本的退出码为141,对应的是SIGPIPE信号。
我原先的脚本最后是这样写的:
tee >(
if [ "$del" -eq 1 ]
then
xargs rm -f
fi
)
分析得到,>( ... ) 这样的进程替换的实现原理是通过命名管道来做的,首先将 >( ... ) 此位置替换为 /dev/fd/63 或其他的命名管道文件,然后启动新的bash程序,来执行 >( ... ) 中的命令部分,而且bash程序的标准输入被替换为 /dev/fd/63 的管道输出端。而此时,为什么会触发SIGPIPE信号?
括号内部的命令部分是个判断语句,当判断为否定的时候,xargs命令不会执行,因此启动的bash程序就退出了,此时就造成,tee命令向 /dev/fd/63 这个管道端写的时候收到SIGPIPE信号,改正的做法是,要么保证 启动的bash程序不会退出,加一个else语句,tee >/dev/null ,要么就是忽略SIGPIPE信号,加上trap "" SIGPIPE就可以,同时将tee命令的stderr输出忽略 tee 2>/dev/null > ( ... )。
shell脚本实现查找文件夹下重复的文件,并提供删除功能的更多相关文章
- linux查找文件夹下的全部文件里是否含有某个字符串
查找文件夹下的全部文件里是否含有某个字符串 find .|xargs grep -ri "IBM" 查找文件夹下的全部文件里是否含有某个字符串,而且仅仅打印出文件名称 fin ...
- 用Java实现将多级文件夹下的所有文件统一放到一个文件夹中
每次下了电影(男生懂得呦),每部电影都放在一个单独的文件夹里,看的时候很是不方便啊,一直重复着进入文件夹.后退,再进.再退的操作,而手动把这些电影全部复制出来又太繁琐.因此为了解决这个问题,用IO写了 ...
- 【HDFS API编程】查看目标文件夹下的所有文件、递归查看目标文件夹下的所有文件
使用hadoop命令:hadoop fs -ls /hdfsapi/test 我们能够查看HDFS文件系统/hdfsapi/test目录下的所有文件信息 那么使用代码怎么写呢?直接先上代码:(这之后 ...
- 读取某文件夹下所有excel文件 python
import os import pandas as pd from sklearn import linear_model path = r'D:\新数据\每日收益率' filenames = os ...
- C/C++ 获取文件夹下的所有文件列表
本系列文章由 @yhl_leo 出品,转载请注明出处. 文章链接: http://blog.csdn.net/yhl_leo/article/details/51009608 提供一段C/C++代码示 ...
- GreenDao 数据库:使用Raw文件夹下的数据库文件以及数据库升级
一.使用Raw文件夹下的数据库文件 在使用GreenDao框架时,数据库和数据表都是根据生成的框架代码来自动创建的,从生成的DaoMaster中的OpenHelper类可以看出: public sta ...
- php 读取网页源码 , 导出成txt文件, 读取xls,读取文件夹下的所有文件的文件名
<?php // 读取网页源码$curl = curl_init();curl_setopt($curl, CURLOPT_URL, $url);curl_setopt($curl, CURLO ...
- C#实现把指定文件夹下的所有文件复制到指定路径下以及修改指定文件的后缀名
1.实现把指定文件夹下的所有文件复制到指定路径下 public static void copyFiles(string path) { DirectoryInfo dir = new Directo ...
- C/C++不同文件夹下包含头文件的方法及#include的使用
转自:http://blog.sina.com.cn/s/blog_6e0693f70100so42.html 本文主要介绍了如何不同文件夹下使用预处理器指示符#include. 假设我们有如下一个工 ...
随机推荐
- java中ExecutorService接口
一.声明 public interface ExecutorService extends Executor 位于java.util.concurrent包下 所有超级接口:Executor 所有已知 ...
- BZOJ 2693 jzptab
http://www.lydsy.com/JudgeOnline/problem.php?id=2693 题解: 考虑把lcm转化成gcd那答案就是然后神奇的设:就有:一样可以枚举 的取值,这是O(√ ...
- Protel 的自动推挤功能
大家都在用PROTEL99SE...有时候要修改布好的线...一条条的去拆,这样很麻烦.其实PROTEL99SE是有推挤功能的...虽然不是很强...但是可以给大家带来方便.下面我就发个自己制作的教程 ...
- Inno Setup for Windows service
Inno Setup for Windows service? up vote86down votefavorite 77 I have a .Net Windows service. I want ...
- OpenRTSP的使用
由于需要研究OpenRTSP的源码,所以先学习下使用. -d [time]--------这个是录制时间,就是单位秒,超时后,程序自动结束. -i -----------以.avi文件格式生成. ...
- 使用定制的ArrayAdapter制作ListView的Items(翻译)
Translated by:AcerWang 原文出自:customizing-android-listview-items-with-custom-arrayadapter 背景介绍 对于现 ...
- 黑马程序员_JavaIO流(四)
File概述 File类 用来将文件或者文件夹封装成对象. 方便对文件与文件夹的属性信息进行操作. File对象可以作为参数传递给流的构造函数. 了解File类中的常用方法. 字段:static St ...
- DPDK2.1开发者手册4-7
Mempool Labrary 一个内存池(memory pool)就是固定大小对象的分配器.在dpdk中,它是通过名字来标示唯一性的,且使用环形队列来保存没有使用的空闲对象.它提供了一些可选项服务例 ...
- linux mysql默认安装在哪个目录
MySQL安装完成后不象SQL Server默认安装在一个目录,它的数据库文件.配置文件和命令文件分别在不同的目录,了解这些目录非常重要,尤其对于Linux的初学者,因为 Linux本身的目录结构就比 ...
- POJ 3723 Conscription
Conscription Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 6325 Accepted: 2184 Desc ...