Bash脚本之文件操作

本文对应《Linux Shell脚本攻略》第三章


概述

Unix将操作系统的一切都视为文件;所有操作都离不开文件,可以利用它们进行各种与系统或进程向相关的处理工作


生成任意大小的文件

创建指定大小文件最简单的方法就是利用dd命令

其中,if代表输入文件(input file),缺省表示标准输入;of代表输出文件(output file),缺省表示标准输出;bs代表以字节为单位的块大小(block size),count代表需要被复制的块数

dd if=/dev/hdb of=/dev/hdd  #将本地的/dev/hdb整盘备份到/dev/hdd
dd if=/dev/zero of=junk.data bs=1M count=1  #创建一个大小为1MB的文件junk.data
dd if=/dev/sth of=wtf.log bs=2MB count=3    #总文件大小为2*3=6MB

以上,/dev/zero是一个字符设备,它会不断返回0值字节(\0)

另外,可以使用dd命令传输大量数据并观察命令输出来测量内存的操作速度

ddcp的区别(本段来自网络):
前者是块级别的拷贝;后者是文件目录级别的拷贝——dd将原始数据(raw data)按照数据源的格式原封不动的拷贝到目的地;cp将文件和目录拷贝到目的地后按照目的地的格式排列新数据,对于不能以文件或目录格式呈现的数据(如引导启动块的数据),cp无能为力


文件的交集和差集

类似数学上的交集(intersection)、差集(set difference),文件也有这样的概念

使用comm命令可以用于两个文件之间的比较

在实战前,要明确的几个基本概念:
1)交集:打印出两个文件所共有的行
2)求差:打印出指定文件所包含的且互不相同的那些行
3)差集:打印出包含在文件A中,但是不包含在其它指定文件中的那些行

例如:文件A内容{1, 2, 3},文件B内容{3, 4, 5}
A和B的:交集3;求差1,2,4,5;差集:1,2

实例:文件A是

apple
orange
gold
silver
steel
iron

文件B是

orange
gold
cookies
carrot

使用comm命令前务必对输入文件进行sort排序

sort A.txt -o A.txt; sort B.txt -o B.txt

1)执行不带任何参数的comm

comm A.txt B.txt

打印内容如下:

apple
        carrot
        cookies
                gold
iron            
                orange
sliver          
steel

解释:要看懂comm命令的输出要一列一列的看;第一列包只在A中出现的行(差集),第二行包含只在B中出现的行(类似差集),第三行包行A和B中相同的行;列与列之间自动用制表符\t作为定界符隔开

2)只打印交集
只打印交集就是只打印第三行

comm A.txt B.txt -1 -2

输出:

gold
orange

解释:参数-n表示不打印第n列(从输出中删除)

3)只打印两个文件中不同的行
有了2中解释,相比已经很清楚了

comm A.txt B.txt -3

解释:参数-3表示不打印第三行,即不打印相同的行

4)通过删除不需要的列,同样可以得到关于A的差集,和关于B的差集

comm A.txt B.txt -2 -3  #删除第二列和第三列
comm A.txt B.txt -1 -3  #删除第一列和第三列

解释:见前面

5)格式化输出

comm A.txt B.txt -3 | sed 's/^\t//'

输出:

apple
carrot
cookies
iron
silver
steel

解释:『sed』是一个“非交互式的”面向字符流的编辑器;能同时处理多个文件多行的内容,可以不对原文件改动,把整个文件输入到屏幕,可以把只匹配到模式的内容输入到屏幕上;还可以对原文件改动,但是不会再屏幕上返回结果

补充:sed命令格式:sed [option] 'sed command'filename
sed脚本格式:sed [option] -f 'sed script'filename
(作为脚本书写时多加一个-f,二者本质一样都是sed命令

另,sed命令支持正则表达式(参考本例)

sed命令的参数选项:
-n:只打印模式匹配的项
-e:直接在命令行模式上进行sed动作编辑(-e参数时默认选项)
-f:将sed的动作写在一个文件内,用-f filename执行filename内的动作
-r:支持扩展表达式
-i:直接修改文件内容


查找并删除重复文件


背景知识:校验和简介

所谓校验和(checksum)是在数据处理和数据通信领域中,用于校验目的地一组数据项的和;它通常是以十六进制为数制表示的形式;如果校验和的数值超过十六进制的FF,也就是255. 就要求其补码作为校验和;通常用来在通信中,尤其是远距离通信中保证数据的完整性和准确性

校验和和根据文件内容计算的,和文件名没有关系

思路概要

考虑到校验和是依据文件的实际内容来计算的,内容相同的文件生成相同的校验和,所以我们可以通过比较校验和来删除内容重复的文件(千万不要简单的使用模式匹配文件名,要知道文件名是写给人看的便于区分而已,而文件的实际内容内容才是划分不同文件的唯一依据)

代码部分

#!/bin/bash
#filename: remove_duplicates.sh
#usage: search and remove the duplicated files

ls -lS  --time-style=long-iso | awk 'BEGIN{
    getline; getline;
    name1=$8; size=$5
}
{
    name2=$8;
    if(size==$5)
    {
        "md5sum" name1 | getline; csum1=$1;
        "md5sum" name2 | getline; csum2=$1;
        if(csum1==csum2)
        {
            print name1;print name2
        }
    };    #注意下,bash脚本中if语句的if分句后面要跟一个分号(表示话没说完)
    size=$5;name1=name2
}' | sort -u > duplicate_files

cat duplicate_files | xargs -I {} | sort | using -w 32 | awk '{print "^"$2"$"}' | sort -u > duplicate_sample

echo Removing...
comm duplicate_files duplicates_sample -2 -3 | tee /dev/stderr | xargs rm
echo Removed duplicates files successfully.

原理及说明

ls -lS对当前目录下的所有文件按照文件大小进行排序,并列出文件的详细信息,接着通过管道把数据作为输入传递给awk;getline读取第一行并丢弃,接着的另一个geline用来读取文件列表的第一行,并存储文件名和大小(分别存储在第8、5列)

我们先将文件根据大小排序,这样大小接近的文件会排列在一起,识别大小相同的文件是查找重复文件的第一步(初步筛选,可以缩小查找范围),接下来,计算这些文件的校验和,如果校验和也相同就是同一个文件,将被删除

在读取文本行之前,先执行awk的BEGIN{}语句块(语句块优先,且语句块可以多层嵌套)

真正的读取文本行的工作发生在{}语句块中;在读取并处理完所有的文本行之后,执行{}END语句块(顺序执行)

其中,awk工具的getline命令用法如下:
case1)当getline左右没有重定向符|<时,getline读去当前文件的第一行并将数据保存到变量中,如果没有变量,则数据保存到$0中;由于awk在处理getline之前已经读入了一行,所以getline得到的返回结果是隔行的
case2)当getline左右有重定向符|<时,getline作用于定向输入文件,由于该文件是刚打开,awk并没有读入一行数据,而getline读入了一行数据,那么getline返回的是该文件的第一行,而不是隔行

另外,上面用到了一个校验和工具md5sum
广为使用的两个校验和技术(工具)是:md5sumSHA-1,它们对文件的完整性使用相应的算法来生成校验和

md5sum filename
#示例输出:8b329da9893e43099c7d8a5cb9c940 filename

如上所示,md5sum是一个32位字符的十六进制串

看awk后面包裹的外层语句块,之后awk进入{}语句块(在这个语句块中读取剩余的文本行——本文操作的基本单位都是行,即一行一行的进行读取),读取到的每一行都会执行该语句块,它将当前行中读取到的文件大小与之前存储在变量size中的值进行比较,如果相等,就意味着两个文件至少在大小上是相等的(之后再用md5sum做进一步验证)

这里有一个小技巧——
在awk中,外部命令的输出可以通过以下方法读取:

"cmd" | getline     #记住,getline和awk是密切相关的

随后就可以在默认变量$0中获取命令的输出,在$1、$2、$3……中获取命令输出的每一列
上面,我们将文件的md5sum保存在变量csum1和吃csum2中,变量name1和name2保存文件列表中位置连续的文件名


使用环回文件

环回(lookback)文件系统是Linux系统中非常有趣的部分;我们通常是在设备上(例如磁盘分区)创建文件系统;这些存储设备能够以设备文件的形式来使用,比如/dev/device_name

为了使用存储在设备上的文件系统,我们需要将其挂在到一些被称为挂载点(mount point)的目录上;环回文件系统是指那些在文件中而非物理设备中创建的文件系统,我们可以将这些文件作为文件系统挂载到挂载点上,这实际上可以让我们在物理磁盘上的文件中创建逻辑磁盘

mkfs命令:创建文件系统(make filesystem)命令,用来在特定的分区建立Linux文件系统,可以看作是一个格式化工具
建立文件系统过程的实质是在磁盘空间上面建立文件系统所需的结构,根据文件系统设计在磁盘空间上写入关于文件系统的数据(称之为文件系统元数据),基于这些数据实现对文件系统的管理和进行相关的操作

mount命令:挂载命令,在参数顺序上先是挂载文件,后是挂载点(目录):

mount -o loop loopbackfile.img .mnt/lookback

这条命令实际上是一种快捷的挂载方法,我们无需手动连接任何设备

在内部,这个环回文件连接到了一个名为/dev/loop1或loop2的设备上

也可以手动完成挂载:

losetup /dev/loop1 loopbackfile.img
mount /dev/loop1 /mnt/loopback

要卸载使用下面的命令(注意umount不是前缀un):

umount mount_point

以上,mountumount命令都是特权命令,所以必须以root用户身份执行

========================================================
下一章讲解命令行下的『网络』相关知识

你可能感兴趣的:(脚本编程)