详解shell脚本(七)——玩转文件

批量重命名和移动文件

#!/bin/bash
#filename: rename.sh
#desc: rename .jpg & .png

count=1;
for img in `find . -iname '*.png' -o -iname '*.jpg' -type f -maxdepth 1`
do
    new=image-$count.${img##*.}

    echo "renaming $img to $new"
    mv "$img" "$new"
    let count++

done

rename 's/ /_/g' *
#把文件名中的空格替换为字符'_'

拼写检查与词典操作

#!/bin/bash
#文件名: checkword.sh
word=$1
grep "^$1$" /usr/share/dict/british-english -q
if [ $? -eq 0 ]; then
echo $word is a dictionary word;
else
echo $word is not a dictionary word;
fi

在grep中,^ 标记着单词的开始,$ 标记着单词的结束。-q 禁止产生任何输出。

并行进程加速命令

#!/bin/bash
PIDARRAY=()
for file in File1.iso File2.so
do
    md5sum $file &
    PIDARRAY+=("$!")
done
wait ${PIDARRAY[@]}

我们利用了Bash的操作符&,它使得shell将命令置于后台并继续执行脚本。这意味着一旦循环结束,脚本就会退出,而md5sum命令仍在后台运行。为了避免这种情况,我们使用!来获得进程的PID,在Bash中,!保存着最近一个后台进程的PID。我们这些PID放入数组,然后使用wait命令等待这些进程结束。

文本文件的交集与差集

comm A.txt B.txt -3 | sed 's/^\t//'
#A,B 文件的求差

在生成统一输出时,sed命令通过管道获取comm的输出。它删除行首的 \t字符。sed中的s表示替换(substitute)。/^\t/ 匹配行前的 \t(^是行首标记)。//(两个/操作符之间没有任何字符)是用来替换行首的\t的字符串。如此一来,就删除了所有行首的\t。

查找并删除重复文件

# !/bin/bash
# 文件名: remove_duplicates.sh
# 用途: 查找并删除重复文件,每一个文件只保留一份
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
}
};
size=$5; name1=name2;
}' | sort -u > duplicate_files

cat duplicate_files | xargs -I {} md5sum {} | sort | uniq -w 32 | awk '{ print"^"$2"$" }' | sort -u > duplicate_sample
echo Removing..
comm duplicate_files duplicate_sample -2 -3 | tee /dev/stderr | xargs rm
echo Removed duplicates files successfully.

ls -lS对当前目录下的所有文件按照文件大小进行排序,并列出文件的详细信息。awk读取ls -lS的输出,对行列进行比较,找出重复文件。

我们将文件依据大小排序并列出,这样大小接近的文件就会排列在一起。识别大小相同的文件是我们查找重复文件的第一步。接下来,计算这些文件的校验和。如果校验和相同,那么这些文件就是重复文件,将被删除。

第1行输出告诉我们文件数量,这个信息在本例中没什么用处。我们用getline读取第1行,然后丢弃。由于需要对每一行及其下一行来比对文件大小,因此用getline读取长文件列表的第一行,并存储文件名和大小(它们分别是第8列和第5列)。这样我们就先得到了一行。接下来,awk进入{}语句块(在这个语句块中读取余下的文本行),读取到的每一行文本都会执行该语句块。它将当前行中读取到的文件大小与之前存储在变量size中的值进行比较。如果相等,那就意味着两个文件至少在大小上是相同的,随后再用md5sum执行进一步的检查。

在awk中,外部命令的输出可以用下面的方法读取:

“cmd”| getline

随后就可以在0中获取命令的输出,在1,2,…n中获取命令输出中的每一列。我们将文件的md5sum保存在变量csum1和csum2中。变量name1和name2保存文件列表中位置连续的文件名。如果两个文件的校验和相同,那它们肯定是重复文件,其文件名会被打印出来。

我们需要从每组重复文件中找出一个文件,这样就可以删除其他副本了。计算重复文件的md5sum,从每一组重复文件中打印出其中一个。这是通过-w 32比较每一行的md5sum(md5sum输出中的前32个字符,md5sum的输出通常由32个字符的散列值和文件名组成),然后找出那些不相同的行。这样,每组重复文件中的一个采样就被写入duplicate_sample。

现在需要将duplicate_files中列出的、且未包含在duplicate_sample之内的全部文件删除。这些文件由comm命令负责打印出来。我们可以使用差集操作来实现(参考3.3节)。

comm通常只接受排序过的文件。所以,在重定向到duplicate_files和duplicate_sample之前,首先用sort -u作为一个过滤器。

tee命令在这里有一个妙用:它在将文件名传递给rm命令的同时,也起到了print的作用。tee将来自stdin的行写入文件,同时将其发送到stdout。我们也可以将文本重定向到stderr来实现终端打印功能。/dev/stderr是对应于stderr(标准错误)的设备。通过重定向到stderr设备文件,来自stdin的文本将会以标准错误的形式出现在终端中。

charttr 设置文件

chattr +i file
#文件设置为不可修改

文件统计信息

#! /bin/bash

if [ $# -ne 1];
then
    echo "Usage is $0 basepath";
    exit
fi
path=$1
declare -A statarray;
while read line;
do 
    ftype=`file -b "$line" | cut -d, -f1`
    let statarray["$ftype"]++;
done < < (find $path -type f -print)

echo ===============File types and counts ================
for ftype in "$(!statarray[@])";
do 
    echo $ftype : ${statarrat["$ftype"]}
done

在脚本中声明了一个关联数组statarray,这样可以用文件类型作为数组索引,将每种文件类型的数量存入数组。每次遇到一个文件类型,就用let增加计数。find命令以递归的方式获取文件路径列表。脚本中的ftype=’file -b “$line”’使用file命令获得文件类型信息。选项-b告诉file命令只打印文件类型(不包括文件名)。输出的文件类型信息包含很多细节,比如图像编码以及分辨率(如果是图像文件的话)。对于这些细节我们并不感兴趣,我们只需要基本的信息就够了。各种细节信息是由逗号分隔的,例如:

$ file a.out -b

ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

我们只需要从上面这些细节中提取ELF 32-bit LSB executable。因此我们使用cut -d,-f1,指明以逗号作为定界符,并且只打印第一个字段。

done<<(find $path –type f –print);是一段很重要的代码。它的执行逻辑如下:

while read line;

do something

done < filename

我们不用filename,而是用find命令的输出。

<(find $path -type f -print)等同于文件名。只不过它用子进程输出来代替文件名。注意,第一个<用于输入重定向,第二个<用于将子进程的输出装换成文件名。在两个<之间有一个空格,避免shell将其解释为<<操作符。

${!statarray[@]}用于返回一个数组索引列表。

diff 生成目录的差异信息

diff -Naur directory1 directory2

-N: 将所有确实的文件视为空文件

-a: 将所有文件视为文本文件

-u:生成一体化输出

-r: 遍历目录下的所有文件

tail 终端持续更新

tail -f growing_file
tail -f /var/log/messages
dmsg | tail -f
#假设我们正在读取一个不断增长的文件,进程Foo一直在向该文件追加数据,那么tail -f 就会一直执行到进程Foo结束
PID=$(pidof Foo)
tail -f file --pid $PID

我们经常会运行dmeg 查看内核的环形缓冲区消息,要么是调试USB设备,要么是查看sdX(X是对应于SCSI磁盘的sd设备的次序列号)。tail -f也可以加入一个睡眠间隔 -s,这样我们就可以设置监视文件更新的时间间隔。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值