7

linux shell 编程基础知识

 2 years ago
source link: https://xujinzh.github.io/2022/04/18/linux-shell-programming-basics/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

linux shell 编程基础知识

发表于

2022-04-18 更新于 2022-04-20 分类于 technologylinux

阅读次数: 41 本文字数: 9.4k 阅读时长 ≈ 9 分钟

shell 是一个用 C 编写的程序,是用户与系统内核之间的桥梁。其提供了一个界面,用户通过该界面访问操作系统的内核服务。Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。其实,shell 也是一种解释性的程序设计语言,用户通过编写 shell 脚本在 Linux 上进行自动运维等。常见的 Linux shell 种类很多,如 Bourne Shell(/usr/bin/sh或/bin/sh)、Bourne Again Shell(/bin/bash)、C Shell(/usr/bin/csh)、K Shell(/usr/bin/ksh)、Shell for Root(/sbin/sh)等,国内常用的是 /bin/bash,也是大多数 Linux 系统默认的 shell。在一般情况下,人们并不区分 Bourne Shell 和 Bourne Again Shell,所以,像 #!/bin/sh,它同样也可以改为 #!/bin/bash.

shell 脚本一般需要

  1. #!/bin/bash 开头,#! 告诉系统其后路径所指定的程序即是解释此脚本文件的 Shell 程序;
  2. 脚本命名常以 .sh 结尾;
  3. 脚本需要可执行权限.
# 利用 vim 创建脚本
vim hello.sh

# 添加脚本内容
#!/bin/bash
echo "hello world" # 打印输出 hello world

# 增加可执行权限
chmod u+x hello.sh

# 执行脚本
./hello.sh # 相对路径执行
/home/jinzhongxu/hello.sh # 绝对路径执行
bash hello.sh # 如果没有增加可执行权限,可用该方法执行

shell 变量

shell 变量分为系统变量和用户自定义变量。

系统变量常见的有:$HOME,$PWD,$SHELL,$USER 等等,查看某个系统变量值可使用命令echo $HOME 完成,当想查看所有系统变量时可使用命令:set 完成。

用户自定义变量

变量名称一般习惯为大写,不过小写也能通过。建议大写,遵守规范。

变量名的命名须遵循如下规则:

  • 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头
  • 中间不能有空格,可以使用下划线 _
  • 不能使用标点符号
  • 不能使用bash里的关键字(可用help命令查看保留关键字)

注意,变量赋值等号两端不要有空格。用 $ 获取变量值。

自定义变量常使用如下方法:

# 变量=值
A=3
echo A=$A
echo "A=$A"

撤销变量,撤销后无法使用该变量:

# unset 变量
unset A

声明静态变量,这种变量不能被 unset

# readonly 变量
readonly B=2
echo "B=$B"

将命令返回值赋值给变量

A=`date`
A=$(date)

预定义变量

预定义变量指 shell 设计者预先定义好的变量,可以直接在 shell 脚本中使用

  1. $$ (功能:当前进程的进程号 PID);
  2. $! (功能:后台运行的最后一个进程的进程号 PID);
  3. $? (功能:最后依次执行的命令的返回状态,如果为 0,表示正确执行;如果非 0(具体值有命令决定),表示执行错误);
  4. 一些获取位置参数的变量,下面介绍。
# 创建脚本
vim predefine_test.sh

# 添加如下内容
#!/bin/bash

echo "当前程序进程ID=$$"

# 后台执行最后一个程序,获取进程号
date &
echo "最后一个后台方式运行的程序进程ID=$!"
echo "最后一个后台方式运行的程序结果(如果是0表示执行正确,否则错误)=$?"

# 执行
bash predefine_test.sh

# 结果
当前程序进程ID=43085
最后一个后台方式运行的程序进程ID=43086
最后一个后台方式运行的程序结果(如果是0表示执行正确,否则错误)=0
2022年 04月 18日 星期一 13:38:49 CST

设置环境变量

  1. export 变量=值 (功能:将 shell 变量输出为环境变量,直接在命令行运行,则直接生效但退出当前 shell 时失效。可以写入到配置文件 ~/.bashrc/etc/profile 中);
  2. source 配置文件 (功能:让配置文件立即生效);
  3. echo $变量 (功能:查看环境变量是否失效)。

shell 注释

单号注释,在当前行开头添加 #

# A=3

多行注释,方法如下:

:<<!
A=3
B=2
!

把需要注释的内容放在 :<<!! 之间,建议注释符分别占一行。

当我们执行一个 shell 脚本时,如果希望获取到命令行的参数信息,需要位置参数,如下

# 将位置参数 100 和 99 供 hello.sh 使用
./hello.sh 100 99

脚本中如何使用位置参数:

  1. $n (功能:n 为自然数,$0 表示命令本身,如 ./hello.sh$1 表示第 1 个参数,如 100,依次类推,对于超过 9 个的位置参数,需增加大括号,如 ${10};
  2. $* (功能:表示命令行中所有的参数,把所有参数看成一个整体);
  3. $@ (功能:表示命令行中所有的参数,不过不是看作一个整体,而是一个”数组”,可以按顺序读取);
  4. $# (功能:表示命令行中所有参数的个数)。
# 编写脚本
vim arg_test.sh

# 添加如下内容
#!/bin/bash
# \$ 表示转义$
echo "\$0=$0"
echo "\$1=$1"
echo "\$*=$*"
echo "\$@=$@"
echo "\$#=$#"

echo "==========="
for i in $@
do
echo "$i"
done

# 执行
bash arg_test.sh 100 99

# 结果
$0=arg_test.sh
$1=100
$*=100 99
$@=100 99
$#=2
===========
100
99

像其他语言一样,shell 中也可以进行运算操作,方法如下:

  1. $[运算式] # 注意中括号和运算式之间可有一个空格,也可不要;
  2. $(( 运算式 )) # 注意内层小括号和运算式之间可有一个空格,也可不要;
  3. expr 运算式 # expr 与运算式之间有一个空格,运算式中运算符与运算数之间有一个空格。常见运算有:expr \*, /, % 分别表示乘,除,取余;
  4. 如果想将运算结果赋值给变量,需添加 ``,注意使用的是反引号 ` 而不是单引号 ‘,如 A=$[ 2 + 3 ] 或 A=`expr 2 + 3`
# 编写脚本
vim operator_test.sh

# 添加如下内容
#!/bin/bash

A=$(( 2+3 ))
echo "A=$A"
B=$[ 2+3 ]
echo "B=$B"
C=`expr 2 + 3`
echo "C=$C"

# 执行
bash operator_test.sh

# 结果
A=5
B=5
C=5

运算符包括算术运算符、关系运算符、布尔运算符、字符串运算符和文件测试运算符。

算术运算符

下表列出了常用的算术运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
+ 加法 expr $a + $b 结果为 30。
- 减法 expr $a - $b 结果为 -10。
* 乘法 expr $a \* $b 结果为 200。
/ 除法 expr $b / $a 结果为 2。
% 取余 expr $b % $a 结果为 0。
= 赋值 a=$b 把变量 b 的值赋给 a。
== 相等。用于比较两个数字,相同则返回 true。 [ $a == $b ] 返回 false。
!= 不相等。用于比较两个数字,不相同则返回 true。 [ $a != $b ] 返回 true。

注意:条件表达式要放在方括号之间,并且要有空格,例如: [$a==$b] 是错误的,必须写成 [ $a == $b ]

关系运算符

关系运算符只支持数字,不支持字符串,除非字符串的值是数字。

下表列出了常用的关系运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
-eq 检测两个数是否相等,相等返回 true。 [ $a -eq $b ] 返回 false。
-ne 检测两个数是否不相等,不相等返回 true。 [ $a -ne $b ] 返回 true。
-gt 检测左边的数是否大于右边的,如果是,则返回 true。 [ $a -gt $b ] 返回 false。
-lt 检测左边的数是否小于右边的,如果是,则返回 true。 [ $a -lt $b ] 返回 true。
-ge 检测左边的数是否大于等于右边的,如果是,则返回 true。 [ $a -ge $b ] 返回 false。
-le 检测左边的数是否小于等于右边的,如果是,则返回 true。 [ $a -le $b ] 返回 true。

布尔运算符

下表列出了常用的布尔运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
! 非运算,表达式为 true 则返回 false,否则返回 true。 [ !false ] 返回 true。
-o 或运算,有一个表达式为 true 则返回 true。 [ $a -lt 20 -o $b -gt 100 ] 返回 true。
-a 与运算,两个表达式都为 true 才返回 true。 [ $a -lt 20 -a $b -gt 100 ] 返回 false。

逻辑运算符

以下介绍 Shell 的逻辑运算符,假定变量 a 为 10,变量 b 为 20:

运算符 说明 举例
&& 逻辑的 AND [[ $a -lt 100 && $b -gt 100 ]] 返回 false
|| 逻辑的 OR [[ $a -lt 100 || $b -gt 100 ]] 返回 true

注意,运算式是有两个中括号嵌套包裹。

字符串运算符

下表列出了常用的字符串运算符,假定变量 a 为 “abc”,变量 b 为 “efg”:

运算符 说明 举例
= 检测两个字符串是否相等,相等返回 true。 [ $a = $b ] 返回 false。
!= 检测两个字符串是否不相等,不相等返回 true。 [ $a != $b ] 返回 true。
-z 检测字符串长度是否为0,为0返回 true。 [ -z $a ] 返回 false。
-n 检测字符串长度是否不为 0,不为 0 返回 true。 [ -n "$a" ] 返回 true。
$ 检测字符串是否为空,不为空返回 true。 [ $a ] 返回 true。

文件测试运算符

文件测试运算符用于检测 Unix 文件的各种属性。

属性检测描述如下:

操作符 说明 举例
-b file 检测文件是否是块设备文件,如果是,则返回 true。 [ -b $file ] 返回 false。
-c file 检测文件是否是字符设备文件,如果是,则返回 true。 [ -c $file ] 返回 false。
-d file 检测文件是否是目录,如果是,则返回 true。 [ -d $file ] 返回 false。
-f file 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 [ -f $file ] 返回 true。
-g file 检测文件是否设置了 SGID 位,如果是,则返回 true。 [ -g $file ] 返回 false。
-k file 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 [ -k $file ] 返回 false。
-p file 检测文件是否是有名管道,如果是,则返回 true。 [ -p $file ] 返回 false。
-u file 检测文件是否设置了 SUID 位,如果是,则返回 true。 [ -u $file ] 返回 false。
-r file 检测文件是否可读,如果是,则返回 true。 [ -r $file ] 返回 true。
-w file 检测文件是否可写,如果是,则返回 true。 [ -w $file ] 返回 true。
-x file 检测文件是否可执行,如果是,则返回 true。 [ -x $file ] 返回 true。
-s file 检测文件是否为空(文件大小是否大于0),不为空返回 true。 [ -s $file ] 返回 true。
-e file 检测文件(包括目录)是否存在,如果是,则返回 true。 [ -e $file ] 返回 true。

其他检查符:

  • -S: 判断某文件是否 socket。
  • -L: 检测文件是否存在并且是一个符号链接。

条件判断和流程控制

基本语法如下:

[ condition ] 

注意,condition 与中括号之间有一个空格;当 condition 为非空时,如 [ A ],返回 true;当为空 [ ] 时,返回 false.

应用实例,如当某个程序执行正确时,执行某子程序,判断为 [ $? ],0 为 true,大于 1 为 false.

if … else …

实例代码:

# 创建脚本
vim operator2_test.sh

# 添加如下代码
#!/bin/bash

# 判断两个字符串是否相等
if [ "a" = "b" ]
then
echo "a=b"
elif [ "a" = "c" ]
then
echo "a=c"
else
echo "a既不等于b也不等于c"
fi

# 判断两个整数关系
if [ 3 -ge 2 ]
then
echo "3大于等于2"
fi

# 判断文件是否是常规文件
if [ -f /etc/passwd ]
then
echo "存在文件"
fi

# 判断文件是否是常规文件
FILE="/etc/passwd"
if [ -f $FILE ]
then
echo "存在文件"
fi

# 添加执行权限
chmod u+x operator2_test.sh

# 执行
./operator2_test.sh

# 结果
a既不等于b也不等于c
3大于等于2
存在文件
存在文件
case $变量 in
"v1")
如果变量值等于v1,则执行该段程序
;;
"v2")
如果变量值等于v2,则执行该段程序
;;
*)
如果上面的值都不是变量的取值,则执行该段程序
;;
esac

基本语法1

for 变量 in v1 v2 vn
do
执行该段程序
done

基本语法2

for (( 初始值; 循环控制条件; 变量变换))
do
执行该段程序
done

实例代码:

# 编写脚本
vim for_test.sh

# 添加内容
#!/bin/bash

echo "### $* ###"
for i in "$*"
do
echo "$i"
done

echo "### $* ###"
for i in $*
do
echo "$i"
done

echo "### $@ ###"
for i in "$@"
do
echo "$i"
done

echo "### 计算1到100整数和 ###"
SUM=0
for (( i=1; i<=100; i++ ))
do
SUM=$[ $SUM+$i ]
done
echo "SUM=$SUM"

# 赋予执行权限
chmod u+x for_test.sh

# 执行
./for_test.sh 100 200

# 结果
### 100 200 ###
100 200
### 100 200 ###
100
200
### 100 200 ###
100
200
### 计算1到100整数和 ###
SUM=5050

while

while [ condition ]
do
执行该段程序
done
# 编写脚本
vim while_test.sh

# 添加内容
#!/bin/bash

# 计算1到100所有整数和
SUM=0
i=0
while [ $i -le 100 ]
do
SUM=$[ $SUM + $i ]
i=$[ $i + 1 ]
done

echo "1+...+100=$SUM"

# 添加执行权限
chmod u+x while_test.sh

# 执行
./while_test.sh

# 结果
1+...+100=5050

until 循环

until 循环执行一系列命令直至条件为 true 时停止。

until 循环与 while 循环在处理方式上刚好相反。

一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。

until 语法格式:

condition 一般为条件表达式,如果返回值为 false,则继续执行循环体内的语句,否则跳出循环。

until condition
do
command
done
#!/bin/bash
# 打印 0 ~ 9
a=0

until [ ! $a -lt 10 ]
do
echo $a
a=`expr $a + 1`
done

在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break 和 continue。

break命令

break命令允许跳出所有循环(终止执行后面的所有循环)。

下面的例子中,脚本进入死循环直至用户输入数字大于 5。要跳出这个循环,返回到shell提示符下,需要使用 break 命令。

#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字:"
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的! 游戏结束"
break
;;
esac
done

continue

continue 命令与 break 命令类似,只有一点差别,它不会跳出所有循环,仅仅跳出当前循环。

对上面的例子进行修改:

#!/bin/bash
while :
do
echo -n "输入 1 到 5 之间的数字: "
read aNum
case $aNum in
1|2|3|4|5) echo "你输入的数字为 $aNum!"
;;
*) echo "你输入的数字不是 1 到 5 之间的!"
continue
echo "游戏结束"
;;
esac
done

运行代码发现,当输入大于 5 的数字时,该例中的循环不会结束,语句 echo “游戏结束” 永远不会被执行。

read 读取控制台输入

read (选项) (参数)
选项:
-p: 指定读取时的提示符
-t: 指定读取等到时间(单位为秒),超时则直接向下执行
参数:
一般为变量
# 编写脚本
vim read_test.sh

# 添加内容
#!/bin/bash

read -p "请输入一个整数:" NUM
echo "输入的整数是 $NUM"

read -p "请输入一个整数(只等待5秒钟):" -t 5 NUM2
echo "输入的整数是 $NUM2"

# 添加执行权限
chmod u+x read_test.sh

# 执行
./read_test.sh

# 结果
请输入一个整数:2
输入的整数是 2
请输入一个整数(只等待5秒钟):3
输入的整数是 3

shell 中有自带的系统函数,用户也可以自定义函数。

系统函数有很多,比如 basename, dirname 等

# basename [文件路径] [扩展名]
# 返回文件名
basename /home/jinzhongxu/test.sh .sh
# 返回结果
test

# 不指定扩展名,则返回带扩展名的文件名
basename /home/jinzhongxu/test.sh
# 返回结果
test.sh
# dirname [文件路径]
# 返回文件路径
dirname /home/jinzhongxu/test.sh
# 返回结果
/home/jinzhongxu

自定义函数

[ function ] funcname [()]
{
action;
[return int;]
}

# 调用
funcname [值]
# 编写脚本
vim func_test.sh

# 添加内容
#!/bin/bash

function add() {
SUM=$[$1+$2]
echo "$1+$2=$SUM"
}

read -p "请输入一个数:" n1
read -p "请输入另一个数:" n2

add $n1 $n2

# 添加执行权限
chmod u+x func_test.sh

# 执行
./func_test.sh

# 结果
请输入一个数:2
请输入另一个数:3
2+3=5

数组中可以存放多个值。Bash Shell 只支持一维数组(不支持多维数组),初始化时不需要定义数组大小(与 PHP 类似)。

与大部分编程语言类似,数组元素的下标由 0 开始。

Shell 数组用括号来表示,元素用”空格”符号分割开,语法格式如下:

array_name=(value1 value2 ... valuen)
#!/bin/bash

my_array=(A B "C" D)

我们也可以使用下标来定义数组:

array_name[0]=value0
array_name[1]=value1
array_name[2]=value2

读取数组元素

读取数组元素值的一般格式是:

${array_name[index]}
#!/bin/bash

my_array=(A B "C" D)

echo "第一个元素为: ${my_array[0]}"
echo "第二个元素为: ${my_array[1]}"
echo "第三个元素为: ${my_array[2]}"
echo "第四个元素为: ${my_array[3]}"

获取数组长度

#!/bin/bash

my_array[0]=A
my_array[1]=B
my_array[2]=C
my_array[3]=D

echo "数组元素个数为: ${#my_array[*]}"
echo "数组元素个数为: ${#my_array[@]}"

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK