Shell编程规范与变量
在Shell编程中,遵循一定的编程规范和正确地使用变量是非常重要的。这不仅能帮助我们写出更易于阅读和维护的脚本,还能减少因代码不规范导致的错误。接下来,我将详细解释Shell编程中的规范以及变量的使用。
一、Shell脚本概述
Shell脚本的概念
- 将要执行的命令按顺序保存到一个文本文件
- 给该文件可执行权限
- 可结合各种Shell控制语句以完成更复杂的操作
Shell脚本应用场景
- 重复性操作
- 交互性任务
- 批量事务处理
- 服务运行状态监控
- 定时任务执行
- …
1.Shell的作用
Shell的作用——命令解释器,“翻译官”
- 介于系统内核与用户之间,负责解释命令行
# 记录当前用户有哪些 SHELL
[root@bogon ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
# 记录当前用户所使用的 SHELL
[root@bogon ~]# echo $SHELL
/bin/bash
2.编写第一个Shell脚本
- Bash(/bin/bash)是目前大多数 Linux 版本采用的默认 Shell。
- Bash 的全称为 Bourne Again Shell,是最受欢迎的开源软件项目之一。
# 新建 kgc.sh 文件,写一个脚本
[root@bogon ~]# vi kgc.sh
[root@bogon ~]# cat kgc.sh
echo "这是我的第一个脚本"
echo "hello"
# 添加可执行权限
# 赋予可执行权限,使脚本具有可执行属性
[root@localhost ~]# chmod +x kgc.sh
# 第一行“#!/bin/bash”是一行特殊的脚本声明,表示此行以后的语句通过/bin/bash 程序来解释执行
# 声明解释器
[root@bogon ~]# vi kgc.sh
[root@bogon ~]# cat kgc.sh
#!/bin/bash
echo "这是我的第一个脚本"
echo "hello"
执行脚本文件
- 内部命令
- sh
- .
- source
- bash
# 执行脚本的几种方式 sh、bash、.、source
[root@bogon ~]# sh kgc.sh
这是我的第一个脚本
hello
[root@bogon ~]# bash kgc.sh
这是我的第一个脚本
hello
[root@bogon ~]# source kgc.sh
这是我的第一个脚本
hello
[root@bogon ~]# . kgc.sh
这是我的第一个脚本
hello
绝对路径和相对路径(必须有x权限)
# 添加 x 权限
# 执行绝对路径和相对路径需要给文件添加权限
[root@bogon ~]# chmod +x kgc.sh
# 绝对路径
[root@bogon ~]# /root/kgc.sh
这是我的第一个脚本
hello
# 相对路径
[root@bogon ~]# ./kgc.sh
这是我的第一个脚本
hello
3.重定向与管道操作
交互式硬件设备
- 标准输入:从该设备接受用户输入的数据
- 标准输出:通过该设备向用户输出数据
- 标准错误:通过该设备报告执行出错信息
类型 | 设备文件 | 文件描述编号 | 默认设备 |
---|---|---|---|
标准输入 | /dev/stdin | 0 | 键盘 |
标准输出 | /dev/stdout | 1 | 显示器 |
标准错误输出 | /dev/stderr | 2 | 显示器 |
重定向操作
类型 | 操作符 | 用途 |
---|---|---|
重定向输入 | < | 从指定的文件读取数据,而不是从键盘输入 |
重定向输出 | > | 将输出结果保存到指定的文件(覆盖原有内容) |
>> | 将输出结果追加到指定的文件尾部 | |
标准错误输出 | 2> | 将错误信息保存到指定的文件(覆盖原有内容) |
2>> | 将错误信息追加到指定的文件中 | |
混合输出 | &> | 将标准输出、标准错误的内容保存到同一个文件中 |
管道操作符号“|”
- 将左侧的命令输出结果,作为右侧命令的处理对象
- cmd1 | cmd2 [… | cmdn]
[root@bogon ~]# grep 'bash$' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@bogon ~]# grep "/bin/bash$" /etc/passwd | awk -F: '{print $1,$7}'
root /bin/bash
# 以冒号:分隔,输出第 1、7个字段
重定向输出
- 重定向输出指的是将命令的正常输出结果保存到指定的文件中,而不是直接显示在显示 器的屏幕上。
- 重定向输出使用“>”或“>>”操作符号,分别用于覆盖或追加文件
[root@bogon ~]# read -p "请输入你的名字:" name
请输入你的名字:王小虚
# 覆盖
[root@bogon ~]# ./kgc.sh > /tmp/kgc.log
[root@bogon ~]# cat /tmp/kgc.log
这是我的第一个脚本
hello
# 追加
[root@bogon ~]# ./kgc.sh >> /tmp/kgc.log
[root@bogon ~]# cat /tmp/kgc.log
这是我的第一个脚本
hello
这是我的第一个脚本
hello
重定向输入
- 重定向输入指的是将命令中接收输入的途径由默认的键盘改为指定的文件,而不是等待 从键盘输入。
- 重定向输入使用“<”操作符
# 添加初始密码串内容"123456"
[root@bogon ~]# vi pass.txt
[root@bogon ~]# cat pass.txt
123456
# 从pass.txt 文件中取密码,需要注意 SELinux 会影响此命令执行,若执行失败可尝试关闭 SELinux
[root@bogon ~]# passwd --stdin root < pass.txt
更改用户 root 的密码 。
passwd:所有的身份验证令牌已经成功更新。
错误重定向
- 错误重定向指的是将执行命令过程中出现的错误信息(如选项或参数错误等)保存到指定的文件,而不是直接显示在屏幕上。
- 错误重定向使用“2>”操作符,其中“2”是指错误文件的编号(在使用标准输出、标准输入重定向时,实际上省略了 1、0 编号)
- 使用“2>”操作符时,会像使用“>”操作符一样覆盖目标文件的内容,若要追加内容而不是覆盖文件,则应改用“2>>”操作符
管道操作
- 管道(pipe)操作为不同命令之间的协同工作提供了一种机制,位于管道符号“|”左侧的命令输出的结果
- 将作为右侧命令的输入(处理对象),同一行命令中可以使用多个管道
- 在 Shell 脚本应用中,管道操作通常用来过滤所需要的关键信息
- awk 命令的作用是以冒号“:”作为分隔,输出第 1 个、第 7 个区域的字符串。
- 其中的“-F”部分用来指定分隔符号(未指定时,默认以空格或制表符分隔)
# 提取之前
[root@bogon ~]# grep "/bin/bash$" /etc/passwd
root:x:0:0:root:/root:/bin/bash
# 提取之后
[root@bogon ~]# grep "/bin/bash$" /etc/passwd | awk -F: '{print $1,$7}'
root /bin/bash
# 提取之前
[root@bogon ~]# df -Th
文件系统 类型 容量 已用 可用 已用% 挂载点
devtmpfs devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs tmpfs 1.9G 12M 1.9G 1% /run
tmpfs tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
/dev/mapper/centos-root xfs 50G 1.5G 49G 3% /
/dev/mapper/centos-home xfs 26G 33M 26G 1% /home
/dev/sda1 xfs 1014M 153M 862M 15% /boot
tmpfs tmpfs 378M 0 378M 0% /run/user/0
# 提取之后
[root@bogon ~]# df -Th | grep "/$" | awk '{print $6}'
3%
# 过滤出shell是bash的用户赋值给test变量
[root@bogon ~]# grep 'bash$' /etc/passwd
root:x:0:0:root:/root:/bin/bash
[root@bogon ~]# test=$(grep 'bash$' /etc/passwd)
[root@bogon ~]# echo $test
root:x:0:0:root:/root:/bin/bash
二、Shell变量的作用、类型
变量的作用
- 用来存放系统和用户需要使用的特定参数(值)
- 变量名:使用固定的名称,由系统预设或用户定义
- 变量值:能够根据用户设置、系统环境的变化而变化
变量的类型
- 自定义变量:由用户自己定义、修改和使用
- 特殊变量:环境变量,只读变量,位置变量,预定义变量
自定义变量
定义一个新的变量
- 变量名以字母或下划线开头,区分大小写,建议全大写
- 变量名=变量值
[root@localhost ~]# name=zhangsan
[root@bogon ~]# age=18
查看和引用变量的值
echo &变量名
# 通过在变量名称前添加前导符号“$”,可以引用一个变量的值
# 使用 echo 命令可以查看变量,可以在一条 echo 命令中同时查看多个变量值
[root@bogon ~]# echo $name
zhangsan
[root@bogon ~]# echo $name $age
zhangsan 18
# 当变量名称容易和紧跟其后的其他字符相混淆时,需要添加大括号“{}”将其括起来,否则将无法确定正确的变量名称
# 对于未定义的变量,将显示为空值
[root@bogon ~]# echo $Product1.8.171
.8.171
[root@bogon ~]# echo ${Product}1.8.171
Java1.8.171
# 双引号特殊字符特殊输出
[root@bogon ~]# echo "$Product"
Java
# 单引号原样输出
[root@bogon ~]# echo '$Product'
$Product
变量赋值的特殊操作
- 双引号:允许通过$符号引用其他变量值
- 单引号:禁止引用其他变量值,$视为普通字符
- 反撇号:命令替换,提取命令执行后的输出结果
双引号(" ")
- 双引号主要起界定字符串的作用,特别是当要赋值的内容中包含空格时,必须以双引号括起来
# 错误的赋值
[root@bogon ~]# PYTHON=Python 2.7.13
-bash: 2.7.13: 未找到命令
# 正确的赋值
[root@bogon ~]# PYTHON="Python 2.7.13"
[root@bogon ~]# echo $PYTHON
Python 2.7.13
[root@bogon ~]# Version=2.7.13
# 以变量的值进行赋值
[root@bogon ~]# PyVersion="Python $Version"
[root@bogon ~]# echo $PyVersion
Python 2.7.13
单引号(’ ')
- 当要赋值的内容中包含$、“、\等具有特殊含义的字符时,应使用单引号括起来
- 在单引号的范围内,将无法引用其他变量的值,任何字符均作为普通字符看待
- 但赋值内容中包 含单引号(‘)时,需使用\’符号进行转义,以免冲突
# $符号不能再引用变量
[root@localhost ~]# PyVersion='Python $Version'
# 原样输出字符串
[root@localhost ~]# echo $PyVersion
Python $Version
反撇号(`)
- 反撇号主要用于命令替换,允许将执行某个命令的屏幕输出结果赋值给变量
- 反撇号括 起来的范围内必须是能够执行的命令行,否则将会出错
[root@bogon ~]# vi users.txt
[root@bogon ~]# cat users.txt
zhangsan
lisi
wangwu
zhaoliu
[root@bogon ~]# name=`cat users.txt`
[root@bogon ~]# echo $name
zhangsan lisi wangwu zhaoliu
[root@bogon ~]# name=$(cat users.txt)
[root@bogon ~]# echo $name
zhangsan lisi wangwu zhaoliu
read命令
- 除了上述赋值操作以外,还可以使用 Bash 的内置命令 read 来给变量赋值。
- read 命令用来提示用户输入信息,从而实现简单的交互过程。
read [-p "提示信息"] 变量名
read -p "请输入第一个数字:" num
- -a 后跟一个变量,该变量会被认为是个数组,然后给其赋值,默认是以空格为分割符。
- -d 后面跟一个标志符,其实只有其后的第一个字符有用,作为结束的标志。
- -p 后面跟提示信息,即在输入前打印提示信息。
- -e 在输入的时候可以使用命令补全功能。
- -n 后跟一个数字,定义输入文本的长度,很实用。
- -r 屏蔽\,如果没有该选项,则\作为一个转义字符,有的话 \就是个正常的字符了。
- -s 安静模式,在输入字符时不再屏幕上显示,例如login时输入密码。
- -t 后面跟秒数,定义输入字符的等待时间。
- -u 后面跟fd,从文件描述符中读入,该文件描述符可以是exec新开启的。
[root@bogon ~]# read ToDir1
# 输入变量ToDir1 的值为/opt/backup/
/opt/backup/
[root@bogon ~]# echo $ToDir1
/opt/backup/
# 为了使交互式操作的界面更加友好,提高易用性,read 命令可以结合“-p”选项来设置提示信息,以便告知用户应该输入什么内容等相关事项
[root@bogon ~]# read -p "请指定备份存放目录:" ToDir2
请指定备份存放目录:/opt/backup
[root@bogon ~]# echo $ToDir2
/opt/backup
设置变量的作用范围
# 查看当前定义的变量值
[root@bogon ~]# echo "$Product $Version"
Java 2.7.13
# 进入子Shell 环境
[root@bogon ~]# bash
# 无法调用父Shell 环境中的变量
[root@bogon ~]# echo "$Product $Version"
# 返回原有的Shell 环境
[root@bogon ~]# exit
exit
# 查看当前定义的变量值
[root@bogon ~]# echo "$Product $Version"
Java 2.7.13
# 将Product、Version 设为全局变量
[root@bogon ~]# export Product Version
# 进入子Shell 环境
[root@bogon ~]# bash
# 可以调用父Shell 的全局变量
[root@bogon ~]# echo "$Product $Version"
Java 2.7.13
# 返回原有的Shell 环境
[root@bogon ~]# exit
exit
数值变量的运算
- 常用运算符
- 加法运算:+
- 减法运算: -
- 乘法运算: *
- 除法运算: /
- 求模(取余)运算: %
expr 变量1 运算符 变量2 [运算符 变量3] ...
[root@bogon ~]# X=35
[root@bogon ~]# Y=16
[root@bogon ~]# expr $X + $Y
51
# 计算变量 Y 的 3 次方,并将结果赋值给变量 Ycube
[root@bogon ~]# Ycube=`expr $Y \* $Y \* $Y`
[root@bogon ~]# echo $Ycube
4096
# 乘
[root@bogon ~]# expr 5 \* 3
15
# 除
[root@bogon ~]# expr 6 / 3
2
# echo 加上中括号 [] 里面可以输入计算数值变量的运算
[root@bogon ~]# echo $[3*5]
15
# echo 加上两对小括号 (()) 里面可以输入计算数值变量的运算
[root@bogon ~]# echo $((3*5))
15
三、特殊的Shell变量
环境变量
- 由系统提前创建,用来设置用户的工作环境
- 配置文件: /etc/profile、~/.bash_profile
# 使用 env 命令可以查看到当前工作环境下的环境变量,对于常见的一些环境变量应了解其各自的用途
# 选取部分内容
[root@bogon ~]# env
XDG_SESSION_ID=135
HOSTNAME=localhost.localdomain
SELINUX_ROLE_REQUESTED=
TERM=xterm
SHELL=/bin/bash HISTSIZE=1000
SSH_CLIENT=172.16.16.1 65489 22
SELINUX_USE_CURRENT_RANGE=
OLDPWD=/usr/src/httpd-2.4.25
SSH_TTY=/dev/pts/1
USER=root
...... # 省略部分内容
- PATH 变量用于设置可执行程序的默认搜索路径,当仅指定文件名称来执行命令程序时,Linux 系统将在 PATH 变量指定的目录范围查找对应的可执行文件,如果找不到则会提示“command not found”
# 确认脚本位置
[root@bogon ~]# ls -lh /root/kgc.sh
-rwxr-xr-x 1 root root 159 6月 14 10:58 /root/kgc.sh
# 查看当前搜索路径
[root@bogon ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
# 直接执行时找不到命令
[root@bogon ~]# kgc.sh
-bash: kgc.sh: command not found
# 将/root 添加到搜索路径
[root@bogon ~]# PATH="$PATH:/root"
# 查看修改后的搜索路径
[root@bogon ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root
# 直接以文件名运行脚本
[root@bogon ~]# kgc.sh
这是我的第一个脚本
hello
只读变量
[root@bogon ~]# name=lisi
# 设置为只读变量
[root@bogon ~]# readonly name
[root@bogon ~]# echo $name
lisi
[root@bogon ~]# name=zhangsan
# 只读变量不可以被重新赋值
-bash: name: 只读变量
# 只读变量不可以被删除
[root@bogon ~]# unset name
-bash: unset: name: 无法反设定: 只读 variable
位置变量
表示为 $n,n为1~9之间的数字
- $1,第1个位置参数
- $2,第2个位置参数
- $6,第6个位置参数
[root@localhost ~]# ./myprog.sh one two three four five six
预定义变量
- $#:表示命令行中位置参数的个数。
- $*:表示所有位置变量参数的内容。
- $?:表示前一条命令执行后的返回状态,返回值为 0 表示执行正确,返回任何非 0值均表示执行出现异常。
- $0:表示当前执行的脚本或程序的名称。
[root@bogon ~]# cat kgc.sh
#! /bin/bash
echo "\$0:$0"
echo "第一个参数是:$1"
echo "第二个参数是:$2"
[root@bogon ~]# ./kgc.sh aa bb
$0:./kgc.sh
第一个参数是:aa
第二个参数是:bb
[root@bogon ~]# cat kgc.sh
#! /bin/bash
echo "\$0:$0"
echo "第一个参数是:$1"
echo "第二个参数是:$2"
echo "你传递的参数都有:$*"
echo "一共有 $# 个参数"
[root@bogon ~]# ./kgc.sh aa bb
$0:./kgc.sh
第一个参数是:aa
第二个参数是:bb
你传递的参数都有:aa bb
一共有 2 个参数
四、总结
通过遵循以上规范和正确使用变量,可以编写出结构清晰、易于阅读和维护的Shell脚本。这不仅能够提高开发效率,还能减少因代码不规范导致的错误,提升脚本的稳定性和可靠性。在实际编程过程中,我们应该注重养成良好的编程习惯,并不断学习和探索新的Shell编程技巧和工具。