• 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
    ", length(key), key, files[n])
    				for(n-- ;n>0 ; n--)
    				{
    					K++
    					if(DEL==0)
    						printf("%-*s  %s
    ", length(key), "", files[n])
    					else
    						printf(""%s"
    ", 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  > ( ... )。



     


     

  • 相关阅读:
    一行代码更改博客园皮肤
    fatal: refusing to merge unrelated histories
    使用 netcat 传输大文件
    linux 命令后台运行
    .net core 使用 Nlog 配置文件
    .net core 使用 Nlog 集成 exceptionless 配置文件
    Mysql不同字符串格式的连表查询
    Mongodb between 时间范围
    VS Code 使用 Debugger for Chrome 调试vue
    css权重说明
  • 原文地址:https://www.cnblogs.com/aukle/p/3230815.html
Copyright © 2020-2023  润新知