Linux云计算 |【第二阶段】SHELL-DAY1

主要内容:

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 脚本不仅需要实现预期的功能,还应该具备良好的结构、注释和错误处理机制。

  1. Shebang 行:脚本的第一行应该是 Shebang 行,指定脚本解释器。
  2. 脚本描述:在 Shebang 行之后,添加一段简短的描述,说明脚本的用途和功能。
  3. 版权和版本信息:添加版权和版本信息,以便其他人了解脚本的来源和更新情况。
  4. 环境变量和全局变量:使用大写字母命名环境变量,小写字母命名全局变量,以示区分。
  5. 函数定义:将复杂的逻辑封装在函数中,提高代码的可读性和可维护性。
  6. 主要逻辑:调用之前定义的函数,并处理可能的输入和输出。
  7. 错误处理:在脚本中添加错误处理机制,确保在出现错误时能够及时通知用户并采取适当的措施。
  8. 清理和收尾:在脚本结束前,进行必要的清理工作,如删除临时文件、恢复环境变量等。
  9. 文档和注释:在脚本中添加详细的注释,解释每个部分的功能。

示例脚本:

#!/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:毕竟两个人的智慧大于一个人的智慧,如果你不理解本章节的内容或需要相关笔记、视频,可私信小安,请不要害羞和回避,可以向他人请教,花点时间直到你真正的理解

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小安运维日记

Hey~ 感谢您的充电支持!!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值