如何在Linux中使用Shell脚本查找和删除重复文件

时间:2020-01-09 10:38:24  来源:igfitidea点击:

如何在Linux或者Unix中删除重复文件。
如何在Linux中使用Shell脚本查找重复文件。
使用Shell脚本列出Linux中的重复文件。
Linux通过名称和哈希值查找重复的文件。
自动重复文件删除器。

Shell脚本来删除重复的文件示例。

脚本1:使用Shell脚本查找重复文件

这可以帮助将用户确定为他/她希望删除的重复文件中的哪个文件。
例如/etc/hosts/etc/hosts.bak是同一个文件,因此脚本将无法理解哪个文件很重要,并且可能会删除/etc/hosts并将其视为重复文件。

以下是在删除文件之前通过提示删除重复文件的示例脚本:

#!/bin/bash
# Find duplicate files using shell script
# Remove duplicate files interactively
TMP_FILE=$(mktemp /tmp/temp_file.XXX)
DUP_FILE=$(mktemp /tmp/dup_file.XXX)
function add_file() {
  # Store the hash of the file if not already added
  echo "" ""  >> $TMP_FILE
}
function red () {
   # print colored output
   /bin/echo -e "\e[01;31m\e[0m" 1>&2
}
function del_file() {
    # Delete the duplicate file
    rm -f "" 2>/dev/null
    [[ $? == 0 ]] && red "File \"\" deleted"
}
function srch_file() {
  # Store the filename in this variable
  local NEW=""
  # Before we check hash value of file, make this variable null
  local SUM="0"
  # If file exists and the temporary file's size is zero
  if [ -f $NEW ] && [ ! -s $TMP_FILE ];then
     # Print Store the hash value of file. This value will be later stored in RET which is further used to check duplicate file
     echo $(sha512sum ${NEW} | awk -F' ' '{print }')
     # Exit the loop here
     return
  fi
  # If the size of temporary file is non-zero read temporary file line by line in a loop. Each line is stored in ELEMENT variable
  while read ELEMENT; do
    # Get the hash value of input file
    SUM=$(sha512sum ${NEW} | awk -F' ' '{print }')
    # Get the hash value of file collected from temporary file
    ELEMENT_SUM=$(echo $ELEMENT | awk -F' ' '{print }')
    ELEMENT_FILENAME=$(echo $ELEMENT | awk -F' ' '{print }')
    # if the hash value is same means we have found a duplicate file
    if [ "${SUM}" == "${ELEMENT_SUM}" ];then
       echo $ELEMENT_FILENAME > $DUP_FILE
       return 1
    else
       continue
    fi
  done<$TMP_FILE
  # If duplicate file is not found then just print the hash value of the file
  echo "${SUM}"
}

function begin_search_and_deduplication {
  local DIR_TO_SRCH=""
  for FILE in `find ${DIR_TO_SRCH} -type f`; do
     # this stores the return value from srch_file function
     RET=`srch_file ${FILE}`
     if [[ "${RET}" == "" ]];then
         FILE1=`cat $DUP_FILE`
         echo "$FILE1 is a duplicate of $FILE"
         while true; do
            read -rp "Which file you wish to delete? $FILE1 (or) $FILE: " ANS
            if [ $ANS == "$FILE1" ];then
               del_file $FILE1
               break
            elif [ $ANS == "$FILE" ];then
               del_file $FILE
               break
            fi
         done
         continue
     elif [[ "${RET}" == "0" ]];then
          continue
     elif [[ "${RET}" == "1" ]];then
          continue
     else
          # If the file hash is not found then it's entry is added in temporary file
          add_file "${RET}" ${FILE}
          continue
      fi
  done
}
# This will read the user input to collect list of directories to search for duplicate files
echo "Enter directory name to search: "
echo "Press [ENTER] when ready"
echo
read DIR
begin_search_and_deduplication "${DIR}"
# Delete the temporary files once done
rm -f $TMP_FILE
rm -f $DUP_FILE

我们可以执行脚本,该脚本将打印出我们要检查的目录列表,并删除Linux中的重复文件。
为了本文的目的,我创建了一些具有重复内容的文件。

# /tmp/remove_duplicate_files.sh
Enter directory name to search:
Press [ENTER] when ready
/dir1 /dir2 /dir3 <-- This is my input (search duplicate files in these directories)
/dir1/file1 is a duplicate of /dir1/file2
Which file you wish to delete? /dir1/file1 (or) /dir1/file2: /dir1/file2
File "/dir1/file2" deleted
/dir1/file1 is a duplicate of /dir2/file101
Which file you wish to delete? /dir1/file1 (or) /dir2/file101: /dir2/file101
File "/dir2/file101" deleted

如我们所见,在提供的目录中找到重复文件时,脚本等待用户输入。
根据用户输入,将继续进行下一步。

怎么运行的?

  • 我已在本节的大部分内容之前添加了注释,这些注释可以了解脚本如何删除重复的文件。

  • 使用此哈希,我们可以将哈希与已计算的哈希列表进行比较。

  • 如果匹配,则我们之前已经看过该文件的内容,因此可以删除它。

  • 如果哈希值是新的,我们可以记录该条目并继续计算下一个文件的哈希值,直到所有文件都被哈希化为止。

现在,如果我们希望脚本自动查找并删除重复文件,则可以删除上面脚本中突出显示的块,只需使用del_file $FILE1,它将直接删除重复文件(如果找到)

脚本2:使用Shell脚本删除重复文件

其中我们将使用awk通过Shell脚本查找重复文件。

此代码将在目录中找到同一文件的副本,并删除该文件的一个副本以外的所有副本。

#!/bin/bash
# Filename: remove_duplicate.sh
# Description: Find and remove duplicate files and
# keep one sample of each file.
ls -lS --time-style=long-iso | awk 'BEGIN {
  getline; getline;
  name1=; size=
}
{
  name2=;
  if (size==)
{
  "md5sum "name1 | getline; csum1=;
  "md5sum "name2 | getline; csum2=;
  if ( csum1==csum2 )
  {
     print name1; print name2
  }
};
size=; name1=name2;
}' | sort -u > duplicate_files
cat duplicate_files | xargs -I {} md5sum {} | sort | uniq -w 32 | awk '{ print  }' | sort -u > unique_files
echo Removing..
comm duplicate_files unique_files -3 | tee /dev/stderr | xargs rm
echo Removed duplicates files successfully.

我们必须在要查找和删除重复文件的目录内导航,然后执行脚本。
其中我想使用/dir1内的shell脚本查找重复的文件,因此我将使用cd/dir1然后执行不带任何参数的脚本

[root@centos-8 dir1]# /tmp/remove_duplicate.sh
Removing..
file1_copy
file2_copy
Removed duplicates files successfully.

在"/dir1"下列出文件,并确保此处没有重复的文件。
创建了更多文件,供我们参考。

[root@centos-8 dir1]# ls -l
total 20
-rw-r--r-- 1 root root 34 Jan  9 07:01 duplicate_files
-rw-r--r-- 1 root root 16 Jan  9 06:56 duplicate_sample
-rw-r--r-- 1 root root  5 Jan  9 07:00 file1
-rw-r--r-- 1 root root  6 Jan  9 07:00 file2
-rw-r--r-- 1 root root 12 Jan  9 07:01 unique_files

怎么运行的?

ls -ls列出了当前文件夹中文件的详细信息,并按文件大小排序。
--time-style = long-iso选项告诉ls以ISO格式打印日期。
awk读取ls-lS的输出,并使用shell脚本对输入文本的列和行进行比较,以找到重复的文件。

使用Shell脚本查找重复文件的代码背后的逻辑如下:

  • 我们列出了按大小排序的文件,因此相同大小的文件将相邻。
    查找相同文件的第一步是查找具有相同大小的文件。
    接下来,我们计算文件的校验和。
    如果校验和匹配,则文件是重复的,并且一组重复项将被删除。

  • 在主处理之前执行awk的BEGIN {}块。
    它读取"总计"行并初始化变量。
    awk读取并处理ls输出的其余部分时,大部分处理将在{}块中进行。
    读取所有输入后,将执行END {}块语句。

  • 在" BEGIN"块中,我们读取第一行并存储名称和大小(它们分别是第八和第五列)。
    当awk进入{}块时,其余的行将被一一读取。
    该块将从当前行获得的大小与先前存储在size变量中的大小进行比较。
    如果它们相等,则意味着两个文件在大小上是重复的,必须由md5sum进一步检查。

  • 读完该行后,整行位于$0中,每一列都位于$1,$2,...,$n中。
    其中我们将文件的md5sum校验和读入csum1和csum2变量。
    " name1"和" name2"变量存储连续的文件名。
    如果两个文件的校验和相同,则确认它们是重复的并被打印。

  • 我们计算重复项的md5sum值,并通过查找唯一的行,从每组重复项中打印一个文件,并使用-w 32比较每行中的md5sum(在md5sum输出中的前32个字符;通常,则md5sum输出由32个字符的哈希值和文件名组成)。
    每组重复项中的一个示例被写入unique_files中。

  • 现在,我们需要删除duplicate_files中列出的文件,不包括unique_files中列出的文件。
    comm命令在duplicate_files中打印文件,但不在unique_files中打印文件。

  • comm仅处理排序的输入。
    因此,sort -u用于过滤duplicate_filesunique_files

  • tee命令用于将文件名传递给rm命令以及打印。
    tee命令将其输入发送到stdout和一个文件。
    我们还可以通过重定向到stderr将文本打印到终端上。
    /dev/stderr是与stderr对应的设备(标准错误)。
    通过重定向到" stderr"设备文件,发送到" stdin"的文本将作为标准错误打印在终端中。