• 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  > ( ... )。



     


     

  • 相关阅读:
    LightOJ 1139 8 puzzle + hdu 1043 Eight A*
    hdu 1180 优先队列 + bfs
    hdu 1270
    HDU Doing Homework
    hdu 1171 Big Event in HDU
    hdu 3613 (KMP)回文串
    POJ 3461 Oulipo(KMP)
    POJ 1565(DP状态压缩)
    NYOJ 634 万里挑一(优先队列)
    职场手记1_你想成文什么样的人
  • 原文地址:https://www.cnblogs.com/aukle/p/3230815.html
Copyright © 2020-2023  润新知