• Shell入门笔记:Linux批量提取文件名/shel文件名提取日期/NCL批量读取文件(shell脚本结合)


    在大型计算时,我们会使用linux系统,而linux系统下使用Shell脚本进行一些批处理任务会十分方便,在这里,我将以我个人遇到的问题为例,结合Shell语言的一些常用命令和知识点,完成对于Shell的基础入门。

    问题描述

    我有来自美国国家冰雪中心的(NSIDC)的一些冰雪海冰数据文件,以nc储存,文件命名格式为:RDEFT4_yyyymmdd.nc
    同时,我有一个NCL脚本,这个脚本可以对该数据进行插值导出。导出文件命名为FILE_yyyy-mm-dd.nc。
    我的脚本只能针对于一个文件,我想加一个循环实现ncl脚本对这些文件的批量处理,而NCL在批量读取文件上的速度并不理想,因此,在这里我想将Shell脚本结合,从Shell中循环获取我想要的文件名,再在ncl中读取文件。
    在这里插入图片描述
    文件命名。

    思路

    根据文件命名特点,在NCL中,我们构造读取文件的方式为:

    ymdd1="20180510"
    ymdd2="2018-05-10"
    data_filename="RDEFT4_"+ymdd1+".nc"
    f=addfile(data_filename,"r")
    output_file_name = name+ymdd2+ ":" + DATE
    
    • 1
    • 2
    • 3
    • 4
    • 5

    从上可知,我们需要从文件名中得到对应的ymdd1,再将其转换为ymdd2格式,输入至NCL中。
    因此我们的思路可以分为以下几步:
    1、shell脚本获取对应文件名
    2、shell从对应文件名提取日期date1
    3、shell将date1格式转变为yyyy-mm-dd格式,存入date2。
    4、将date1、date2作为变量输入至NCL。
    5、在shell脚本中,循环执行NCL。
    下面,我们将首先简单介绍这几步中使用到的shell命令和知识点,再结合实例撰写代码脚本,并根据知识点进行代码解析,

    相关Shell命令介绍

    循环与条件语句

    Linux下的循环与条件语句并没有什么特别,与其他语言类似,循环主要分为:

    1、for循环,又之为条件循环,或者for i in ,其循环次数和给与的条件是成正比的,基本使用结构为:

    for 变量名 in 取值列表
    do
    命令序列
    done
    
    • 1
    • 2
    • 3
    • 4

    2、until循环,条件测试循环,只要条件不成立则反复循环。

    until 条件测试操作
    do  
       命令序列
    done
    
    • 1
    • 2
    • 3
    • 4

    3、While循环,与untill相反,只要满足输入条件,则开始循环

    while语句结构
    while 条件测试操作
    do
       命令序列
    done
    
    • 1
    • 2
    • 3
    • 4
    • 5

    条件则分为:
    1、test命令,测试条件是否成立,成立返回1,不成立返回0,主要进行逻辑判断,常用于测试权限和文件目录是否存在。
    2、If语句,主要进行条件测试,满足则执行命令,不满足则不执行,常与for循环连用。
    3、case语句,用于多在情景下的输出:常用于分类输出,如,输出学生成绩分段等。
    关于循环与条件语句,可参照这两篇博文理解:Linux条件测试 Linux循环架构
    在本次实例中,主要是用For循环与if语句结合。

    Linux变量

    Linux的变量可分为:环境变量与自定义变量。
    在lshell中,当你想定义一个变量时,非常简单,只需:变量名=赋值内容,即可完成变量定义与赋值。
    值得注意的是,变量名称依然有着它的规范:如能使用英文字母,数字和下划线,首个字符不能以数字开头。中间不能有空格,可以使用下划线(_)、不能包含特殊字符等等。
    特别地,变量赋值时,=两端不能有空格,否则赋值失败。
    当我们想使用自己定义的变量时,需要用$varname来使用,否则shell无法判断这是命令还是变量。

    Shell中的特殊字符

    Shell 特殊字符纷繁复杂,在shell中,如果无法正确的使用各种特殊符号,则会对我们执行命令带来许多麻烦。可分为:特殊变量,替换符,转义字符,字符串符(引号),功能符,运算符。
    这些字符数量众多却零碎,依靠死记硬背并不现实,因此建议使用者先有一个初步的概念,在真正使用时再去查询用法。
    特殊字符的整理可见:Shell特殊字符在本文中,我们主要使用以下字符:
    1、$符号来定义变量
    2、‘’ " "单双引号表明字符串
    3、``英文半角符号,作为声明整句作为命令执行。
    4、{}花括号,分隔扩展。
    5、正则与贪婪匹配文件名日期等等。

    Shell正则匹配/通配符(贪婪与非贪婪匹配)

    Shell中的正则与通配符属于Shell中的特殊字符,主要用于提取字符串中对应的信息。
    贪婪匹配可以用于匹配输入的字符,一般油% #表示,%是从右向左匹配,#则是从左向右匹配。
    %为非贪婪匹配,即匹配最短结果。%从右到左进行非贪婪匹配,如:

    v=http.123.com
    echo ${v%.*}
    
    • 1
    • 2

    通过非贪婪匹配,会匹配到.com,随后便将匹配到的字符删去,随后返回:http.123,通过贪婪匹配可以提取对应的文件名。
    正则表达式用来在文件中匹配符合条件的字符串,正则是包含匹配。在Shell中,grep、awk、sed 等命令可以支持正则表达式。
    grep 命令用来行提取字符串;cut 命令用来列提取字符串;sed则为一种轻量编辑器,主要通过与正则结合,对文本进行匹配编辑。
    在本文中,我们使用grep命令来提取日期。

    Shell中的列表(List)

    Shell中也有着让我们储存变量的类型,当我们有着多个变量时,便可选择列表(List)进行存储。
    shell中列表的定义也非常简单,只需listname=()便可定义一个空列表,随后,这个空列表便可以在循环中不断赋值。
    通过类似于切片的操作,可以得到列表哦某一特定下标的元素:

    echo ${listTest[1]} #输出列表第二个元素
    echo ${listTest[@]} #输出所有List
    
    • 1
    • 2

    在本次实例中,我们会定义空列表,来存放提取的文件名与对应日期。

    ls命令

    ls命令是linux下最常用的命令,是list的缩写,通常用来打印出路径下所有文件名,格式为:

    ls [选项] [目录名]
    -a 显示所有文件及目录 (. 开头的隐藏文件也会列出)
    -l 除文件名称外,亦将文件型态、权限、拥有者、文件大小等资讯详细列出
    -r 将文件以相反次序显示(原定依英文字母次序)
    -t 将文件依建立时间之先后次序列出
    -A 同 -a ,但不列出 "." (目前目录)".." (父目录)
    -F 在列出的文件名称后加一符号;例如可执行档则加 "*", 目录则加 "/"
    -R 若目录下有文件,则以下之文件亦皆依序列出
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    本次实例中,通过ls命令,将符合要求的文件列入循环中。

    echo命令

    echo命令是一个内置在Bash中的shell,通常用于shell脚本中以显示消息或输出其他命令的结果。

    echo [选项] string
    
    • 1

    echo除了可以用来打印相应的变量,来判断脚本执行情况外,还可以作为参数传递给变量,不过需要注意,在将参数传递给echo命令之前,shell将替换所有变量、通配符匹配和特殊字符。同时,在使用时,应当注意单双引号的使用。

    grep命令

    Linux grep 命令用于查找文件里符合条件的字符串。

    grep [-abcEFGhHilLnqrsvVwxy][-A<显示行数>][-B<显示列数>][-C<显示列数>][-d<进行动作>][-e<范本样式>][-f<范本文件>][--help][范本样式][文件或目录...]
    
    • 1

    我们可以通过grep命令结合正则,提取出文件名的日期。

    Shell中的日期(date命令)

    Linux date 命令可以用来显示或设定系统的日期与时间,其中Formate规定了日期输出的格式。

    date [OPTION]... [+FORMAT]
    -d, --date=STRING:通过字符串显示时间格式,字符串不能是'now'。
    -f, --file=DATEFILE:类似于--date; 一次从DATEFILE处理一行。
    -I[FMT], --iso-8601[=FMT]:按照 ISO 8601 格式输出时间,FMT 可以为'date'(默认)'hours''minutes''seconds''ns'。 可用于设置日期和时间的精度,例如:2006-08-14T02:34:56-0600。
    -R, --rfc-2822 : 按照 RFC 5322 格式输出时间和日期,例如: Mon, 14 Aug 2006 02:34:56 -0600。
    --rfc-3339=FMT:按照 RFC 3339 格式输出,FMT 可以为'date', 'seconds','ns'中的一个,可用于设置日期和时间的精度, 例如:2006-08-14 02:34:56-06:00。
    -r, --reference=FILE:显示文件的上次修改时间。
    -s, --set=STRING:根据字符串设置系统时间。
    -u, --utc, --universal:显示或设置协调世界时(UTC)。
    --help:显示帮助信息。
    --version:输出版本信息。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    格式化输出:

    date +"%Y-%m-%d"
    2019-12-07
    #输出当前时间2s后时间
    date -d "2 second" +"%Y-%m-%d %H:%M.%S"
    2018-11-20 14:21.31
    
    • 1
    • 2
    • 3
    • 4
    • 5

    在本例中,我们使用date参数,进行date格式的转换,即将yyyymmdd格式转为yyyy-mm-dd

    操作-实例

    文件遍历

    首先,应该将文件夹所有符合要求的文件遍历,输入循环中。
    这里我们要提取所有以RDEFT4开头的文件,使用ls命令与for循环。

    #!/bin/bash
    for file in $(ls [RDEFT4_]*)#遍历所有RDEFT4_开头文件,作为循环输入
    
    • 1
    • 2

    提取文件名

    这里使用到了贪婪匹配,与变量定义

    file_list=() #定义存放文件名的空列表
    do
    filename=${file%.*} #get the name
            #echo ${filename}
            file_list[${#file_list[*]}]=${filename} #save name
    
    • 1
    • 2
    • 3
    • 4
    • 5

    提取日期/格式转换

    使用grep从文件名中查找,并用date转换格式

    date1=()
    date2=()
             basedate1=`echo ${filename} |grep -Eo '[[:digit:]]{8}'` #get date
            basedate2=`date +%Y-%m-%d -d "${basedate1}"` #change format
            date1[${#date1[*]}]=${basedate1}
            date2[${#date2[*]}]=${basedate2}
            #echo ${basedate2}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    与NCL结合

    以上的代码我们提出了日期,并以两种格式存储,我们需要将这两种格式,循环输出到ncl中。
    在ncl里有getenv()函数,可以从shell脚本中获取环境变量,因此我们可以实现:

    ymdd1=getenv("basedate1")
    ymdd2=getenv("basedate2")#从shell中读取环境变量
    
    • 1
    • 2
     export basedate1=`echo ${filename} |grep -Eo '[[:digit:]]{8}'` #get date
     export basedate2=`date +%Y-%m-%d -d "${basedate1}"` #change format
     ncl wrint_int_RDEFT4.ncl
    
    done
    
    • 1
    • 2
    • 3
    • 4
    • 5

    以上便完成了全部操作。

    完整代码&小结

    Shell脚本的使用让我们在Linux系统下的操作效率大幅度提升,而shell语言本身比之普通的计算语言如:matlab、R、python并无太大区别,只要注意语法细节便可。
    语言的入门往往需要以实践为基础,从个人来看,遍历文件夹获取文件名并其中提取日期/数字的操作很适合作为一个入门练习,因为它涉及到了不少基础知识点(循环、变量、匹配、输出、格式转换),同时也非常实用(尤其是对文件名有日期格式要求,而日期不连续的情况下)
    shell脚本实现的资源相对较少,希望这篇博文能给后来者起到一定帮助。
    完整代码:

    #!/bin/bash
    file_list=()
    date1=()
    date2=()
    for file in $(ls [RDEFT4_]*)
    do 
            filename=${file%.*} #get the name
            #echo ${filename}
            file_list[${#file_list[*]}]=${filename} #save name
            export basedate1=`echo ${filename} |grep -Eo '[[:digit:]]{8}'` #get date
            export basedate2=`date +%Y-%m-%d -d "${basedate1}"` #change format
            #date1[${#date1[*]}]=${basedate1}
            #date2[${#date2[*]}]=${basedate2}
            #echo ${basedate2}
            ncl wrint_int_RDEFT4.ncl
    
    done
    #echo ${file_list[*]}
    #echo ${date1[*]}
    #echo ${date2[*]}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
  • 相关阅读:
    内存溢出、内存泄漏与内存抖动
    深度学习之二(前馈神经网络--Feedforward Neural Network)
    计算机专业的学生需要每天刷题吗?
    基于微信小程序的校园信息共享平台 毕业设计-附源码211615
    SpringMVC入门到实战------2、SpringMVC创建实例Hello SpringMVC(maven+tomcat)
    基于android校园新闻APP开发的设计与实现
    信息学奥赛一本通:1309:【例1.6】回文数(Noip1999)
    线性代数的艺术
    C++数组指针、函数指针、成员函数指针
    docker 增加cpu线程数
  • 原文地址:https://blog.csdn.net/weixin_43750300/article/details/127560093