您的位置:新葡亰496net > 电脑系统 > 如何解决read命令产生的硬编码问题,read命令产生

如何解决read命令产生的硬编码问题,read命令产生

发布时间:2019-08-03 20:45编辑:电脑系统浏览(138)

    情景

    我们知道,read命令可以读取文件内容,并把内容赋值给变量。

    以如下的数据文件为例。

    $ cat data.txt
    
    1 201623210021 wangzhiguo 25
    2 201623210022 yangjiangbo 26
    3 201623210023 yangzhen 24
    4 201623210024 wangdong 23
    5 201623210025 songdong 25
    

    以上文件的四列分别为序号(index)、学号(number)、姓名(name)、年龄(age)。用shell脚本读取该文件并输出每行的值:

    $ cat read_data.sh
    
    #!/bin/bash
    
    cat data.txt | while read index number name age
    do
        echo "index:${index}"
        echo "number:${number}"
        echo "name:${name}"
        echo "age:${age}"
        echo " "
    done
    

    执行脚本,查看结果:

    $ sh read_data.sh 
    
    index:1
    number:201623210021
    name:wangzhiguo
    age:25
    
    index:2
    number:201623210022
    name:yangjiangbo
    age:26
    
    index:3
    number:201623210023
    name:yangzhen
    age:24
    
    index:4
    number:201623210024
    name:wangdong
    age:23
    
    index:5
    number:201623210025
    name:songdong
    age:25
    

    不知你发现没有,这样的实现方式有着明显的弊端:

    1. 列名(read index number name age)显式地在代码中指定,如果只是想弄清楚数据文件的每列含义,则需要阅读脚本;
    2. 该脚本中指明了每列的名称,如果希望修改各字段的英文名称(比如序号的英文名称希望改为NUMBER)则需要修改脚本,且修改多处;
    3. 该脚本按一定顺序读取数据文件,因此,如果数据文件中的列顺序发生了变化,则依然需要修改脚本;
    4. 如果有其他数据文件需要按此方式读取,则需要根据数据文件的实际情况再重写一个新脚本;

    上述实现方式虽然看起来简单,但基于上述的弊端,我们还应对其进行优化。

    情景

    我们知道,read命令可以读取文件内容,并把内容赋值给变量。

    以如下的数据文件为例。

    $ cat data.txt
    
    1 201623210021 wangzhiguo 25
    2 201623210022 yangjiangbo 26
    3 201623210023 yangzhen 24
    4 201623210024 wangdong 23
    5 201623210025 songdong 25
    

    以上文件的四列分别为序号(index)、学号(number)、姓名(name)、年龄(age)。用shell脚本读取该文件并输出每行的值:

    $ cat read_data.sh
    
    #!/bin/bash
    
    cat data.txt | while read index number name age
    do
        echo "index:${index}"
        echo "number:${number}"
        echo "name:${name}"
        echo "age:${age}"
        echo " "
    done
    

    执行脚本,查看结果:

    $ sh read_data.sh 
    
    index:1
    number:201623210021
    name:wangzhiguo
    age:25
    
    index:2
    number:201623210022
    name:yangjiangbo
    age:26
    
    index:3
    number:201623210023
    name:yangzhen
    age:24
    
    index:4
    number:201623210024
    name:wangdong
    age:23
    
    index:5
    number:201623210025
    name:songdong
    age:25
    

    不知你发现没有,这样的实现方式有着明显的弊端:

    1. 列名(read index number name age)显式地在代码中指定,如果只是想弄清楚数据文件的每列含义,则需要阅读脚本;
    2. 该脚本中指明了每列的名称,如果希望修改各字段的英文名称(比如序号的英文名称希望改为NUMBER)则需要修改脚本,且修改多处;
    3. 该脚本按一定顺序读取数据文件,因此,如果数据文件中的列顺序发生了变化,则依然需要修改脚本;
    4. 如果有其他数据文件需要按此方式读取,则需要根据数据文件的实际情况再重写一个新脚本;

    上述实现方式虽然看起来简单,但基于上述的弊端,我们还应对其进行优化。

    a. 准备工作 marks.txt 本文件有4列

    1)    Amit     Physics    80
    2)    Rahul    Maths      90
    3)    Shyam    Biology    87
    4)    Kedar    English    85
    5)    Hari     History    89
    

    marks2.txt 本文件有部分列有5列

    1)    Andy     Physics    80
    2)    Jacky    Maths      90    10
    3)    Hill    Biology    87
    4)    John    English    85 12
    5)    Mary     History
    

    方案

    解决的根本应该是写尽可能通用的脚本,不依赖数据文件本身的列数、列顺序、列名称(含义)等。

    可以将数据文件的各字段名称存于该数据文件的首行。当读取数据文件时,首先读取数据文件的首行,以获取各字段名称的列表;读取其它行时,将首行的值与非首行的值进行一一对应即可。

    数据文件

    $ cat new_data.txt 
    
    index number name age
    1 201623210021 wangzhiguo 25
    2 201623210022 yangjiangbo 26
    3 201623210023 yangzhen 24
    4 201623210024 wangdong 23
    5 201623210025 songdong 25
    

    脚本

    $ cat new_read_data.sh
    
    #!/bin/bash
    
    # 读取文件头行,存于一个数组中
    tablehead=(`head -n 1 new_data.txt`)
    
    # 从文件第二行开始读取,按上述数组顺序读取各字段
    tail -n  2 new_data.txt | while read ${tablehead[*]}
    do
        # 遍历数组的下标,获取tablehead数组的对应值,以及以该值命名的变量的值
        for i in `seq 0 $((${#tablehead[@]}-1))`
        do
            temp=${tablehead[$i]}
            echo "${temp}:${!temp}"
        done
        echo ""
    done
    

    结果

    $ sh new_read_data.sh 
    
    index:1
    number:201623210021
    name:wangzhiguo
    age:25
    
    index:2
    number:201623210022
    name:yangjiangbo
    age:26
    
    index:3
    number:201623210023
    name:yangzhen
    age:24
    
    index:4
    number:201623210024
    name:wangdong
    age:23
    
    index:5
    number:201623210025
    name:songdong
    age:25
    

    要写出更通用的脚本,还可以做一些判断和处理,比如:数据文件作为参数传入、检查数据文件的行数、检查数据文件的列数,等等。

    方案

    解决的根本应该是写尽可能通用的脚本,不依赖数据文件本身的列数、列顺序、列名称(含义)等。

    可以将数据文件的各字段名称存于该数据文件的首行。当读取数据文件时,首先读取数据文件的首行,以获取各字段名称的列表;读取其它行时,将首行的值与非首行的值进行一一对应即可。

    数据文件

    $ cat new_data.txt 
    
    index number name age
    1 201623210021 wangzhiguo 25
    2 201623210022 yangjiangbo 26
    3 201623210023 yangzhen 24
    4 201623210024 wangdong 23
    5 201623210025 songdong 25
    

    脚本

    $ cat new_read_data.sh
    
    #!/bin/bash
    
    # 读取文件头行,存于一个数组中
    tablehead=(`head -n 1 new_data.txt`)
    
    # 从文件第二行开始读取,按上述数组顺序读取各字段
    tail -n  2 new_data.txt | while read ${tablehead[*]}
    do
        # 遍历数组的下标,获取tablehead数组的对应值,以及以该值命名的变量的值
        for i in `seq 0 $((${#tablehead[@]}-1))`
        do
            temp=${tablehead[$i]}
            echo "${temp}:${!temp}"
        done
        echo ""
    done
    

    结果

    $ sh new_read_data.sh 
    
    index:1
    number:201623210021
    name:wangzhiguo
    age:25
    
    index:2
    number:201623210022
    name:yangjiangbo
    age:26
    
    index:3
    number:201623210023
    name:yangzhen
    age:24
    
    index:4
    number:201623210024
    name:wangdong
    age:23
    
    index:5
    number:201623210025
    name:songdong
    age:25
    

    要写出更通用的脚本,还可以做一些判断和处理,比如:数据文件作为参数传入、检查数据文件的行数、检查数据文件的列数,等等。

    b. 样例

    • (1).文本加标题行 "序号 姓名 科目 分数" 显示:
    >awk 'BEGIN {printf "序号t姓名t科目t分数n"} {print}' marks.txt 
    
    序号  姓名  科目  分数
    1)    Amit     Physics    80
    2)    Rahul    Maths      90
    3)    Shyam    Biology    87
    4)    Kedar    English    85
    5)    Hari     History    89
    
    • (2).执行awk脚本:将单引号内的脚本写到一个文件,然后通过 -f 引用文件执行:
    > cat command.awk 
    BEGIN {printf "序号t姓名t科目t分数n"} {print}
    
    > awk -f command.awk marks.txt
    序号  姓名  科目  分数
    1)    Amit     Physics    80
    2)    Rahul    Maths      90
    3)    Shyam    Biology    87
    4)    Kedar    English    85
    5)    Hari     History    89
    
    • (3). -v声明一个变量的值,BEGIN前分配
    > awk -v user=root 'BEGIN{printf "name=%sn", user}' `
    name=root
    
    • (4). 计算: - * / %
    > awk 'BEGIN {a=52; b=50; print "a%b=", (a%b)}'  `
    a%b= 2
    
    • (5). BEGIN END 定义变量a,在处理行的过程中增加(可以与行内的内容进行计算),END时输出:(计算总分数)
    >awk 'BEGIN { sumScore = 0; printf "总分数sumScore = %dn", a } {sumScore=sumScore $NF;print} END{print ">总分数sumScore=",sumScore}' marks.txt`
    总分数sumScore = 0
    1)    Amit     Physics    80
    2)    Rahul    Maths      90
    3)    Shyam    Biology    87
    4)    Kedar    English    85
    5)    Hari     History    89
    >总分数sumScore= 431
    

    新葡亰496net 1

    疲劳的分割线


    如何解决read命令产生的硬编码问题,read命令产生硬编码问题解决。扩展知识

    从脚本的改进上看,比原脚本略显复杂,但却更加通用了。
    如果觉得阅读脚本吃力,可以有针对性地学习下,尤其是以下知识点:

    • 新葡亰496net,数组的相关知识:数组长度、数组内容、数组元素等
    • 变量${abc}${!abc}的区别

    扩展知识

    从脚本的改进上看,比原脚本略显复杂,但却更加通用了。
    如果觉得阅读脚本吃力,可以有针对性地学习下,尤其是以下知识点:

    • 数组的相关知识:数组长度、数组内容、数组元素等
    • 变量${abc}${!abc}的区别

    本文永久更新链接地址:http://www.linuxidc.com/Linux/2017-06/145163.htm

    新葡亰496net 2

    awk内置变量: NR FNR NF

    NR = Number of Records
    NF = Number of Fields
    FNR = File Number of Records
    下面摘自: The GNU Awk User’s Guide

    awk divides the input for your program into records and fields. It keeps track of the number of records that have been read so far from the current input file. This value is stored in a predefined variable called FNR, which is reset to zero every time a new file is started. Another predefined variable, NR, records the total number of input records read so far from all data files. It starts at zero, but is never automatically reset to zero.
    ......
    The value of the built-in variable NF is the number of fields in the current record.

    翻译与小结
    awk将程序的输入分为记录和字段。
    它跟踪从当前输入文件到目前为止已经读取的记录数量。

    FNR 该值存储在一个名为FNR的预定义变量中,每启动一个新文件时,该变量将重置为零。【每行的行号-单个文件

    NR 另一个预定义变量NR记录从所有数据文件中读取的输入记录总数。 它从零开始,但 从不 自动重置为零。【每行的行号-全部文件

    NF 内置变量NF的值是当前记录中的字段数。在执行块中$NF可以得到当前行末字段的值

    • (6). 打印全局行号 当前行号 行内列数 行末字段值
    > awk 'BEGIN{print "全局行号t当前行号t行内列数t行末字段值"} {print NR,"t", FNR,"t", NF,"t", $NF}' marks.txt marks2.txt
    
    全局行号    当前行号    行内列数    行末字段值
    1    1   4   80
    2    2   4   90
    3    3   4   87
    4    4   4   85
    5    5   4   89
    6    1   4   80
    7    2   5   10
    8    3   4   87
    9    4   5   12
    10   5   3   History
    

    新葡亰496net 3

    疲劳分割线

    本文由新葡亰496net发布于电脑系统,转载请注明出处:如何解决read命令产生的硬编码问题,read命令产生

    关键词: