Shell编程学习记录

本文主要是记录学习 Shell 编程时所记录的一些笔记。

Shell 脚本的格式

在 Linux 中有很多命令,每一条命令都可以完成某种特定的任务,这也是 UNIX 的设计哲学:一条命令只做一件事。因此为了组合命令以及便于多次执行,可以使用脚本文件来保存需要执行的命令。

创建一个以 .sh 为后缀的文件,我们可以在其中编写很多指令。如果执行的是刚编写好的脚本文件,那么在执行前还需要为其赋予可执行权限 chmod u+rx filename

说到 Shell 脚本执行方式,有下面几种:

  1. bash ./filename.sh
  2. ./filename.sh
  3. source ./filename.sh
  4. .filename.sh

在 1、2 中,会创建一个名叫 bash 的子进程,Shell 脚本就会运行在该子进程中,两者的区别就在于 2 需要在脚本开头使用 #!/bin/bash 来指定使用 bash,而方式 1 在执行时就明确需要使用 bash 了。方式 3、4 则是在当前进程中执行 Shell 脚本。

如何理解前两种和后两种执行方式的区别呢?比如在 Shell 脚本中存在切换目录的操作 cd ,在前两种方式中,在脚本执行完成后,当前的路径是不会被改变的,因为是使用另一个进程执行的该脚本,而后两种方式在脚本执行完成后,当前路径就会被改变,因为它就是在当前进程下执行的。

有时候我们会发现 Shell 脚本的第一行往往都是 #!/bin/bash,写这一行代码有什么作用呢?或者说有什么好处呢?

简而言之,在第一行开头使用 #! 可以在文件中指定脚本的解析方式,在使用 bash filename.sh 来执行 Shell 时,就是直接指定 bash 作为解析方式,文件内以 # 开头的部分都会被当作注释。而在使用 ./filename.sh 来执行脚本文件时,会使用系统自带的 Shell 来解析脚本文件,遇到以 #! 开头的行时就会被当成非注释,因此在开头写上 #!/bin/bash 就表示需要使用 bash 来解析脚本文件。换句话说,如果第一行写的是,#!/usr/bin/python 或者 #!/usr/bin/python3 就表示该文件需要使用 Python 来解析,即使文件后缀为 .sh

管道与重定向

  • 管道:进程通信的主要工具,在 Shell 中是为了方便两条命令能够进行通信
  • 重定向:将标准输出输出到指定的位置

管道

因为在 Shell 编程中,管道是没有名字的,所以也叫匿名管道或者管道符。管道符是 | ,其作用就是将前一个命令执行的结果传递给后面的命令。比如,echo 123 | cat

值得注意的是,管道会为两条命令创建一个子进程来进行通信,所以在使用 cdpwd 等内部命令时,当前的进程是得不到相应的结果的。

重定向

一个进程默认会打开标准输入、标准输出、错误输出三个文件描述符

  • 输入重定向 < 比如 read var < /path/filename
  • 输出重定向 >>>2>&> 比如 echo 123 > /path/filename
    • > 清空文件内容并输入
    • >> 在文件末尾追加
    • 2> 当命令执行错误时,会将错误信息输出到指定文件
    • &> 无论命令执行成功与否,都会将信息输出到指定文件中

变量

定义 变量的命名与大多数编程语言的区别相差不大,都是由字母、数字以及下划线组成且不以数字开头。

变量的赋值

  • 变量名=变量值= 两边不允许出现空格,因为出现空格后,变量名会被当作命令,因此会报错)
    • a=123
  • 使用 let 为变量赋值
    • let a=10+20
  • 将命令赋值给变量
    • l=ls
  • 将命令结果赋值给变量,使用 $() 或者 ''
    • letc=$(ls -l /etc)
  • 变量值有空格等特殊字符可以包含在 ""''

变量的引用

定义完变量后就需要使用它,使用方式为 ${变量名},当然如果变量在引用时不会引发歧义的话,是可以省略掉 {} 的,什么叫不会引发歧义呢?

1
2
3
4
5
6
7
8
s1='hello'

# 直接引用变量,往往是可以省略掉 {}
echo $s1

# 如果还需要在变量之后衔接其他东西,那么就能会识别成别的变量,导致无法输出想要的值
echo $s123 # 没有该变量,输出空串
echo ${s1}23 # 输出 hello23

此外,还有一种使用场景,如果我需要使用的变量为空值,是否可以使用别的值来代替空值,而当变量被被复赋值后,又可以直接输出变量值而非控制?有一种才做叫做变量替换,形式为 变量=${引用变量-替换值}

1
2
3
4
5
# 设定 path 为文件名,如果为空则输出下划线 _
path=${filename-_}

echo path
# 如果设定了 filename 的值,则输出该值,否则会输出下划线 _

变量的作用范围

变量的默认作用范围是只在当前的进程当中,也就是说,别的进程想要引用另外一个进程的变量是取不到的。

这就需要用到之前提及过的 Shell 脚本的几种解析方式了,如果我们想在脚本中使用该进程中设置的其他变量,就需要使用到 source filename.sh 或者 . filename.sh,因为这样脚本就会运行在当前进程下。

  • 那如果子进程也想访问父进程中的变量,该如何实现呢?

    可以使用 export 关键字,通过 export 变量 的方式,让别的进程也可以放到这一变量。

  • 如果想要删除设置过的变量,又该如何实现呢?

    可以使用 unset 关键字,通过 unset 变量 来删除变量。

系统环境的变量

系统的环境变量都为大写字母加下划线的组合,要查看有哪些环境变量可以使用命令 env | more 或者 set | more 来查看,使用 set 可以查看更多的命令,这里还是用了管道加 more 指令的方式来分页查看命令

常见的系统变量,比如有 PATH,当我们要使用非系统自带的指令时,就需要将可执行命令的路劲追加PATH 中,也就是使用冒号 : 来分割 PATH=$PATH:新的命令路径,并记得用 export 来到处 PATH,这样在使用时,系统就可以找到需要使用的命令。

除了系统变量外,还有一些预定义变量

  • $?:用来表示上一条命令是否成功执行,成功为 0, 失败为 1
  • $$:用来查看当前进程的 PID,在对脚本的运行状态以及监测时会用到
  • $0:用来查看当前进程的名称

此外,还有用于为脚本传递参数的位置变量,比如 $1$2$3$4$5$6$7$8$9${10}…… ……

它们分别用来代替第一个脚本参数以及之后的参数,这样我们在编写 Shell 脚本时就可以很方便的指代传进来的参数。

环境变量的配置

环境变量的配置文件有很多个

  • /etc/profile 保存系统或终端启动时的运行环境,在登录情况下,会被第一个加载
  • ~/.bash_profile
  • /etc/profile.d/ 这是一个目录,根据不同的 Shell 版本会执行下面不同的脚本
  • ~/.bashrc
  • /etc/bashrc

简单区别,在 etc 目录下的配置文件,是所有用户通用的;而其他跟在 ~ 后的配置文件,表示在家(home)目录下,当前用户特有的配置文件。此外,除了目录的不同,还区分了 profilebashrc 这几种配置文件,这是因为登录当前用户的情况分为 loggin shellno login shell,也就是说对于 login shell 会使用当前的所有配置文件,而对于 no login shell 则会只使用 bashrc 的文件。

各个配置文件在加载时也分先后,相同的变量,后加载的会覆盖掉先加载的。在登陆状态下,配置文件的加载顺序为

  1. /etc/profile
  2. ~/.bash_profile
  3. ~/.bashrc
  4. /etc/bashrc

而在非登录的情况下,配置文件是加载不完全的,其加载顺序如下:

  1. ~/.bashrc
  2. /etc/bashrc

值得注意的是,这些配置文件都是在每次打开终端或者 Shell 时会去加载,因此如果新添加了环境变量是不会立即生效的,所以要使得环境变量立即生效可以使用 source 来重新加载添加了新环境变量的的配置文件。

数组

  • 定义数组

    1
    + **显示数组的所有元素** ```echo ${array[@]}

  • 显示数组元素个数

    ${#array[@]}```
    1
    + **显示数组的第一个元素** ```echo ${array[0]}

运算符

赋值运算符

  • = 赋值运算符,用于算数赋值和字符串复制
  • 使用 unset 取消变量的复制
  • = 除了作为复制运算符还可以作为测试操作符

算数运算符

使用 expr 来进行计算 expr 4 + 5,运算符两边要加空格,否则无法计算,当然除了基本运算,expr 还支持其它操作,更多操作可以使用 man expr 来查看。

注意 expr 只支持整数运算,而不支持浮点数运算

如果要将运算结果赋值给变量,可以使用反引号 `, 比如

1
num=`expr 4 + 5 `

数字常量

数字常量的使用方法

  • let "变量名 = 变量值"
  • 变量值以 0 开头为八进制
  • 变量值以 0x 开头为十六进制

双圆括号

双圆括号是 let 命令的简化

  • (( a = 10 ))
  • (( a++ ))
  • echo $(( 10 + 20 ))

测试和判断

测试命令 test,可以用于检查文件或者比较值,可以做以下测试:

  • 文件测试
  • 整数测试
  • 字符串测试

test 命令的具体操作可以使用命令行输入 man test 来查看。常用的参数有一下几种

  • -n STRING 判断字符串长度是否是非零(nonzero)
  • -z STRING 判断字符串长度是否为零(zero)
  • INTEGER -eq INTERGER 等于判断(equal),字符串的等于判断可以用 =
  • INTEGER -ge INTERGER 大于等于判断(greater than or equal)
  • INTEGER -gt INTERGER 大于判断(greater than)
  • INTEGER -le INTERGER 小于等于判断(less than or equal)
  • INTEGER -lt INTERGER 小于判断(less than)
  • INTEGER -ne INTERGER 不等于判断(not equal),字符串的不等于判断可以用 !=
  • -d FILE 判断FILE是否为目录(directory)
  • -e FILE 判断FILE是否存在(exist)
  • -f FILE 判断FILE是否为文件(file)
  • -r FILE 判断FILE是否可读(readable)
  • -s FILE 判断FILE大小(size)是否大于 0
  • -w FILE 判断FILE是否可写(writable)
  • -x FILE 判断FILE是否可执行(executable)

test 测试语句可以简化为 [] 符号,[] 符号还有扩展写法 [[]] 支持 &&||<>

根据之前提到的,在命令行中,如果要测试上一条语句是否成功,还可以使用 echo $? 来判断结果(0 为成功,非零为失败)。

分支

if 判断

if-then 语句的基本用法

1
2
3
if [ 测试条件 ] 或 命令返回值是否为 0
then 执行相应命令
fi 结束

有时候也可以写成一行,但是要注意使用 ; 分隔

1
if [ 测试条件 ] 或 命令返回值是否为 0; then 执行相应命令; fi

此外还有 if-then-else 语句可以在条件不成功的情况下也运行相应的命令

1
2
3
4
if [ 测试条件 ] 或 命令返回值是否为 0
then 执行相应命令
else 测试条件不成立,执行相应命令
fi 结束

如果还需要多个 if 判断,就可以使用 if-elif-else 语句

1
2
3
4
5
6
7
if [ 测试条件成立 ]; then 
执行相应命令
elif [ 测试条件成立 ]; then
执行相应命令
else
测试条件不成立,执行相应命令
fi 结束

而对于复杂的判断,可能还需要使用嵌套的 if 语句

1
2
3
4
5
6
if [ 测试条件成立 ]
then 执行相应命令
if [ 测试条件成立 ]
then 执行相应命令
fi
fi 结束

case 分支

对于条件判断有很多的情况,可以使用 case 语句和 select 语句来构成分支

1
2
3
4
5
6
7
8
case "$变量" in
"情况 1" )
命令... ;;
"情况 2" )
命令... ;;
* )
命令... ;;
esac

* 作为通配符,在 case 分支中的作用类似与其他语言 switch 的 defaul 语句。如果要判断的变量是符合两种情况之一即可执行命令,那么可以在情况判断中使用 | 来分隔多个情况。

循环

for 循环遍历命令的执行结果

for 循环的语法

1
2
3
for 参数 in 列表
do 执行命令
done 封闭一个循环

使用反引号或 $() 方式执行命令,命令的结果当作列表进行处理,列表可以使用 {} 来产生,比如 {1..9} 表示 1 到 9 的列表。

列表中若有多个变量,需用空格分隔,在对文本进行处理时,需要使用文本查看命令去除文本内容(文本处理默认逐行处理,如果文本出现空格会当作多行处理)。

当前目录下,含有三个文件 a.mp3 b.mp3 c.mp3,现需要修改文件后缀为 mp4

1
2
3
4
5
6
for filename in `ls *.mp3`
do
# basename 可以去除文件后缀
# basename $filename .mp3 就是去除 .mp3 后缀
mv $filename $(basename $filename .mp3).mp4
done

C 语言风格的 for 命令

1
2
3
4
for (( 变量初始化; 循环判断条件; 变量变化))
do
循环执行的命令
done

while 循环

1
2
3
4
while test测试是否成立
do
命令
done

如果测试条件始终成立就会形成死循环,有时候也可以不写条件而用 : 来代替,表示什么都不做始终为真。

1
2
3
4
while :
do
命令
done

死循环可以用来构建命令菜单等操作。

循环可以嵌套使用,当想中止循环时,可以使用 break 来提前中止;当想跳过本次循环时,可以使用 cotinue 来跳过本次循环。

until 循环

until 循环与 while 循环相反,循环测试为假时,执行循环,为真时循环终止

1
2
3
4
until test测试是否成立
do
命令
done

使用循环对命令参数的处理

之前提到过命令参数可以使用 $1$1 …… ${10} …… $n 进行读取,而 $0 表示的是脚本名称。

此外,$*$@ 代表所有的位置参数,可以用于遍历命令参数;$# 代表位置参数的数量,也可以用于使用 C 语言风格 for 来遍历位置参数。

1
2
3
4
for pos in $*
do
其他操作
done

在遍历命令参数时,还可以使用 shift 命令来对关键字参数列表进行左移,这样每次左移都会把第一个位置的参数去掉,用后一个参数补上该位置。这在使用 while 循环来遍历位置参数时,比较有用。

1
2
3
4
5
6
while [ $# -ge 1 ] 
do
ehco $#
echo "do something"
shift
done

函数

1
2
3
function fname() {
命令
}

这里也可以省略 function,直接 函数名() { } 的方式来定义函数。

如果要声明一些函数作用范围内的变量,可以使用 local 变量名 的方式,这样可以避免影响到函数外面的变量的使用。

值得注意的是,() 不写参数类型,要往函数中传递参数可以直接在使用时跟在函数名后面,而要调用函数参数就可以使用位置参数 $1$1 …… ${10} …… $n

此外,函数也可以具有返回值,使用 return 来返回特定值。

值得注意的是,当把函数写进 Shell 脚本文件后,在运行时应该使用 source 或者 . 来运行脚本,否则当前进程
是找不到刚写的函数的。

计划任务

平时我们编写的脚本都是手动去运行,但有时候也需要设定在某一时刻去运行脚本,这时人为的去运行往往不太合适,这时候就需要提到计划任务了。

一次性计划任务

一次性任务需要使用 at 命令。

在命令行中输入 at 时间 就可以进入设定一次性任务的交互界面(如果需要查看当前时间可以提前输入 date 命令来查看),比如输入 at 21:30 就表示要在晚上九点半执行接下来你要输入的指令,在输入完要计划定时执行的任务后,还需要通过 ctrl + D 来进行提交。如果要查看有多少计划任务,可以输入 atq 命令来查看。

值得注意的是,计划任务是不会出现终端的,所以如果包含标准输出的话,需要重定向到文件中,以便到时进行查看。

周期性计划任务

周期性计划任务需要使用 crontab 命令。

要设置周期性计划任务还需要加一个参数,即输入 crontab -e 来设置,输入该命令后即进入配置页面,配置周日任务的格式为 分钟 小时 日期 月份 星期 要执行的命令,比如

在 4 月 15 日早上 6 点 30 分执行任务(将日期追加到日期文件里)

1
30 6 15 4 * /usr/bin/date >> /tmp/date.txt

每周一到周五的晚上 9 点整执行任务

1
0 21 * * 1-5 /usr/bin/date >> /tmp/date.txt

值得注意的是,因为最小时间单位为 分钟 ,所以,如果在 分钟 的位置为 * 就表示每分钟都要执行该任务,如果要表示整点,那么在 分钟 的位置要为 0。此外,使用 数字-数字 表示连续的时刻,数字,数字 表示分隔的两个时刻。

每一个用户都可以设置自己的周期任务,可以在 /var/spool/cron/{用户名} 中查看到。我们也可以使用 crontab -l 来查看现有的计划任务。

在执行周期性计划任务时,有时候会出现以外的情况,比如设定了下午三点半的任务,但是任务开始前服务器意外宕机,而设定时间过了之后才得以重启服务器,那么此时周期任务就无法按时执行了。此时就会延时运行之前的任务,延时任务的配置文件路径为 /etc/anacrontab,一般情况下不需要更改。

计划任务加锁 flock

有时候,会在某一时刻执行多个任务,比如因为延时上一个备份任务与这一时刻的备份任务同时启动,假如都对同一份数据进行备份,此时为了备份的完整性,会对要备份的文件或者数据上锁,在同一时间只能允许一个程序去访问(排他锁),使用命令

1
flock -xn "/tmp/f.lock" -c "{要上锁的文件路径}"

文本搜索

文本内容的过滤(查找) grep

grep 命令的使用方法

1
grep 匹配模板 文件路径

匹配模板就可以理解为正则表达式,关于正则表达式的使用可以另寻查找。

文件的查找命令 find

find 命令的使用方法

1
find 路径 查找条件 [补充条件]

比如要查找 /etc 下所有文件名中含有 passwd 的文件

1
find /etc -name passwd

或者使用正则来查找是文件类型且含有 pass 的文件

1
find /etc -type f -regex pass.*

更多使用方式可以使用 man find 来查看。

行编辑器介绍:sed 和 awk

Vim 我们通常称为全文本编辑器,而 sed 和 awk 都是对行进行编辑,所以称为行编辑器

sed 用法

sed 一般用于对文本内容做替换,其基本工作方式为:将文件以行为单位读取到内存(模式空间),使用 sed 的每个脚本对该行进行操作,处理完成后输出该行。

sed 的替换命令 s

  • sed 's/old/new/ filename
  • sed -e 's/old/new/' -e 's/old/new/' filename -e 可以连续进行替换,也可以用分号分隔 sed 's/old/new/;s/old/new/' filename
  • sed -i 's/old/new/' 's/old/new' filename -i 是将修改内容写回到源文件,默认是输出修改但不改变源文件

带正则表达式的替换命令 s

  • sed 's/正则表达式/new/' filename
  • sed -r 's/扩展正则表达式/new/' filename

有时候需要匹配 /,但是 / 太多会出现错误,可以使用其他符号代替分隔符,比如 @! 等,即 sed 's!/!new!' filename

在扩展正则表达式中还有一种概念叫做分组,即用圆括号 () 来框住要匹配的内容,如果要取得匹配到的分组内容,可以这么用 sed -r 's/(匹配分组1)|(匹配分组2)/\1\2/'\1\2 分别表示分组的序号。

替换命令 s 的加强版

标志位

s/old/new/标志位,通过设定标志位来加强搜索功能

  • 全局替换:sed 's/old/new/g' filename,其中 g 就表示全局替换,用于替换所有出现的次数
  • 替换第n次匹配: sed 's/old/new/数字' filename,如果数字为 2 则表示只替换第 2 次的匹配
  • 打印替换成功的内容 sed 's/old/new/p' filename,具体的意思就是如果匹配到了某一行,不仅会将改行的内容进行替换,还会在下一行中打印输出(看着就有两行相同的)
  • 阻止默认输出 sed -n 's/old/new/p' filename,如果标志位不为 p,则不会有输出内容,如果修改为 p,则只会将匹配并修改成功的行输出
  • 将替换成功的内容写入到文件 sed 's/old/new/w output_file' filename,在标志位 w 后空一格在写上输出文件,注意这只输出匹配并替换成功的内容,其他内容是不会被输出的

寻址

sed 命令还有寻址的操作,可以找到特定行后,再对行内内容进行查询与替换

  • sed '/正则表达式/s/old/new/g' filename,比如只查询以数字开头的行 sed '/^[0-9]/s/old/new' filename
  • sed '行号s/old/new/g' filename ,行号可以是具体的行,也可以是最后一行 $,或者指定多行 '1,3s/old/new' (只查询 1 到 3 行)

可以混合使用行号和正则地址

分组

在匹配到某行后,对行内的内容进行多种替换

/正则表达式/{s/old/new;s/old/new}

sed 脚本文件

如果匹配规则很长,也可以是将匹配内容保存为文件,使用 -f 加载脚本文件

sed -f sedscript filename

sed 的其它命令

  • 删除命令 dsed '/regex/d' filename 将匹配到的行删除,在执行删除命令后,后面跟着的其它命令都不会被执行
    • 相较于替换命令 s 的 sed 's/old//' filename,这只是去掉了匹配的内容,但是该行还是保留了
  • 追加命令 ased '/regex/a 追加内容' filename 在匹配到的行之下追加内容
    • 读取文件内容并追加 r sed '/regex/r input_file' filename
  • 插入命令 ised '/regex/i 插入内容' filename 在匹配到的行之上插入内容
  • 更改命令 csed '/regex/c 更改内容' filename 将匹配到的行整个更改
  • 打印行号 =sed '/regex/=' filename
  • 对匹配到的行的下一行进行操作 nsed '/regex/new/n' filename(比如只想对奇数行或偶数行进行操作时)
  • 将匹配到的行进行输出 psed '/regex/p' filename
    • 相较于替换命令 s 的 sed 's/old/new/p' filename,这是打印替换后的内容,而这次是将匹配到的内容输出
  • 退出命令 qsed 行号q filename,当读到某一行时退出
    • 另一种写法 sed -n 1,10p filename,这种方式效率没上面高,因为这要把所有行都处理一遍但只输出前十行,而上一种方法只读到前十行就结束了

awk 用法

awk 一般用于对“比较规范”的文本进行处理,用于统计数量并输出指定字段、按需要的格式进行输出,更像是脚本语言。

使用 sed 将不规范的文本,处理为“比较规范”的文本。

awk 脚本的流程控制

  • 输入数据前例程 BEGIN{}
  • 主输入循环 {}
  • 所有文件读取完成例程 END{}

awk 的字段引用和分离

每一行称作 awk 的记录;使用空格、制表符分隔开的单词称作字段(可以自己指定分隔的字段)

字段的引用

  • awk 中使用 $1 $2 ... $n 表示每一个字段,而 $0 表示一整行:awk '{print $1, $2, $3}' filename
  • awk 可以使用 -F 选项改变字段分隔符:awk -F '分隔符' '{print $1, $2, $3}' filename(分隔符可以使用正则表达式)
  • awk 对符合要求的特定行(利用正则表达式)做处理:awk '/regex/{print $1, $2, $3}' filename

awk 的系统变量

  • FS 和 OFS 字段分隔符,OFS 表示输出的字段分隔符
    • 指定字段分隔符:awk 'BEGIN{FS=":"}{print $1} filename,指定 : 为分隔符,与 awk -F ':' '{print $1, $2, $3}' filename 的作用一样
    • 输出时指定字段的分隔符:awk 'BEGIN{FS=":";OFS="-"}{print $1 $2} filename,源文件以 : 分隔字段,输出时再以 - 连接字段
  • RS 记录分隔符
    • 输出时以记录分隔符来分行:awk 'BEGIN{RS=":"}{print $0} filename
  • NR 和 FNR 行数(区别体现在多文件输出上)
    • 在多文件输出上 NR 会把多个文件整合然后从 1 直到末尾:awk '{print NR, $0}' filename
    • 而 FNR 则会对每个文件都从 1 开始计数:awk '{print FNR, $0}' filename
  • NF 字符按数量,最后一个字段内容可以用 $NF 取出
    • 输出记录的字段个数:awk '{print NF}' filename
    • 直接输出每行记录的最后一个字段:awk '{print $NF}' filename

awk 判断和循环

正如之前是所说,awk 就像一个脚本语言,它也包含一些类似于其他语言的赋值操作符、算术操作符、关系操作符、布尔操作符等。

条件语句

条件语句使用 if 开头,格式如下

1
2
3
4
if (表达式)
awk 语句
else
awk 语句

类似于 C 语言的 if-else 语句,如果有多个语句需要执行则需要用 {} 将多个语句括起来。

循环语句

while 循环

1
2
while(表达式) 
awk 语句

有多个 awk 语句时,需要用 {} 将多个语句括起来。

do-while 循环

1
2
3
do {
awk 语句
} while(表达式)

for 循环

1
2
for(初始值; 循环判断条件; 累加)
awk 语句

有多个 awk 语句时,需要用 {} 将多个语句括起来。

在对循环的边界进行判断时,对于记录长度,可以使用之前讲过的系统变量 NF。

类似的,循环语句也与 C 语言的相似,而且同样支持 break 和 continue。

awk 数组

awk 数组的定义:数组名[下标]=值,下标可以使用数字也可以使用字符串。

在对数组进行遍历的方法如下:

1
2
for(变量 in 数组名)
通过 数组名[变量] 来获取值

如果要删除数组的值,可以使用 delete 数组[下标] 的方式。

有时候 awk 语句很长或者说会经常被使用,那么可以将 awk 语句写入以 .awk 结尾的文件中,使用时就可以利用 awk -f backup.awk filename

awk 还有命令行参数数组,这也与 C 语言的参数数组类似:

  • ARGC:表示命令行参数长度
  • ARGV:表示命令行参数数组

不过值得注意的时,ARGV[0] 为命令名称,从 ARGV[1] 往后才是命令参数;此外,在处理脚本的时候,是在读入文件之前就要进行处理,所以需要在 BEGIN{} 进行使用。

因此,结合文件和命令参数,就可以更方便的使用 awk:awk -f backup.awk 参数1 参数2

文章作者: Inno Fang
文章链接: https://innofang.github.io/2020/04/05/Shell编程学习记录/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来自 Inno's Blog