自学内容网 自学内容网

21 Shell Script文件操作

Shell Script文件操作

一、遍历文件内容

一)for方法

​ 查看for循环帮助手册

[root@localhost ~]# help for
for: for NAME [in WORDS ... ] ; do COMMANDS; done
    Execute commands for each member in a list.
    
    The `for' loop executes a sequence of commands for each member in a
    list of items.  If `in WORDS ...;' is not present, then `in "$@"' is
    assumed.  For each element in WORDS, NAME is set to that element, and
    the COMMANDS are executed.
    
    Exit Status:
    Returns the status of the last command executed.
for ((: for (( exp1; exp2; exp3 )); do COMMANDS; done
    Arithmetic for loop.
    
    Equivalent to
    (( EXP1 ))
    while (( EXP2 )); do
    COMMANDS
    (( EXP3 ))
    done
    EXP1, EXP2, and EXP3 are arithmetic expressions.  If any expression is
    omitted, it behaves as if it evaluates to 1.
    
    Exit Status:
    Returns the status of the last command executed.

​ 创建test文件

[root@localhost ~]# vi test.txt
01 hello world
02 hello linux
03 hello every one
1.方法一

​ for便利循环有两种语法格式,我们先使用第一种方式

# 编写for in方式的脚本
[root@localhost ~]# vi for_in.sh
#!/bin/bash
num = 0
for line in `cat test.txt`; do
    echo $line
    let num++
done

# 执行结果
[root@localhost ~]# bash for_in.sh
01
hello
world
02
hello
linux
03
hello
every
one

​ 输出显示的确是循环遍历了文件

​ 文件中的行数据发生了切割

​ 遍历的元素数变成了10个

​ 因为:

​ 在bash在对接收到的字符串进程扩展的时候出发了词的拆分的扩展

​ for line in`cat file.txt`;do

​ 这行执行时:命令替换先进行,将文件内容扩展到该位置

​ 之后,bash先处发了词的拆分根据环境变量$IFS

​ $IFS中默认定义[空格/制表符/换行符]三个元素来切割字符串

​ 修改代码:

# 复制for_in
[root@localhost ~]# cp for_in.sh for_in_v2.sh

# 修改原脚本
[root@localhost ~]# vi for_in_v2.sh         
#!/bin/bash
ordifs=$IFS
num=0
IFS=$'\n'
for line in `cat test.txt`; do
    echo $line
    let num++
done
echo "文件总行数:$num"
IFS=$oldifs

# 查看执行结果
[root@localhost ~]# bash for_in_v2.sh 
01 hello world
02 hello linux
03 hello every one
文件总行数:3
2.方法二
# 编写for exp方式的脚本
[root@localhost ~]# vi for_exp.sh   
#!/bin/bash
count=`wc -l test.txt | cut -d' ' -f1`
for i in `seq $count`; do
    # 使用了head与tail组合模拟游标,获取最后一行
    line=`head -$i test.txt | tail -1`
    echo $line
done
echo "文件总行数:$count"

# 运行结果
[root@localhost ~]# bash for_exp.sh 
01 hello world
02 hello linux
03 hello every one
文件总行数:3

​ 也可以用for的另外一种方式

# 
[root@localhost ~]# vi for_exp_v2.sh
#!/bin/bash
count=`wc -l test.txt | cut -d' ' -f1`
for ((i=1;i<=count;i++)); do
    # 使用了head与tail组合模拟游标,获取最后一行
    line=`head -$i test.txt | tail -1`
    echo $line
done
echo "文件总行数:$count"

# 运行结果
[root@localhost ~]# bash for_exp_v2.sh 
01 hello world
02 hello linux
03 hello every one
文件总行数:3

二)while方法

​ 查看while循环帮助手册

[root@localhost ~]# help while
while: while COMMANDS; do COMMANDS; done
    Execute commands as long as a test succeeds.
    
    Expand and execute COMMANDS as long as the final command in the
    `while' COMMANDS has an exit status of zero.
    
    Exit Status:
    Returns the status of the last command executed.
1.方法一
# 创建file文件
[root@localhost ~]# touch file.txt

# 使用while
[root@localhost ~]# vi while_read.sh   
#!/bin/bash
num=0
while read line; do
    echo $line
    let num++
done < test.txt
echo "文件总行数:$num"

# 查看运行结果
[root@localhost ~]# bash while_read.sh 
01 hello world
02 hello linux
03 hello every one
文件总行数:3

​ 这里用到了重定向基于while开启文件输入流每次循环根据read命令的特征只取出一个换行符的内容也就是一行

[root@localhost ~]# vi while_read_v2.sh   
#!/bin/bash
# 添加一个test的输入文件描述符:3
exec 3< test.txt
num=0
while read line <& 3; do
    echo $line
    let num++
done
echo "文件总行数:$num"
# 删除文件描述符,3
exec 3<&-

# 查看运行结果
[root@localhost ~]# bash while_read_v2.sh 
01 hello world
02 hello linux
03 hello every one
文件总行数:3
2.方法二

​ 使用管道的实现方法

[root@localhost ~]# vi while_pipe.sh
#!/bin/bash
num=0
cat test.txt | while read line; do
    echo $line
    let num++
done
echo "文件总行数:$num"

# 运行结果
[root@localhost ~]# bash while_pipe.sh
01 hello world
02 hello linux
03 hello every one
文件总行数:0

​ 观察输出结果你会发现行数统计还是0

​ 这个是因为管道的特征造成的

​ 管道操作符两边会开启新的子shell来处理对应的命令

​ 所以num++的操作没有修改到父shell中的num

​ 这点在开发中一定要特别注意

​ 注意:补充

​ 有人一定会说对num做export

​ export定义的是导出而非共享

​ 父/子进程任意一方修改导出的变量都不会对对方产生影响

二、综合案例

一)案例需求

​ 遍历一个目录,目录下可能还有目录

​ 读取目录下的文本文件,找到每个文件中单词使用频率最多的单词。

​ 需求:

​ 列出每个文件使用频率最高的单词

​ 忽略单词的大小写

​ 如果是数字也理解为一个单词

​ 同时要打印出单词的最高使用次数

# 样板数据
[root@localhost ~]# tree ./
./
└── data
    ├── a
    │   └── test2.txt
    └── test1.txt

2 directories, 2 files

# 编辑test1
[root@localhost ~]# vi data/test1.txt 
01 hello world
02 hello linux
03 hello every one

# 编辑test2
[root@localhost ~]# vi data/a/test2.txt     
01 hello world
02 hello linux
03 hello every one

二)代码实现

1.处理单文件

​ 第一步:

​ 写一个脚本计算出一个文件的单词使用频率–将文本文件以一行一个单词的形式显示出来;

​ 将单词中的大写字母转化成小写字母,即Word和word认为一个单词;

​ 对单词进行排序;

​ 对排序好的单词列表统计每个单词出现的次数;

# 将大写转换为小写
[root@localhost ~]# vi process_file.sh
#!/bin/bash
cat $1 | sed 's/[A-Z]/\l&/g'

# 测试
[root@localhost ~]# bash process_file.sh data/test1.txt 
01 hello world
02 hello linux
03 hello every one

​ tr是sed命令的简化

# 使用tr代替sed命令,并将一个单词一行显示
[root@localhost ~]# vi process_file.sh
#!/bin/bash
cat $1 | tr "[A-Z]" "[a-z]" | tr -c "[a-z][0-9]" "\n"

# 测试
[root@localhost ~]# bash process_file.sh data/test1.txt 
01
hello
world
02
hello
linux
03
hello
every
one

​ 去重,并统计单词出现次数

# 去重,统计数量
[root@localhost ~]# vi process_file.sh
#!/bin/bash
cat $1 | tr "[A-Z]" "[a-z]" | tr -c "[a-z][0-9]" "\n" | sort | uniq -c

# 测试
[root@localhost ~]# bash process_file.sh data/test1.txt 
      1 01
      1 02
      1 03
      1 every
      3 hello
      1 linux
      1 one
      1 world

​ 输出结果,先按出现次数排序,次按字母排序

# 排序
[root@localhost ~]# vi process_file.sh
#!/bin/bash
cat $1 | tr "[A-Z]" "[a-z]" | tr -c "[a-z][0-9]" "\n" | sort | uniq -c | sort -k1nr -k2

# 测试
[root@localhost ~]# bash process_file.sh data/test1.txt 
      3 hello
      1 01
      1 02
      1 03
      1 every
      1 linux
      1 one
      1 world

# 只显示出现次数最多的
[root@localhost ~]# vi process_file.sh
#!/bin/bash
cat $1 | tr "[A-Z]" "[a-z]" | tr -c "[a-z][0-9]" "\n" | sort | uniq -c | sort -k1nr -k2 | head -1

# 测试
[root@localhost ~]# bash process_file.sh data/test1.txt 
      3 hello
2.遍历目录

​ 第二步:

​ 遍历整个目录,分别读取每个文件。

# 递归遍历目录
[root@localhost ~]# vi process_file.sh
#!/bin/bash
# 处理文件
function read_file()
{
    cat $1 | tr "[A-Z]" "[a-z]" | tr -c "[a-z][0-9]" "\n" | sort | uniq -c | sort -k1nr -k2 | head -1
}
# 遍历目录,使用递归
function read_dir()
{
    # $1表示目录
    for file in `ls $1`; do
        # 获取文件路径
        path=$1"/"$file
        # 判断是否是目录
        if [ -d $path ]; then
            read_dir $path
        else
            str=`read_file $path`
            count=`echo $str | awk '{print $1}'`
            word=`echo $str | awk '{print $2}'`
            echo "当前文件为:$path,出现次数最多的单词为:$word,出现次数为:$count"
        fi
    done
}
read_dir ./data

# 运行结果
[root@localhost ~]# bash process_file.sh
当前文件为:./data/a/test2.txt,出现次数最多的单词为:hello,出现次数为:3
当前文件为:./data/test1.txt,出现次数最多的单词为:hello,出现次数为:3

原文地址:https://blog.csdn.net/weixin_48089507/article/details/142983443

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!