主要内容:
Shell概述,编写及执行脚本、Shell变量(自定义变量、环境变量、预定义变量、位置变量)、数值运算(expr工具、$[]、let、bc)
一、Shell概述
Shell 是操作系统提供的一种命令行解释器,它允许用户通过输入命令来与操作系统进行交互。Shell 提供了一个界面,用户可以通过这个界面执行各种系统命令、运行程序、管理文件和目录等。在Linux内核与用户之间的解释器程序,通常指:/bin/bash
负责向内核翻译及传达用户/程序指令,相当于操作系统的“外壳”
1)基本功能
- 命令解释:Shell 负责解释用户输入的命令,并将其转换为操作系统可以执行的操作。
- 脚本编写:Shell 支持编写脚本,用户可以将一系列命令组合成一个脚本文件,以便自动执行复杂的任务。
- 环境管理:Shell 管理用户的环境变量,如 PATH、HOME 等,这些变量影响命令的查找和执行。
- 管道和重定向:Shell 支持使用管道(|)将一个命令的输出作为另一个命令的输入,以及使用重定向(>、<、>>)将命令的输入输出重定向到文件。
2)常见的 Shell
- Bash (Bourne Again SHell):Linux 和 macOS 默认的 Shell,是 Bourne Shell (sh) 的增强版本,支持命令补全、历史记录、脚本编程等功能。
- Zsh (Z Shell):一个兼容 Bash 的 Shell,提供了更多的功能和自定义选项,如更好的命令补全、主题支持等。
- Fish (Friendly Interactive Shell):一个用户友好的 Shell,提供了自动建议、语法高亮、Web 配置界面等功能。
- PowerShell:微软开发的 Shell,主要用于 Windows 系统,支持命令行管理、自动化任务和脚本编写。
3)Shell的使用方式
- 交互式(命令行):人工干预、智能化程度高;逐条解释执行、效率低;
- 非交互式(脚本):需要提前设计、智能化难度大;批量执行、效率高、方便在后台运行;
4)常见的Shell程序种类
- 通过usermod、chsh更改登录Shell;
- 手动执行目标Shell程序;
[root@svr7 ~]# cat /etc/shells //查看系统拥有的解释器
/bin/sh //多数Unix默认的Shell
/bin/bash //多数Linux默认使用的Shell
/sbin/nologin //非登录Shell
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh
若需要临时使用另一种Shell环境,可以直接执行对应的Shell解释器程序
例如1:切换sh解释器
[root@svr7 ~]# sh //切换sh解释器
sh-4.2# //sh运行风格
sh-4.2# exit //返回到切换前的bash环境
解释:用户 —> /bin/bash默认解释器—> sh解释器(用户登录后,默认使用bash解释器,切换sh解释器(子Shell),则默认bash解释器暂时放入后台等待子Shell解释器退出)
例如2:要执行ksh可以切换到ksh命令行环境
[root@svr7 ~]# yum -y install ksh //安装ksh解释器
[root@svr7 ~]# cat /etc/shells
/bin/ksh //确认当前系统已识别ksh
/bin/rksh
...
[root@svr7 ~]# ksh //切换ksh解释器
# //ksh运行风格
# ls
anaconda-ks.cfg 公共 视频 文档 音乐
initial-setup-ks.cfg 模板 图片 下载 桌面
# exit //返回到切换前的bash环境
补充:ksh无法进行Tab补齐,不支持快捷键
5)环境配置
用户可以通过修改 Shell 的配置文件来自定义 Shell 的行为,常见的配置文件包括:
- .bashrc:Bash 的交互式非登录 Shell 配置文件。
- .bash_profile:Bash 的登录 Shell 配置文件。
- .zshrc:Zsh 的配置文件。
- config.fish:Fish 的配置文件。
通过这些配置文件,用户可以设置别名、环境变量、自定义提示符等。
二、Shell脚本介绍
提前写好可执行语句,能够完成特定任务的文件(顺序、批量化处理,解释型程序)
1、创建脚本思路
1)明确脚本目标
在开始编写脚本之前,首先要明确脚本的目标和功能。例如,你可能想要创建一个脚本来备份文件、监控系统状态、自动化日常任务等。
2)规划脚本结构
根据脚本的目标,规划脚本的结构和流程。考虑需要执行的步骤、可能的输入和输出、以及如何处理错误和异常情况。
3)选择合适的 Shell
根据你的需求和操作系统的不同,选择一个合适的 Shell。常见的选择包括 Bash、Zsh 和 Fish。大多数情况下,Bash 是一个通用且功能强大的选择。
4)编写脚本
① 脚本头部
每个 Shell 脚本都应该以一个“shebang”行开始*(Sha-Bang调用标记),指定要使用的 Shell 解释器。例如,使用 Bash 时,脚本的第一行应该是:
#!/bin/bash
② 添加注释
在脚本中添加注释,解释每个部分的功能。注释以 #
开头,例如:
# 这是一个备份脚本的示例
③ 定义变量
根据需要定义变量,存储文件路径、配置选项等。例如:
source_dir="/path/to/source"
backup_dir="/path/to/backup"
④ 编写主要逻辑
根据规划的结构,编写脚本的主要逻辑。使用条件判断、循环、函数等结构来实现所需的功能。例如,一个简单的备份脚本可能包含以下逻辑:
# 检查源目录是否存在
if [ ! -d "$source_dir" ]; then
echo "源目录不存在: $source_dir"
exit 1
fi
# 创建备份目录(如果不存在)
mkdir -p "$backup_dir"
# 执行备份
cp -r "$source_dir"/* "$backup_dir"
# 输出备份完成信息
echo "备份完成: $source_dir -> $backup_dir"
⑤ 处理错误和异常
在脚本中添加错误处理逻辑,确保在出现错误时能够及时通知用户并采取适当的措施。例如:
if [ $? -ne 0 ]; then
echo "备份失败"
exit 1
fi
5)添加执行权限,并测试脚本
编写完成后,测试脚本以确保其按预期工作。尝试不同的输入和场景,检查输出和错误处理是否正确。
[root@svr7 ~]# vim /root/first.sh //创建脚本文件
echo 'Hello World!' //写脚本语句
[root@svr7 ~]# chmod +x /root/first.sh //加执行权限
[root@svr7 ~]# /root/first.sh //运行脚本
Hello World!
6)优化和改进
根据测试结果,优化和改进脚本。考虑添加更多的功能、改进错误处理、提高脚本的效率和可读性。
7)文档和分享
为脚本编写文档,解释其功能、使用方法和注意事项。如果合适,可以将脚本分享给其他人使用。
2、规范的脚本构成
一个规范的 Shell 脚本不仅需要实现预期的功能,还应该具备良好的结构、注释和错误处理机制。
- Shebang 行:脚本的第一行应该是 Shebang 行,指定脚本解释器。
- 脚本描述:在 Shebang 行之后,添加一段简短的描述,说明脚本的用途和功能。
- 版权和版本信息:添加版权和版本信息,以便其他人了解脚本的来源和更新情况。
- 环境变量和全局变量:使用大写字母命名环境变量,小写字母命名全局变量,以示区分。
- 函数定义:将复杂的逻辑封装在函数中,提高代码的可读性和可维护性。
- 主要逻辑:调用之前定义的函数,并处理可能的输入和输出。
- 错误处理:在脚本中添加错误处理机制,确保在出现错误时能够及时通知用户并采取适当的措施。
- 清理和收尾:在脚本结束前,进行必要的清理工作,如删除临时文件、恢复环境变量等。
- 文档和注释:在脚本中添加详细的注释,解释每个部分的功能。
示例脚本:
#!/bin/bash
# 这是一个用于备份指定目录的脚本
# 作者: [你的名字]
# 版本: 1.0
# 最后更新日期: [日期]
# 环境变量
export TEMP_DIR="/tmp"
# 全局变量
source_dir="/path/to/source"
backup_dir="/path/to/backup"
# 检查目录是否存在的函数
# 参数: 目录路径
check_directory() {
if [ ! -d "\$1" ]; then
echo "目录不存在: \$1"
exit 1
fi
}
# 执行备份的函数
perform_backup() {
cp -r "$source_dir"/* "$backup_dir"
if [ $? -ne 0 ]; then
echo "备份失败"
exit 1
fi
}
# 主要逻辑
check_directory "$source_dir"
check_directory "$backup_dir"
perform_backup
echo "备份完成: $source_dir -> $backup_dir"
# 清理临时文件
rm -rf "$TEMP_DIR/backup_tmp"
补充:脚本 &> /dev/null 程序后台运行并把输出丢入”黑洞”
3、脚本的执行方式
脚本的执行方式取决于操作系统和脚本文件的权限设置。
1)直接执行(标准规范方式)
- 添加x权限,指定脚本文件的路径(绝对路径 / 相对路径)(脚本声明)
解释:用户 —> /bin/bash默认解释器 —> bash解释器(读取脚本声明)—> 执行脚本文件
[root@svr7 opt]# vim test01.sh
#!/bin/bash //声明解释器
#A test program for Shell-Script //声明注释
echo 'Hello World' //执行脚本
[root@svr7 opt]# chmod +x /opt/test01.sh //脚本添加执行权限
[root@svr7 opt]# /opt/test01.sh //绝对路径方式运行
Hello World
常见报错:坏的解释器: 没有那个文件或目录
[root@svr7 opt]# vim /opt/test01.sh
#!/bin/baash //脚本声明的错误解释器
...
[root@svr7 opt]# /opt/test01.sh
-bash: /opt/test01.sh: /bin/baash: 坏的解释器: 没有那个文件或目录
2)通过解释器执行
- 格式:sh 脚本文件路径 //使用解释器执行脚本。无需x权限(开启子Shell)
解释:用户 —> /bin/bash默认解释器 —> 使用新的bash(子Shell)—> 执行脚本文件
# 使用解释器执行:
/bin/bash script_name.sh
# 或者
bash script_name.sh
补充1:bash程序可以“套娃式”多开,执行脚本后会自动退出新解释器,回到默认解释器下;
补充2:可打开新的终端,通过pstree -a命令查看当前运行了几个子Shell程序;
补充3:即使脚本没有可执行权限,也可以通过指定解释器来执行脚本。
3)通过 source 或 . 执行
- 格式:source 脚本文件路径 //调用默认的解释器执行脚本。无需x权限(默认Shell)
- 格式:. 脚本文件路径
解释:用户 —> /bin/bash默认解释器 —> 调用默认解释器 —> 执行脚本文件
# 使用 source 执行:
source script_name.sh
# 或者
. script_name.sh
补充:使用 source 或 . 命令可以在当前 Shell 环境中执行脚本,这会使得脚本中的变量和函数在当前 Shell 中生效。
4)作为命令的一部分
可以将脚本作为命令的一部分来执行,通常用于简单的单行脚本
bash -c 'echo "Hello, World!"'
5)通过 nohup 执行
使用 nohup 命令可以在后台执行脚本,并且不受终端关闭的影响。
nohup ./script_name.sh &
6)通过 at 命令执行
使用 at 命令可以在指定的时间执行脚本。
echo "./script_name.sh" | at 10:00
7)通过 cron 定时执行
使用 cron 可以设置定时任务,定期执行脚本。
# 编辑 cron 表:
crontab -e
# 添加定时任务:
* * * * * /path/to/script_name.sh
4、调试Shell脚本
调试 Shell 脚本是一个重要的技能,可以帮助你快速定位和解决脚本中的问题。
1)使用 echo 语句
在脚本中插入 echo 语句,输出变量的值或程序的执行路径,帮助你了解脚本的执行情况
#!/bin/bash
echo "开始执行脚本"
source_dir="/path/to/source"
echo "源目录: $source_dir"
if [ ! -d "$source_dir" ]; then
echo "源目录不存在: $source_dir"
exit 1
fi
echo "源目录存在,继续执行"
# 其他逻辑...
2)使用 set -x 和 set +x开启调试模式
set -x 命令可以在脚本中启用调试模式,显示每个命令的执行情况。set +x 命令可以关闭调试模式。
#!/bin/bash
set -x
echo "开始执行脚本"
source_dir="/path/to/source"
echo "源目录: $source_dir"
if [ ! -d "$source_dir" ]; then
echo "源目录不存在: $source_dir"
exit 1
fi
set +x
echo "源目录存在,继续执行"
# 其他逻辑...
3)使用 trap 命令
trap 命令可以捕获脚本中的信号,并在捕获到信号时执行指定的命令。例如,捕获 ERR 信号可以在脚本出错时输出调试信息。
#!/bin/bash
trap 'echo "错误发生在行号: $LINENO, 命令: $BASH_COMMAND"' ERR
echo "开始执行脚本"
source_dir="/path/to/source"
echo "源目录: $source_dir"
if [ ! -d "$source_dir" ]; then
echo "源目录不存在: $source_dir"
exit 1
fi
echo "源目录存在,继续执行"
# 其他逻辑...
4)使用 bash -x 执行脚本
在执行脚本时使用 bash -x 参数,可以在终端中显示脚本的执行过程。
bash -x script_name.sh
5)使用调试工具
有一些专门的调试工具可以帮助你调试 Shell 脚本,例如 bashdb。
# 安装 bashdb:
sudo apt-get install bashdb
# 使用 bashdb:
bashdb script_name.sh
6)使用 set -e 和 set -u
set -e 命令可以在脚本遇到错误时立即退出,set -u 命令可以在使用未定义的变量时报错。
#!/bin/bash
set -e
set -u
echo "开始执行脚本"
source_dir="/path/to/source"
echo "源目录: $source_dir"
if [ ! -d "$source_dir" ]; then
echo "源目录不存在: $source_dir"
exit 1
fi
echo "源目录存在,继续执行"
# 其他逻辑...
5、使用脚本编写简单服务部署
案例1:使用脚本快速配置Yum仓库
- 软件源位于:file:///mydvd(提前检查是否挂载)
- 编写脚本:
[root@svr7 opt]# vim test03.sh
#!/bin/bash
rm -rf /etc/yum.repos.d/*.repo //删除原有的repo文件
echo "[abc] //编写YUM仓库配置文件,注意空行
name=ABC
baseurl=file:///mydvd
gpgcheck=0
enabled=1" > /etc/yum.repos.d/abc.repo
[root@svr7 opt]# bash test03.sh //运行脚本
[root@svr7 opt]# yum repolist //列出仓库清单
源标识 源名称 状态
abc ABC 9,911
...
案例2:使用脚本快速搭建HTTP服务
- 装包、编写起始页面文件、起服务、设开机自动启动
- 编写脚本:
[root@svr7 opt]# vim test04.sh
#!/bin/bash
yum -y install httpd //安装软件包
echo "Web Hello" > /var/www/html/index.html //编写起始页面
systemctl restart httpd //重启服务
systemctl enable httpd //开机自启
[root@svr7 opt]# bash test04.sh //运行脚本
[root@svr7 opt]# curl 192.168.4.7 //测试
Web Hello
案例3:使用脚本快速搭FTP服务
- 装包、起服务、设开机自动启动
- 编写脚本:
[root@svr7 opt]# vim test05.sh
#!/bin/bash
yum -y install vsftpd
systemctl restart vsftpd
systemctl enable vsftpd
[root@svr7 opt]# bash test05.sh //运行脚本
三、变量的设置和取消
常量和变量是编程中的基本概念,用于存储和管理数据。它们在不同的编程语言中有不同的实现方式,但基本概念是相似的。
- 常量:固定不变的值,一旦定义就不能修改。
- 变量:用于存储数据的容器,其值可以在程序运行期间被修改;提高脚本对任务需求、运行环境变化的适应能力,方便在脚本中重复使用。
- 全局文件为:/etc/profile,对所有用户有效;
- 用户文件为:~/.bash_profile,仅对指定的用户有效;
root@svr5 ~]# cat /etc/profile
.. ..
HOSTNAME=`/bin/hostname`
HISTSIZE=1000
.. ..
export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE INPUTRC
.. ..
1)定义/赋值变量
- 格式:variable_name=value
2)查看变量
- 格式:$variable_name //引用变量值
- 格式:echo $variable_name //未定义的变量无取值
- 格式:echo ${variable_name} //变量名易混淆时,以{}界定
3)取消变量
- 格式:unset variable_name //手动取消变量
退出定义变量的Shell环境时,变量会自动失效;也可通过手动取消变量方式;
4)导出变量
- 格式:export variable_name=value
export 命令用于将变量导出为环境变量,使其在子进程中可用。
例如1:
[root@svr7 opt]# a=10 //定义变量
[root@svr7 opt]# echo $a //引用变量
10
[root@svr7 opt]# a=20 //再次定义变量将重新赋值
[root@svr7 opt]# echo $a
20
[root@svr7 opt]# a= //定义变量为空(取消变量)
[root@svr7 opt]# echo $a
//结果为空
[root@svr7 opt]# unset a //取消变量
[root@svr7 opt]# echo $a
//结果为空
例如2:
[root@svr7 opt]# a=10
[root@svr7 opt]# echo $aRMB
//结果为空,因引用变量时,系统未有aRMB的变量
[root@svr7 opt]# echo $a'RMB'
10RMB
[root@svr7 opt]# echo ${a}RMB //为变量以{}界定,可避免混淆造成调用变量失败
10RMB
例如3:
# 设置并导出变量
export name="Alice"
echo "名字: $name"
# 在子进程中使用导出的变量
bash -c 'echo "子进程中的名字: $name"'
常见报错:定义变量时,等于号两边不能有空格;
[root@svr7 opt]# A =10
bash: A: 未找到命令...
[root@svr7 opt]# A= 10
bash: 10: 未找到命令...
常见报错:定义变量区分大小写,写入的A不存在则显示为空;
[root@svr7 opt]# echo $a
10
[root@svr7 opt]# echo $A
//结果为空
常见报错:定义变量不能使用数字命名
[root@svr7 opt]# 10=ABC
bash: 10=ABC: 未找到命令...
常见报错:定义变量禁止使用关键字定义;
[root@svr7 opt]# ls=10
[root@svr7 opt]# echo #ls
//结果为空
常见报错:定义变量禁止使用特殊符号;
[root@svr7 opt]# %=10
-bash: fg: %=10: 无此任务
四、变量的种类
1、数据类型分类
- 基本数据类型变量:整数、浮点数、字符、布尔值等。
int_var=10
float_var=3.14
char_var='A'
bool_var=True
- 复合数据类型变量:数组、列表、字典、结构体、类等。
list_var=[1, 2, 3]
dict_var={'name': 'Alice', 'age': 30}
2、作用域分类
1)环境变量
环境变量是由操作系统维护的变量,提供运行环境信息。环境变量可以在整个系统或特定进程中使用。在 Shell 脚本中,可以使用 export 命令将变量导出为环境变量。
- 配置文件:/etc/profile、~/.bash_profile
- 相关操作:
[ env ] 列出所有的环境变量
[ set ] 列出所有变量
常见的环境变量:PWD、PATH、USER、LOGNAME、UID、SHELL、HOME、PS1、PS2...
例如:使用env可查看所有环境变量
[root@svr7 opt]# env | grep HOST
HOSTNAME=svr7.tedu.cn
[root@svr5 src]# env
HOSTNAME=svr5.tarena.com
SHELL=/bin/bash
HISTSIZE=1000
SSH_CLIENT=192.168.4.110 59026 22
OLDPWD=/root
SSH_TTY=/dev/pts/0
USER=root
...
例如:使用set可查看所有变量(包括env能看到的环境变量)
[root@svr7 opt]# AAA=123456
[root@svr7 opt]# set | grep 123456
AAA=123456
[root@svr5 src]# set
BASH=/bin/bash
BASH_ARGC=()
BASH_ARGV=()
BASH_LINENO=()
...
例如:常见的环境变量
[root@svr7 opt]# echo $USER //当前用户名
root
[root@svr7 opt]# echo $HOME //当前用户的家目录
/root
[root@svr7 opt]# echo $UID //当前用户的UID
0
[root@svr7 opt]# echo $SHELL //当前用户的解释器
/bin/bash
[root@svr7 opt]# echo $HOSTNAME //当前主机名
svr7.tedu.cn
[root@svr7 opt]# echo $PWD //当前所在目录位置
/opt
[root@svr7 opt]# echo $PS1 //一级提示符,即命令行提示符(\u 用户名、\h 主机名、\W 工作目录、\$ 权限标识)
[\u@\h \W]\$
[root@svr7 opt]# echo $PS2 //二级提示符;出现强制换行、at任务编辑等场合:
>
例如:[root@svr7 opt]# echo \ // [ \ ]强制换行,一行命令写不下用该方式换行写
> 123
123
[root@svr7 opt]# echo $PATH //记录命令程序的路径
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
2)局部变量
局部变量是在函数内部定义的变量,其作用域仅限于该函数内部。局部变量通常用于避免变量名冲突和限制变量的作用范围。
#!/bin/bash
function my_function {
local local_var="I am a local variable"
echo $local_var
}
my_function
echo $local_var # 这行将不会输出任何内容,因为 local_var 是局部变量
新定义的变量默认只在当前Shell环境中有效,无法在子Shell环境中使用;
3)全局变量
全局变量是在函数外部定义的变量,其作用域覆盖整个脚本,可以在脚本的任何地方访问和修改。
#!/bin/bash
global_var="I am a global variable"
function my_function {
echo $global_var
}
my_function
echo $global_var # 这行将输出 "I am a global variable"
全局变量在当前Shell及子Shell环境中均有效; 使用export命令可将局部变量声明为全局变量;
4)位置参数变量
位置参数是在调用脚本或函数时传递的参数。位置参数可以通过 \$1, \$2, \$3, ... 来访问。
#!/bin/bash
echo "第一个参数: \$1"
echo "第二个参数: \$2"
# 调用脚本时:
./script.sh arg1 arg2
# 输出:
第一个参数: arg1
第二个参数: arg2
5)预定义变量(特殊变量)
特殊变量是由 Shell 预定义的变量,用于存储特定的信息,如脚本名、参数个数等。可直接使用这些变量,但不能直接为这些变量赋值。
[root@svr7 opt]# vim script.sh //预定义变量
#!/bin/bash
echo "脚本名: \$0" //当前脚本名
echo $1 //执行脚本时,后面跟的第1个位置参数
echo $2 //执行脚本时,后面跟的第2个位置参数
echo $3 //执行脚本时,后面跟的第3个位置参数
echo "所有参数: $*" //所有的位置参数
echo "参数个数: $#" //位置参数的个数
echo "当前 Shell 进程的 PID: $$" //运行进程的PID号
echo "上一个命令的退出状态: $?" //判断上条命令执行后的依据(0表示正常、非0表示异常)
[root@svr7 opt]# bash test06.sh TOM JERY HAHA XIXI //引用位置变量
脚本名: ./script.sh
TOM
JERY
HAHA
所有参数: TOM JERY HAHA XIXI
参数个数: 4
当前 Shell 进程的 PID: 6758
上一个命令的退出状态: 0
例如:利用位置参数及预定义变量实现创建用户脚本
[root@svr7 opt]# vim test07.sh //创建脚本
#!/bin/bash
useradd $1
echo $2 | passwd --stdin $1
[root@svr7 opt]# bash test07.sh TOM 123 //创建用户TOM,密码123
更改用户 TOM 的密码 。
passwd:所有的身份验证令牌已经成功更新。
例如:将已有的局部变量声明为全局变量
[root@svr7 opt]# a=100
[root@svr7 opt]# bash //开启新子Shell
[root@svr7 opt]# echo $a
//结果为空
[root@svr7 opt]# export a //声明全局变量(必须在默认解释器运行)
[root@svr7 opt]# bash //临时切换新Shell环境(子Shell)
[root@svr7 opt]# echo $a
100
[root@svr7 opt]# bash //在新Shell环境再次切换到新Shell环境(子Shell)
[root@svr7 opt]# echo $a
100
[root@svr7 opt]# exit //返回一层
exit
[root@svr7 opt]# exit //返回到切换前的bash环境
exit
例如:直接创建全局变量,并将全局变量恢复成局部变量
[root@svr7 opt]# export b=200 //创建全局变量
[root@svr7 opt]# echo $b
200
[root@svr7 opt]# bash //开启子shell
[root@svr7 opt]# echo $b
200
[root@svr7 opt]# exit
exit
[root@svr7 opt]# export -n b //将全局变量恢复成局部变量
[root@svr7 opt]# echo $b
200
[root@svr7 opt]# bash
[root@svr7 opt]# echo $b
//结果为空
五、变量值的定界符
在 Shell 脚本中,变量值的定界符主要用于定义和引用变量的值。以下是一些常见的定界符及其用法:
1、字符串定界符
字符串定界符用于定义字符串类型的变量值。常见的字符串定界符有单引号(')和双引号(")。
1)常见三种定界符
- [ 双引号 “ ” ] 允许扩展,可以$引用其它变量;
- [ 单引号 ‘ ’ ] 禁用扩展,即便$也视为普通字符;(屏蔽特殊符号功能)
- [ 反撇号 ` ` ] 将命令的执行输出作为变量值;使用[ $() ]实现同样结果
单引号('
)
单引号用于定义一个字面量字符串,其中的所有字符都会被视为普通字符,不会进行任何变量替换或转义。
name='Alice'
echo 'Hello, $name' # 输出: Hello, $name
双引号("
)
双引号用于定义一个字符串,其中的变量和特殊字符会被替换或转义。
name="Alice"
echo "Hello, $name" # 输出: Hello, Alice
扩展:read标准输入取值
read从键盘读入变量值完成赋值
- 格式:read [ -p “提示信息” ] 变量名 //read提示符和变量之间要有空格
常用选项
[ p ] 可选(友好提示)
[ t ] 可指定超时秒数
终端显示控制
[ stty -echo ] 关闭终端输出(屏蔽回显)
[ stty echo ] 恢复终端输出(恢复显示)
例如:
[root@svr7 opt]# vim test07.sh
#!/bin/bash
read -p "请输入用户名:" user
useradd $user
stty -echo //屏蔽回显
read -p "请输入密码:" pass
stty echo //恢复显示
echo $pass | passwd --stdin $user
[root@svr7 opt]# bash test07.sh //运行脚本
请输入用户名:AQA
请输入密码:更改用户 AQA 的密码 。
passwd:所有的身份验证令牌已经成功更新。
注意:不加stty -echo显示控制时,密码会暴露显示,不安全
常见报错:read提示符和变量之间缺少空格,导致读取变量内容为空
[root@svr7 opt]# vim /opt/test09.sh
read -p "Please input pass:"pass //变量前未加空格
[root@svr7 opt]# bash /opt/test09.sh
Please input pass:pass
[root@svr7 opt]# echo $pass
//结果为空
分析故障原因:因为没有空格,read将提示符和变量识别为一个整体
2、数组定界符
在 Shell 中,数组可以使用括号(())来定义。数组元素之间用空格分隔。
fruits=("apple" "banana" "cherry")
echo ${fruits[0]} # 输出: apple
echo ${fruits[1]} # 输出: banana
echo ${fruits[2]} # 输出: cherry
3、分隔符
分隔符用于分隔不同的变量或命令。常见的分隔符有空格、逗号、分号等。
# 空格分隔符
var1="Hello"
var2="World"
echo $var1 $var2 # 输出: Hello World
# 逗号分隔符
echo "apple,banana,cherry" | cut -d',' -f1 # 输出: apple
# 分号分隔符
echo "Hello"; echo "World" # 输出: Hello
# World
4、转义字符
转义字符(\)用于转义特殊字符,使其成为普通字符。
echo "He said, \"Hello, World!\"" # 输出: He said, "Hello, World!"
5、注释定界符
注释定界符用于在脚本中添加注释,常见的注释定界符是井号(#)。
# 这是一个注释
echo "Hello, World!" # 这也是一个注释
六、Shell的运算
在 Shell 脚本中,进行整数运算有多种方法。以下是几种常见的方法:
expr
:适用于简单的整数运算,但语法稍显繁琐。$((...))
:简洁且功能强大,推荐使用。let
:适用于将结果赋值给变量。bc
:适用于高精度计算,包括浮点数运算。
1)基本运算类别
四则运算
- 加法:num1 + num2
- 减法:num1 - num2
- 乘法:num1 \* num2 //或’*’
- 除法:num1 / num2
取余数运算
- 求模:num1 % num2
1、expr运算工具(简单计算器)
expr 是一个用于表达式计算的命令行工具。它可以进行基本的整数运算,在UNIX/LINUX下求表达式变量的值,一般用于整数值,也可用于字符串(计算指定的表达式,并输出结果)
- 格式:expr int1 运算符 int2
注意:乘法需采用【\*】转义或【'*'】,避免被作为Shell通配符
注意:参与运算的整数值与运算操作符之间需要以空格分开,引用变量时必须加$符号
result=$(expr 5 + 3)
echo "5 + 3 = $result" # 输出: 5 + 3 = 8
result=$(expr 10 - 4)
echo "10 - 4 = $result" # 输出: 10 - 4 = 6
result=$(expr 6 \* 3) # 注意: 乘法需要转义
echo "6 * 3 = $result" # 输出: 6 * 3 = 18
result=$(expr 20 / 4)
echo "20 / 4 = $result" # 输出: 20 / 4 = 5
练习:
[root@svr7 ~]# X=10 //定义变量X
[root@svr7 ~]# expr $X + 18 //加法
28
[root@svr7 ~]# expr $X - 8 //减法
2
[root@svr7 ~]# expr $X \* 5 //乘法,操作符应添加\转义
50
[root@svr7 ~]# expr $X '*' 5 //乘法,操作符应添加\转义
50
[root@svr7 ~]# expr $X / 5 //除法,仅保留整除结果
2
[root@svr7 ~]# expr $X % 3 //求模(取余数)
1
[root@svr7 ~]# expr 30 / 3 / 2 //除法:30除3除2
5
[root@svr7 ~]# expr length “this is a test” //计算字串长度(包括空格)
14
[root@svr7 ~]# expr index "sarasara" a //抓取第一个字符数字串出现的位置
2
常见报错:运算符号两边必须要空格;
[root@svr7 opt]# expr 1+1
1+1
[root@svr7 opt]# expr 1 +1
expr: 语法错误
[root@svr7 opt]# expr 1+ 1
expr: 语法错误
常见报错:【*】表示通配符,直接输入会报错,可使用转义符【\】或单引号【‘’】
[root@svr7 opt]# expr 2 * 2
expr: 语法错误
2、使用 $[...]或 $((...)) 表达式
$((...))或 $[...]是一种更简洁的语法,用于进行整数运算。它支持基本的算术运算和一些高级运算。
- 格式:$[ int1 运算符 int2 ]
注意:乘法操作*无需转义;运算符两侧可以无空格;引用变量可省略$符号;
注意:计算结果替换表达式本身,可结合echo命令输出(方便做变量赋值)
result=$((5 + 3))
echo "5 + 3 = $result" # 输出: 5 + 3 = 8
result=$((10 - 4))
echo "10 - 4 = $result" # 输出: 10 - 4 = 6
result=$((6 * 3))
echo "6 * 3 = $result" # 输出: 6 * 3 = 18
result=$((20 / 4))
echo "20 / 4 = $result" # 输出: 20 / 4 = 5
练习:
[root@svr7 opt]# echo $[1+1] //相当于echo 2(计算结果替换表达式本身)
2
[root@svr7 opt]# $[1+1] //需结合echo命令输出
bash: 2: 未找到命令...
[root@svr7 opt]# 2
bash: 2: 未找到命令...
练习:
[root@svr7 opt]# a=1+1 //赋值变量,无法计算
[root@svr7 opt]# echo $a
1+1
[root@svr7 opt]# a=$[1+1] //赋值变量,可计算
[root@svr7 opt]# echo $a
2
[root@svr7 opt]# echo $[1+1] //加法
2
[root@svr7 opt]# echo $[2-1] //减法
1
[root@svr7 opt]# echo $[2*2] //乘法(无虚转义)
4
[root@svr7 opt]# echo $[10/2] //除法
5
[root@svr7 opt]# echo $[10%3] //取余
1
[root@svr7 opt]# a=10
[root@svr7 opt]# b=20
[root@svr7 opt]# echo $[a-b] //同等于echo $[$a-$b],引用变量可省略$符号
-10
[root@svr7 opt]# echo $[a-10]
0
[root@svr7 opt]# x=10
[root@svr7 opt]# echo $((x-1)),$((x*3))
9,30
[root@svr7 opt]# $((x-1)),$((x*3))
bash: 9,30: 未找到命令...
3、使用 let 命令
let 是一个内置命令,用于进行算术运算。它可以将结果赋值给变量。
let命令不显示结果,需要配合变量使用,主要用于创建变量或者变量的自增减
let result=5+3
echo "5 + 3 = $result" # 输出: 5 + 3 = 8
let result=10-4
echo "10 - 4 = $result" # 输出: 10 - 4 = 6
let result=6*3
echo "6 * 3 = $result" # 输出: 6 * 3 = 18
let result=20/4
echo "20 / 4 = $result" # 输出: 20 / 4 = 5
练习:对变量进行自增减
[root@svr7 opt]# let x=1+1 //同等于let x=2
[root@svr7 opt]# echo $x
2
[root@svr7 opt]# let x++ //加1(常规写法x=x+1,2+1=3)
[root@svr7 opt]# echo $x
3
[root@svr7 opt]# let x-- //减1(常规写法x=x-1,3-1=2)
[root@svr7 opt]# echo $x
2
[root@svr7 opt]# let x+=2 //加2(常规写法x=x+2,2+2=4)
[root@svr7 opt]# echo $x
4
[root@svr7 opt]# let x-=4 //减4(常规写法x=x-4,4-4=0)
[root@svr7 opt]# echo $x
0
[root@svr7 opt]# let x=10 //赋予变量
[root@svr7 opt]# let x%=3 //取余数(常规写法x=x%1,10%3)
[root@svr7 opt]# echo $x
1
[root@svr7 opt]# let x*=12 //乘法(常规写法x=x\*1,1*12)
[root@svr7 opt]# echo $x
12
[root@svr7 opt]# let x++;echo $x
13
[root@svr7 opt]# let x-=3;echo $x
10
4、使用 bc命令
bc 是一个高精度的计算器语言,可以进行浮点数运算,也可以用于整数运算。
- ①支持高精度的数值运算;
注意:expr工具、$[]算式替换都不支持有小数的运算;
- ②直接运行bc可进入交互式运算界面,quit退出;(可使用管道达到非交互)
- ③ 结合管道向bc发送表达式:
- 多个表达式以分号【;】分隔;
- 通过echo命令+管道传递要计算的表达式;
- 基本用法:echo “ 数值1 比较符 数值2 ” | bc
- 选项: scale=n 可约束小数位;
result=$(echo "5 + 3" | bc)
echo "5 + 3 = $result" # 输出: 5 + 3 = 8
result=$(echo "10 - 4" | bc)
echo "10 - 4 = $result" # 输出: 10 - 4 = 6
result=$(echo "6 * 3" | bc)
echo "6 * 3 = $result" # 输出: 6 * 3 = 18
result=$(echo "20 / 4" | bc)
echo "20 / 4 = $result" # 输出: 20 / 4 = 5
练习:
[root@svr7 ~]# echo "1.1+1" | bc
2.1
[root@svr7 ~]# echo "1.1+1;2.2+1" | bc //分号隔开,可实现多表达式
2.1
3.2
[root@svr7 ~]# echo "scale=3;10/3" | bc //除法,显示小数点后3位
3.333
[root@svr7 opt]# A=2.1
[root@svr7 opt]# echo "scale=4;$A*1.1" | bc //乘法
2.31
- ④ 小数值的比较
如果表达式成立,则返回的计算结果为1(正确),否则返回0(错误)
常见比较操作:> 、>= 、< 、
例如:
[root@svr7 opt]# A=12.34;B=56.78
[root@svr7 opt]# echo "$A<=$B" |bc //A是否小于或等于B
1
[root@svr7 opt]# echo "$A>$B" |bc //A是否大于B
0
[root@svr7 opt]# echo "$A==$B" |bc //A是否等于B
0
[root@svr7 opt]# echo "$A!=$B" |bc //A是否不等于B
1
扩展知识:&> /dev/null
&> /dev/null 是一个在 Shell 脚本中常用的重定向操作符组合,用于将命令的标准输出(stdout)和标准错误(stderr)都重定向到 /dev/null 设备。/dev/null 是一个特殊的文件,通常被称为“位桶”或“黑洞”,任何写入到它的数据都会被丢弃,不会被保存或显示。
- &>:这是一个复合重定向操作符,表示将标准输出和标准错误都重定向到指定的文件或设备
- /dev/null:这是一个特殊的文件系统设备,用于丢弃写入它的所有数据
隐藏命令输出:当你执行一个命令,但不希望在终端上看到任何输出时,可以使用 &> /dev/null。
清理日志:在脚本中,有时你可能不希望保存某些命令的输出,可以使用这个操作符来丢弃输出。
示例:
① 仅重定向标准输出:
some_command > /dev/null
② 仅重定向标准错误:
some_command 2> /dev/null
③ 将标准输出和标准错误分别重定向到不同文件:
some_command > output.log 2> error.log
④ 将标准输出和标准错误合并重定向到同一文件:
some_command &> combined.log
扩展知识:重定向标准输入/输出、错误输出
- >:将标准输出重定向到文件。
- 2>:将标准错误重定向到文件。
- >>:将标准输出追加到文件。
- 2>>:将标准错误追加到文件。
- |:管道操作符,将一个命令的标准输出作为另一个命令的标准输入。
1)重定向标准输出( > 、 >> )
- ① 使用 > 将命令执行的正常输出重定向到文件:
[root@svr7 ~]# ls -ld /etc/ //正常应输出到屏幕
drwxr-xr-x. 140 root root 8192 8月 2 04:45 /etc/
[root@svr7 ~]# ls -ld /etc/ > stdout.txt //重定向到文件
[root@svr7 ~]# cat stdout.txt //确认重定向输出的结果
drwxr-xr-x. 140 root root 8192 8月 2 04:45 /etc/
- ② > 操作会覆盖目标文件(先清空、再写入):
[root@svr7 ~]# echo "I am the king." > stdout.txt //覆盖目标文件
[root@svr7 ~]# cat stdout.txt //确认结果
I am the king.
- ③ 改用 >> 可实现追加重定向输出:
[root@svr7 ~]# ls -ld /etc/ >> stdout.txt //追加输出
[root@svr7 ~]# cat stdout.txt
I am the king. //原有内容还保留
drwxr-xr-x. 140 root root 8192 8月 2 04:45 /etc/
2)重定向标准错误输出( 2> 、 2>>)
- ① 对于命令执行中出现错误信息,在使用 > 无法保存错误信息,且仍然会输出到屏幕;
比如使用ls命令同时查看两个对象,其中nb.txt文件并不存在,重定向输出:
[root@svr7 ~]# ls -l nb.txt /etc/fstab > stderr.txt
[root@svr7 ~]# cat stderr.txt //正常信息成功重定向到目标文件,错误信息不保存
-rw-r--r--. 1 root root 541 1月 5 2017 /etc/fstab
② 使用 2> 可重定向错误信息,比如执行一个错误的命令:
[root@svr7 ~]# ls -l nb.txt /etc/fstab 2> stderr.txt
-rw-r--r--. 1 root root 541 1月 5 2017 /etc/fstab
正确的信息默认输出至屏幕,错误信息重定向到目标文件
[root@svr7 ~]# cat stderr.txt //从文件中查看出错信息
ls: nb.txt: 没有那个文件或目录
③ 使用2>> 可实现追加输出:
[root@svr7 ~]# ls tmpfile 2>> stderr.txt
[root@svr7 ~]# cat stderr.txt
ls: nb.txt: 没有那个文件或目录
ls: tmpfile: 没有那个文件或目录
3)将正常输出、错误输出重定向同一个文件( &> )
[root@svr7 ~]# ls -l nb.txt /etc/fstab &> stderr.txt
[root@svr7 ~]# cat stderr.txt
ls: nb.txt: 没有那个文件或目录
-rw-r--r--. 1 root root 541 1月 5 2017 /etc/fstab
经典搭配
[root@svr7 ~]# ls -l nb.txt /etc/fstab &> /dev/null
4)重定向标准输入( < )
[root@svr7 ~]# mail -s Error root < /etc/passwd
参考:https://blog.csdn.net/liucy007/article/details/90207830
输出即把相关对象通过输出设备(显示器等)显示出来,输出又分正确输出和错误输出;
一般情况下标准输出设备为显示器,标准输入设备为键盘。
Linux中用0代表标准输入,1代表标准正确输出,2代表标准错误输出。
输出重定向:正常输出是把内容输出到显示器上,而输出重定向是把内容输出到文件中
注意:错误输出重定向> / >>后边没有空格
命令 >> 文件 2>&1 和 命令 &>>文件 两个命令作用相同
注意:2>&1 和 &> 都是用于重定向标准错误输出的操作符。
- 语法简洁性:&> 语法更简洁,因为它一次性完成了两个重定向操作。
- 可读性:2>&1 更明确地展示了重定向的过程,对于初学者来说可能更容易理解。
- 兼容性:&> 在一些较旧的 shell 版本中可能不被支持,而 2>&1 是 POSIX 标准的一部分,几乎所有 Unix 和 Linux 系统都支持。
扩展知识:nohup命令
nohup 是一个在 Unix 和 Linux 系统中常用的命令,用于在用户退出终端会话后继续运行指定的命令。它的全称是 "no hangup",意味着即使终端挂起(例如用户退出登录),命令也会继续运行。
在默认情况下(非重定向时),会输出一个名叫 nohup.out 的文件到当前目录下,如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。
- 权限:所有使用者
- 格式:nohup command [arguments] &
command:你希望在后台运行的命令。
arguments:传递给命令的参数。
&:将命令放入后台运行。
示例:
假设你希望在后台运行一个长时间的任务,比如一个 Python 脚本:
nohup python myscript.py &
运行上述命令后,
nohup
会自动将输出重定向到一个文件nohup.out
,除非你指定了其他输出文件。
默认情况下,nohup
会将标准输出和标准错误输出重定向到 nohup.out
文件中。可通过重定向操作符自定义输出文件:
nohup python myscript.py > output.log 2>&1 &
- > output.log:将标准输出重定向到 output.log 文件
- 2>&1:将标准错误输出重定向到标准输出,即 output.log 文件
使用 jobs 或者 ps 命令 可以查看当前终端会话中的后台作业:
jobs
# 或者
ps aux | grep myscript.py
如果你需要终止后台进程,可以使用 kill
命令,首先找到进程的 PID(进程ID):
kill -9 PID
注意事项:
- nohup 不会自动将命令放入后台,你需要使用 & 来实现这一点。
- 如果命令需要交互输入,nohup 可能不适用,因为 nohup 会忽略挂起信号(SIGHUP),但不会处理交互输入。
- 使用 nohup 运行命令时,确保你有足够的权限和资源来执行该命令。
小结:
本篇章节为【第二阶段】SHELL-DAY1 的学习笔记,这篇笔记可以初步了解到 Shell概述,编写及执行脚本、Shell变量(自定义变量、环境变量、预定义变量、位置变量)、数值运算(expr工具、$[]、let、bc)。除此之外推荐参考相关学习网址:
Tip:毕竟两个人的智慧大于一个人的智慧,如果你不理解本章节的内容或需要相关笔记、视频,可私信小安,请不要害羞和回避,可以向他人请教,花点时间直到你真正的理解