当前位置: 首页 > news >正文

Shell 编程

文章目录

  • Shell 编程
    • 创建第一个 Shell 脚本
      • Shell 脚本
      • 脚本开头
      • 脚本注释
      • bash 与 sh 的区别
      • Shell 脚本的执行
      • 示例
    • Shell 中特殊变量
      • Shell 位置参数变量
      • Shell 进程中的特殊状态变量
        • $?
      • Shell 内置变量命令
        • echo
        • read
          • 示例1
          • 示例2
          • 示例3
    • 变量的数值计算实践
      • Shell 中常见的算术运算命令
      • (()) 双小括号数运算实践
      • let 命令
      • bc 命令
    • Shell 脚本的条件测试
      • 条件测试方法综述
      • test 条件测试

Shell 编程

创建第一个 Shell 脚本

Shell 脚本

在 Linux 系统中, Shell 脚本通常是在编辑器 vi/vim 中编写的,由UNIX/Linux命令、bash Shell 命令、程序结构控制语句和注释等内容组成。这里推荐用Linux自带的功能更强大的vim编辑器来编写,可以事先做一个别名alias vi='vim',并使其永久生效,这样以后习惯输入 vi 的读者也就可以直接调用 vim 编辑器了。设置方法如下:

[root@server ~ 16:50:18]# echo "alias vi='vim'" >> /etc/bashrc
[root@server ~ 16:50:47]# source /etc/bashrc

脚本开头

一个规范的 Shell 脚本在第一行会指出由哪个程序(解释器)来执行脚本中的内容,这一行内容在Linux bash 的编程一般为:

#!/bin/bash

在执行bash脚本的时候,内核会根据"#!"字符后的解释器来确定该用哪个程序解释这个脚本中的内容。

脚本注释

在 Shell 脚本中,跟在#后面的内容表示注释,用来对脚本进行注释说明,注释部分不会被当作程序来执行,仅仅是给开发者和使用者看的,系统解释器是看不到的,更不会执行。

注释可自成一行,也可以跟在脚本命令的后面与命令在同一行。

开发脚本时,如果没有注释,那么团队里的其他人就会很难理解脚本对应内容的用途,而且若时间长了,自己也会忘记。因此,我们要尽量养成为所开发的 Shell 脚本书写关键注释的习惯,书写注释不光是为了方便别人,更是为了方便自己,避免影响团队的协作效率,以及给后来接手的人带来维护困难。

特别提示一下,注释尽量不要用中文,在脚本中最好也不要有中文。

bash 与 sh 的区别

早期的bash与sh稍有不同,但大多数脚本都可以不加修改地在sh上运行。

sh为bash的软链接,大多数情况下,脚本的开头使用 #!/bin/bash#!/bin/sh 是没有区别的,但更规范的写法是在脚本的开头使用 #!/bin/bash

[wan@server ~ 16:57:16]$ ll /bin/sh
lrwxrwxrwx. 1 root root 4 92 10:18 /bin/sh -> bash

Shell 脚本的执行

Shell 脚本的执行通常可以采用以下几种方式:

bash script-name 或sh script-name:这是当脚本文件本身没有可执行权限(即文件权限属性x位为-号)时常使用的方法,或者脚本文件开头没有指定解释器时需要使用的方法。

[wan@server ~ 16:59:23]$ bash script.sh
Hello World!

/path/script-name 或 ./script-name:指在当前路径下执行脚本(脚本需要有执行权限),需要将脚本文件的权限先改为可执行,然后通过脚本绝对路径或相对路径就可以直接执行脚本了。

[wan@server ~ 16:59:33]$ chmod +x script.sh# 绝对路径
[wan@server ~ 16:59:55]$ /home/wan/script.sh# 相对路径
[wan@server ~ 17:00:13]$ ./script.sh
Hello World!

source script-name 或 . script-name:这种方法通常是使用 source. 点号读入或加载指定的 Shell 脚本文件,然后,依次执行指定的 Shell 脚本文件中所有语句。这些语句将在当前父 Shell 脚本进程中运行(其他几种模式都会启动新的进程执行子脚本)。因此,**使用 source 可以将子脚本中的变量值、函数值等传递到当前父 Shell 脚本中使用。**这是它和其他几种方法最大的区别,也是值得读者特别注意的地方。

[wan@server ~ 17:00:42]$ source script.sh
Hello World!
# 等效于
[wan@server ~ 17:01:23]$ . script.sh
Hello World!

bash < script-name 或 cat scripts-name|sh:同样适用于bash,这种用法也很常见。

[wan@server ~ 17:01:29]$ bash < script.sh
Hello World![wan@server ~ 17:02:12]$ cat script.sh | bash
Hello World!

示例

# 创建所需目录
[wan@server ~ 17:05:02]$ mkdir bin# 编写脚本
[wan@server ~ 17:05:08]$ vim ssh_ctl
# 在文件中写入以下内容
#!/bin/bash
systemctl $1 sshd# 赋予文件可执行权限
[wan@server ~ 17:06:33]$ chmod +x ssh_ctl# 普通用户没有权限,切换root用户
[wan@server ~ 17:08:07]$ su - root# 执行脚本
[root@server bin 17:09:38]# bash ssh_ctl status
● sshd.service - OpenSSH server daemonLoaded: loaded (/usr/lib/systemd/system/sshd.service; enabled; vendor preset: enabled)Active: active (running) since 四 2025-10-09 16:46:27 CST; 23min agoDocs: man:sshd(8)man:sshd_config(5)Main PID: 86202 (sshd)CGroup: /system.slice/sshd.service└─86202 /usr/sbin/sshd -D10月 09 16:46:27 server systemd[1]: Starting OpenSSH server da...
10月 09 16:46:27 server sshd[86202]: Server listening on 0.0.0...
10月 09 16:46:27 server sshd[86202]: Server listening on :: po...
10月 09 16:46:27 server systemd[1]: Started OpenSSH server dae...
10月 09 16:57:15 server sshd[109383]: Accepted password for wa...
Hint: Some lines were ellipsized, use -l to show in full.[root@server bin 17:09:53]# bash ssh_ctl stop[root@server bin 17:10:15]# bash ssh_ctl start

Shell 中特殊变量

Shell 位置参数变量

在 Shell 中存在一些特殊且重要的变量,例如:$0$1,我们称之为位置参数变量。要从命令行、函数或脚本执行等处传递参数时,就需要在 Shell 脚本中使用位置参数变量。

部分位置参数变量如下:

  1. $0,获取当前执行的 Shell 脚本的文件名,如果执行脚本包含了路径,那么就包括脚本路径。
  2. **n∗∗,获取当前执行的Shell脚本的∗∗第n个参数值∗∗。如果n大于9,则用大括号括起来,例如n**,获取当前执行的 Shell 脚本的**第n个参数值**。如果n大于9,则用大括号括起来,例如n,获取当前执行的Shell脚本的n个参数值。如果n大于9,则用大括号括起来,例如{10},接的参数以空格隔开。
  3. $#,获取当前执行的 Shell 脚本后面接的参数数量
  4. **∗∗∗,获取当前Shell脚本所有传参的参数,不加引号和‘***,获取当前 Shell 脚本所有传参的参数,不加引号和`,获取当前Shell脚本所有传参的参数,不加引号和@相同;如果给∗‘加上双引号,例如:‘"*`加上双引号,例如:`"加上双引号,例如:‘"*",则表示将所有的参数视为单个字符串,相当于$1 $2 $3`。
  5. **@∗∗,获取当前Shell脚本所有传参的参数,不加引号和@**,获取当前 Shell 脚本所有传参的参数,不加引号和@,获取当前Shell脚本所有传参的参数,不加引号和*相同;如果给@加上双引号,例如:‘@加上双引号,例如:`@加上双引号,例如:@,则表示将所有的参数视为不同的独立字符串,相当于"$1"、“$2”、“$3”…`,这是将多参数传递给其他程序的最佳方式,因为它会保留所有的内嵌在每个参数里的任何空白。

Shell 进程中的特殊状态变量

$?

作用:获取执行上一个指令的执行状态返回值:0为成功,非零为失败,这个变量最常用。

[wan@server ~ 17:15:00]$ ls
bin  ssh_ctl
# 说明ls命令执行成功
[wan@server ~ 17:15:01]$ echo $?
0[wan@server ~ 17:15:23]$ ls /root
ls: 无法打开目录/root: 权限不够
# 说明ls命令执行失败
[wan@server ~ 17:15:52]$ echo $?
2

Shell 内置变量命令

bash Shell 包含一些内置命令。 这些内置命令在目录列表里是看不见的,它们由 Shell 本身提供。常用的内部命令有: echoevalexecreadshift等。

echo

echo命令参数选项:

  • -n,不换行输出内容。
  • -e,解析转义字符(见下面的字符)

转义字符:

  • \n,换行。
  • \t,制表符(tab)。
  • \b,退格。
[wan@server ~ 17:17:29]$ echo -n "wan ";echo wang
wan wang[wan@server ~ 17:17:54]$ echo -e "wan\nwang"
wan
wang[wan@server ~ 17:18:12]$ echo -e "wan\twang"
wan     wang[wan@server ~ 17:19:00]$ echo -e "1\b23"
23[wan@server ~ 17:19:21]$ echo -e "123\b"
123
read

从标准输入读取字符串等信息, 传给 Shell 程序内部定义的变量。

示例1
# 创建所需文件
[wan@server ~ 17:21:00]$ vim useradd.sh
# 在文件中写入以下内容
#!/bin/bash# 提供用户名
read -p  "请输入用户名:" user_name# 提供用户性别
read -p  "请输入用户性别:" user_sex# 提供用户年龄
read -p  "请输入用户年龄:" user_age# 提供用户密码,-s 选项,实现不显示用户密码
read -sp  "请输入用户密码:" user_pass
echo# 添加用户
useradd ${user_name}
# 设置用户密码
echo ${user_pass} | passwd --stdin ${user_name}# 将用户信息存储到/etc/users.info中
echo ${user_name}:x:${user_sex}:${user_age} >> /etc/users.info# 打印用户信息存储到/etc/users.info中
echo "用户信息已经存储到/etc/users.info中."# 给予权限
[wan@server ~ 17:23:06]$ chmod +x useradd.sh# 执行脚本,因为普通用户没有修改密码权限,所以无法修改密码
[wan@server ~ 17:23:20]$ bash useradd.sh 
请输入用户名:wang
请输入用户性别:male
请输入用户年龄:18
请输入用户密码:'123'
useradd: Permission denied.
useradd:无法锁定 /etc/passwd,请稍后再试。
只有根用户才能进行此操作。
useradd.sh:行22: /etc/users.info: 权限不够
用户信息已经存储到/etc/users.info中.
示例2
# 准备所需文件
[root@server ~ 17:30:20]# vim users.info
user_name=zhangsan
user_sex=male
user_age=18
user_pass=123# 读取文件内容到当前shell中
[root@server ~ 17:30:52]# source users.info 
[root@server ~ 17:31:14]# echo $user_name
zhangsan# 创建用户,用户信息提前准备好,存放在/root/users.info中
[root@server ~ 17:32:01]# vim useradd-2.sh
#!/bin/bash# 获取用户信息:读取用户信息到当前shell中
source /root/users.info# 添加用户
useradd ${user_name}# 设置用户密码
echo ${user_pass} | passwd --stdin ${user_name}# 执行脚本
[root@server ~ 17:33:38]# bash useradd-2.sh 
更改用户 zhangsan 的密码 。
passwd:所有的身份验证令牌已经成功更新。
示例3
# 判断数值大小
[wan@server tmp 18:02:06]$ vim test_num.sh
#!/bin/bashread -p "请输入一个数字:" num
# 大于 10
(( $num>10 )) && echo "$num > 10"
# 小于 10
(( $num<10 )) && echo "$num < 10"
# 等于 10
(( $num==10 )) && echo "$num = 10"# 验证
[wan@server tmp 18:02:31]$ bash test_num.sh 
请输入一个数字:1
1 < 10
[wan@server tmp 18:03:04]$ bash test_num.sh 
请输入一个数字:13
13 > 10
[wan@server tmp 18:03:10]$ bash test_num.sh 
请输入一个数字:10
10 = 10

变量的数值计算实践

Shell 中常见的算术运算命令

  • (()),用于整数运算的常用运算符,效率很高。
  • let,用于整数运算,类似于(())
  • expr,可用于整数运算,但还有很多其他的额外功能。
  • bc,Linux下的一个计算器程序(适合整数及小数运算)。
  • $[],用于整数运算。
  • awk,awk 既可以用于整数运算,也可以用于小数运算。
  • declare,定义变量值和属性,-i参数可以用于定义整形变量,做运算。

(()) 双小括号数运算实践

[root@server ~ 17:33:40]# echo $((1+2))
3# 计算从1加到100的值
[root@server ~ 17:37:26]# echo $(($(echo {1..100} | sed 's/ /+/g')))
5050

let 命令

[root@server ~ 17:38:35]# let sum=1+2
[root@server ~ 17:41:16]# echo $sum
3

bc 命令

bc 是UNIX/Linux下的计算器,因此,除了可以作为计算器来使用,还可以作为命令行计算工具使用。

# echo无法直接计算小数
[root@server ~ 17:41:19]# echo $[1.1+2.2]
-bash: 1.1+2.2: 语法错误: 无效的算术运算符 (错误符号是 ".1+2.2"# 使用bc可以计算小数
[root@server ~ 17:42:51]# echo 1.1+2.2 | bc
3.3

Shell 脚本的条件测试

条件测试方法综述

通常,在 bash 的各种条件结构和流程控制结构中都要进行各种测试,然后根据测试结果执行不同的操作,有时也会与if等条件语句相结合,来完成测试判断,以减少程序运行的错误。

执行条件判断表达式后通常会返回"真"或"假",就像执行命令后的返回值为0,表示真;非0,表示假。

在 bash 编程里,条件测试常用的语法如下:

  • 语法1:test <判断表达式>,test命令和"<判断表达式>"之间至少有一个空格。

  • 语法2:[ <判断表达式> ],和test命令的用法相同,这是推荐方法。[]的边界和内容之间至少有一个空格。

  • 语法3:[[ <判断表达式> ]],是比test和[]更新的语法格式。[[]]的边界和内容之间至少有一个空格。

  • 语法4:(( <判断表达式> )),一般用于 if 语句。(())(双小括号)两端不需要有空格。

  • 语法5:comand,命令的返回值确定表达式的真值或假值。

    有几个注意事项需要说明一下:

  • 语法1中的test命令语法2中的[]是等价的,语法3中的[[]]为扩展的test命令,语法4中的(())常用于计算。建议使用相对友好的语法2,即中括号[]的语法格式。

  • [[ ]](双中括号)中可以使用通配符等进行模式匹配,这是其区别于其他几种语法格式的地方。

  • &&、||、>、<等操作符可以应用于[[]]中,但不能应用于[]中,在[]中一般用-a、-o、-gt(用于整数)、-lt(用于整数)代替上述操作符。

  • 对于整数的关系运算,也可以使用 Shell 的算术运算符(())

test 条件测试

# 判断文件是否存在
[root@server ~ 17:47:12]# test -f file && echo true || echo false
false# -z 选项判断变量值是否为空,如果为空,则为真值,反之为假值
[root@server ~ 17:47:28]# unset string
[root@server ~ 17:48:02]# test -z "$string" && echo null string || echo $string
null string# 判断文件状态
# 如果目录存在,就提示目录存在
[wan@server ~ 17:49:44]$ mkdir /tmp/zhangsan
[wan@server ~ 17:50:16]$ test -d /tmp/zhangsan/ && echo /tmp/zhangsan/ is exist
/tmp/zhangsan/ is exist# 如果目录不存在,就创建目录
[wan@server bin 17:53:14]$ rmdir /tmp/zhangsan/
[wan@server bin 17:53:46]$ test ! -d /tmp/zhangsan/ && mkdir /tmp/zhangsan/
[wan@server bin 17:54:07]$ echo $?
0# 第二次执行,目录已经存储
[wan@server bin 17:54:07]$ test ! -d /tmp/zhangsan && mkdir /tmp/zhangsan
[wan@server bin 17:55:07]$ echo $?
1# 判断用户是否为root
# 确保脚本以root身份运行
[wan@server tmp 17:57:39]$ vim ssh_ctl
#!/bin/bash
test "$USER" != "root" && echo Pls run as root. && exit
systemctl $1 sshd# 确保脚本以root身份运行。
[wan@server tmp 18:00:21]$ vim ssh_ctl
#!/bin/bash
(( $UID!=0 )) && echo Pls run as root. && exit
systemctl $1 sshd
http://www.dtcms.com/a/460786.html

相关文章:

  • 来料不良---供应商还是企业的问题?
  • 六轴工业机器人可视化模拟平台 (Vue + Three.js + Blender)
  • Java数据结构:单链表
  • 硬件基础深度解析(一):电阻——电路世界中无处不在的基石**
  • 记力扣2106.摘水果 练习理解
  • Java中的泛型 Generics
  • [linux仓库]信号产生[进程信号·贰]
  • 网站内部链接优化网红营销李佳琦案例分析
  • 有哪些网站设计比较好的公司wordpress右上角登录
  • 在Python中加载.pkl文件
  • HarmonyOS大厂面试总结大全二
  • python 字符串压缩(字符串-中等)含源码(九)
  • 如何自己设计一个网页宁波seo推广哪家好
  • 【STM32项目开源】STM32单片机智能温室大棚控制系统
  • 车牌 OCR 识别:国庆高速免费通行的 “隐形引擎”
  • wpf之TabControl
  • WPF应用最小化到系统托盘
  • 使用平行型子环腔的 23 KHz 线宽 1064 nm SOA 光纤激光器
  • 保定企业建站程序wordpress数据都被存在哪
  • 深圳做网站做app少儿戏曲知识 网站建设
  • SymPy 符号计算:从基础到高级的完整指南
  • 成免费crm推广网站黄石网站建设黄石
  • HTB Monitored writeup(nagios api v1 login)
  • 开源 C++ QT QML 开发(十三)多线程
  • 企业如何建设网站,企业搭建网站的流程
  • HarmonyOS SaveButton深度解析:安全便捷的媒体资源保存方案
  • 如何用开源外卖系统源码打造私域O2O生态?技术+运营双轮驱动
  • {title:敏捷开发实战如何利用Scrum框架在30天内交付高质量软件}
  • 浏览器端音视频处理新选择:Mediabunny 让 Web 媒体开发飞起来
  • iOS 26 能耗监测全景,Adaptive Power、新电池视图