shell脚本编程


第一部分 Shell 自动化运维编程基础

第一章 Shell 概述

1.1. 为什么学习和使用Shell编程

  • 简单易学

来源于C语言,很多语法很相近

  • 解释性语言,不需要编译即可执行

解释性语言是一种编程语言的执行方式,与编译性语言相对应。在解释性语言中,源代码不是直接转换为机器代码,而是通过解释器逐行解释和执行。

解释性语言的主要特点如下:

  1. 逐行解释:解释器会逐行读取源代码,并将其转换为机器可以理解和执行的指令。

  2. 不需要显式编译:解释性语言的源代码可以直接运行,无需进行显式的编译过程。这意味着开发者可以更快速地进行开发和测试。

  3. 动态类型:大多数解释性语言是动态类型的,可以在运行时进行类型检查。这使得变量的类型可以根据实际情况动态改变。

  4. 跨平台性:解释性语言通常可以在不同的操作系统上运行,因为解释器本身会根据不同的平台进行适配。

  5. 相对较慢:解释性语言的执行速度通常比编译性语言慢。由于每行代码都需要解释器进行解释和执行,相比直接执行机器代码,解释性语言的执行效率较低。

常见的解释性语言包括Python、JavaScript、Ruby、Perl等。这些语言具有灵活的语法和易于学习的特点,适用于快速原型开发、脚本编写、Web开发等多种应用场景。

解释性语言适合数据流业务的原因有以下几点:

  1. 灵活性:解释性语言通常具有更高的灵活性,能够适应不断变化的业务需求。这些语言通常提供了丰富的内置函数和库,使开发人员能够更快速地开发和修改代码。

  2. 跨平台支持:解释性语言往往具有跨平台的特性,可以在多种操作系统上运行,这对于流业务来说非常重要。不论是在服务器端还是客户端,解释性语言都能够提供良好的跨平台支持,保证应用程序能够全面覆盖不同的设备和操作系统。

  3. 实时性:解释性语言通常可以实现实时的数据处理和响应,这对于流业务来说非常关键。解释性语言的执行速度较快,可以迅速处理大量的实时数据,并给出结果。这使得解释性语言特别适合于处理实时流数据、推送服务、即时通讯等流程密集型业务。

  4. 简化开发:解释性语言通常具有直观的语法和较少的开发复杂性,使开发人员能够更加专注于业务逻辑和功能实现。相比于编译型语言,解释性语言更容易学习和使用,开发速度更快,能够快速迭代和调试代码。

总结起来,解释性语言由于其灵活性、跨平台支持、实时性和简化开发等特点,使其特别适合于处理流业务。它能够满足流业务实时性和快速迭代的需求,并且更易于开发人员使用和维护。

腾讯视频可以被归类为流业务。流业务是指基于网络实时传输媒体内容或数据的业务模式。腾讯视频是腾讯集团旗下的在线视频平台,通过流媒体技术提供了大量的在线视频内容,包括电影、电视剧、综艺节目和大量用户生成的内容等。用户可以通过互联网实时流式播放视频,无需下载到本地设备。

作为流业务,腾讯视频在提供高质量视频内容的同时,也注重实时性和用户体验。通过流媒体技术,腾讯视频能够将视频内容以流式方式传输给用户,而用户可以随时随地在线观看,无需等待下载完成。流业务的特点是实时性和即时性,能够满足用户对内容的实时需求,提供更加便捷的观看体验。

腾讯视频作为一个领先的在线视频平台,不仅提供了丰富多样的内容,还提供了个性化推荐、评论互动、VIP会员服务等功能,以满足用户的不同需求。通过不断改进和优化,腾讯视频持续提升在流业务领域的竞争力,为用户提供更好的观看体验和服务。

  • 对于一个合格的系统管理员来说,学习和掌握Shell编程是非常重要的,通过shell程序,可以在很大程度上简化日常的维护工作,使得管理员从简单的重复劳动中解脱出来

为社么要学习shell编程,不能用别的语言代替吗?
理由是:专业的人干专业的事

在脚本这个专业来讲的话,最适用的还得是shell和python,用别的语言也能间接达得到,但是从执行效率和理解性来讲还是不太好

在常见的自动化运维领域、云原生领域里面,对于脚本的日常分析以及统计、过滤,需要预先编写好的脚本。

所以日常运维工作人员在管理计算机的时候是通过事先预制的脚本来实现的,无论是查看计算机的性能,通过这个性能分析出相关的指标,是否需要人为去介入,都要使用预先编写好的脚本,如果现场去完成会来不及,如果服务器正受到攻击,那我们不可能现场去编写一个脚本去分析攻击的来源、攻击的目的以及怎么去修复数据,所以要预先针对不同的业务去编写脚本。

简而言之,这种脚本能使你从复杂的工作中解脱出来。      

1.2. Shell是什么

1.2.1. shell起源
  • 1964年,美国AT&T公司的贝尔实验室、麻省理工学院及美国通用电气公司共同参与开始研发一套可以安 装在大型主机上的多用户、多任务的操作系统,该操作系统的名称为Multics

美国AT公司很出名,在二战期间垄断了美国的远程电话和无线电业务。

  • 1970年,丹尼斯里奇和汤普逊启动了另外一个新的多用户、多任务的操作系统的项目,他们把这个项目 称之为UNICS
  • 1973年,使用C语言重写编写了Unix。通过这次编写,使得Unix得以移植到其他的小型机上面。
  • 1979年,第一个重要的标准UNIX ShellUnix的第7版中推出,并以作者史蒂夫伯恩 (StephenBourne)的名字命名,叫做Bourne Shell,简称为sh
  • 20世纪70年代末,C Shell作为2BSD UNIX的一部分发布,简称csh。之后又出现了许多其他的Shell程 序,主要包括Tenex C Shelltcsh)、Korn Shellksh)以及GNU Bourne-Again shellbash)。
1.2.2. 查看当前系统支持的shell
[root@redhathost20230911 ~]#  cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash

前两个是我们正在使用的两个版本的命令解释器的路径。

后两个是在/usr目录下备份的一份。

1.2.3. 查看当前系统默认shell
[root@redhathost20230911 ~]#  echo $SHELL
/bin/bash
# $:提取变量的值
1.2.4. Shell 概念
  • Shell(外壳):是一种命令解释器程序,它能识别用户输入的各种命令,并传递给操作系统
  • 结构图:
  • 真正能够控制计算机硬件(CPU、内存、显示器等)的只有操作系统内核(Kernel),图形界面和命令行 只是架设在用户和内核之间的一座桥梁,由于安全、复杂、繁琐等原因,用户不能直接接触内核(也没有 必要),需要另外再开发一个程序,让用户直接使用这个程序;该程序的作用就是接收用户的操作(点击 图标、输入命令),并进行简单的处理,然后再传递给内核,这样用户就能间接地使用操作系统内核
  • 用户界面和命令行就是这个另外开发的程序,就是这层代理。在Linux下,这个命令行程序叫做 Shell, Shell 是一个应用程序,它连接了用户和 Linux 内核,让用户能够更加高效、安全、低成本地使用 Linux 内核,这就是 Shell 的本质。
  • Shell 本身并不是内核的一部分,它只是站在内核的基础上编写的一个应用程序,它和 QQ、迅雷、 Firefox 等其它软件没有什么区别。然而 Shell 也有着它的特殊性,就是开机立马启动,并呈现在用户面 前;用户通过 Shell 来使用 Linux,不启动 Shell 的话,用户就没办法使用 Linux

1.3. Shell 程序设计语言

1.3.1. Shell 也是一种脚本语言
  • 任何代码最终都要被翻译成二进制的形式才能在计算机中执行。
  • 有的编程语言,如 C/C++PascalGo语言、汇编等,必须在程序运行之前将所有代码都翻译成二进制形 式,也就是生成可执行文件,用户拿到的是最终生成的可执行文件,看不到源码。这个过程叫做编译 Compile),这样的编程语言叫做编译型语言,完成编译过程的软件叫做编译器(Compiler)。
  • 有的编程语言,如 ShellJavaScriptPythonPHP等,需要一边执行一边翻译,不会生成任何可执行 文件,用户必须拿到源码才能运行程序。程序运行后会即时翻译,翻译完一部分执行一部分,不用等到所 有代码都翻译完。这个过程叫做解释,这样的编程语言叫做解释型语言或者脚本语言(Script),完成解释过程的软件叫做解释器。
  • 编译型语言的优点是执行速度快、对硬件要求低、保密性好,适合开发操作系统、大型应用程序、数据库 等。
  • 脚本语言的优点是使用灵活、部署容易、跨平台性好,非常适合 Web 开发以及小工具的制作。
  • Shell 就是一种脚本语言,我们编写完源码后不用编译,直接运行源码即可。
1.3.2. 用途
  • shell脚本的优势在于处理操作系统底层的业务 (linux系统内部的应用都是shell脚本完成)因为有大量的 linux系统命令为它做支撑。2000多个命令都是shell脚本编程的有力支撑,特别是grepawksed等。 例如:一键软件安装、优化、监控报警脚本,常规的业务应用,shell开发更简单快速,符合运维的简单、 易用、高效原则。
  • PHPPython优势在于开发运维工具以及web界面的管理工具,web业务的开发等。处理一键软件安 装、优化,报警脚本。常规业务的应用等php/python也是能够做到的。但是开发效率和复杂比用shell就 差很多了。

1.4. 如何学好shell

1.4.1. 熟练掌握shell编程基础知识
  • 熟练使用vivim)编辑器
  • 熟练掌握Linux基本命令
  • 熟练掌握文本三剑客工具(grepsedawk
  • 熟悉常用服务器部署、优化、日志及排错

1.5. Shell脚本的基本元素

        在文件名中加上 “.sh” 后缀通常表示该文件是一个 Shell 脚本文件。“.sh” 是用于标识 Shell 脚本文件的常见约定。

        Shell 脚本是一种编程脚本,主要用于在Unix、Linux和类Unix系统中执行命令和自动化任务。常见的 shell 脚本语言包括 bash(Bourne Again SHell)、sh(Bourne SHell)、csh(C SHell)和ksh(Korn SHell)等。通过将 “.sh” 后缀添加到文件名中,可以让其他用户或开发者迅速识别并理解该文件是一个 Shell 脚本文件。

        具体来说,例如,一个名为 “myscript.sh” 的文件通常被认为是一个 Shell 脚本文件,表示其中包含一段可以被 Shell 解释和执行的脚本代码。这样的文件可以在命令行中直接执行,例如通过运行 ./myscript.sh 来运行脚本。

        当然,后缀名只是一个约定,你可以选择不带后缀名或使用其他后缀名来表示 Shell 脚本文件。然而,使用 “.sh” 后缀是一种常见的做法,它有助于标识文件类型并遵循广泛接受的约定。

1.5.1. 基本元素构成
  • 1行的“#!/bin/bash”
  • 注释:说明某些代码的功能
  • 可执行语句:实现程序的功能
1.5.2. Shell脚本中的注释和风格
  • 作用:通过在代码中增加注释可以提高程序的可读性
  • 传统的Shell只支持单行注释,其表示方法是一个井号“#”,从该符号开始一直到行尾都属于注释的内容, 如:
#comment1
#comment2
#comment3
...
  • 多行注释:使用冒号“:”配合here document,语法如下:
:<<'xxxx'
comment1
comment2
comment3
……
xxxx
xxxx 可以是字符或数字,单引号可以不加,但以防出现莫名其妙的意外发生,比如发生字符扩展,命令 替换

1.6. Shell脚本编写规范

1.6.1. 脚本开头
  • 开头指定脚本解释器:#!/bin/sh #!/bin/bash

#!是一个特殊的东西,不是注释。

声明语句,声明此脚本所使用的解释器。        

解释器不一样,语法可能会有部分不一样。

  • 其他行#表示注释
  • 程序段开头需要加版本版权等信息,如:
# 此文件使用的解释器
# File Name:文件名称
# Version: 版本号
# Author: 作者
# Email:电子邮件
# Organization:参考网址
# Created Time:创建时间
# Description:简述
# 等
  • 脚本自动增加注释版权信息
[root@redhathost20230911 ~]# vim ~/.vimrc # 新建配置文件
autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"
  
func SetTitle()
    if expand("%:e") == 'sh'
        call setline(1,"#!/bin/bash")
        call setline(2,"##############################################################")
        call setline(3, "# File Name: ".expand("%"))
        call setline(4, "# Version: V1.0")
        call setline(5, "# Author: oldchild")
        call setline(6, "# Email: 2363543991@qq.com")
        call setline(7, "# Organization: https://blog.csdn.net/m0_63636799?spm=1000.2115.3001.5343")
        call setline(8, "# Created Time : ".strftime("%F %T"))
        call setline(9, "# Description:")
        call setline(10,"##############################################################")
        call setline(11, "")
    endif
endfunc

autocmd BufNewFile *.py,*.cc,*.sh,*.java exec ":call SetTitle()"

文件检索的识别问题,给.py,.cc,.sh,.java这些种类的文件增加自动化抬头信息 

1.6.2. 脚本中尽量不用中文注释
  • 别吝啬添加注释,必要的注释方便自己别人理解脚本逻辑和功能
  • 尽量用英文注释,防止本机或切换系统环境后中文乱码的困扰
  • 单行注释,可以放在代码行的尾部或代码行的上部
  • 多行注释,用于注解复杂的功能说明,可以放在程序体中,也可以放在代码块的开始部分 代码修改时,对修改的内容
1.6.3. 多使用内部命令
  • 无论碰到哪种情况,请尽量考虑使用内部命令而不是外部命令
  • 内部命令执行的效率高,性能好
1.6.4. 没有必要使用cat命令
  • 这是我们经常在论坛里讨论的话题之一。没有必要使用cat命令指的是在有些时候,我们会发现根本没有必要使用cat命令。使用了多余的cat命令会让你的代码看起来很丑陋,而且还会带来性能上的问题
  • 例如:以下两条命令的结果一样
[root@redhathost20230911 ~]# head -5 /etc/passwd | tail -1
[root@redhathost20230911 ~]# cat /etc/passwd | head -5 | tail -1

grep、head、tail等都能将文件内容显示到标准输出上,即屏幕上,没必要再使用cat多此一举。

1.6.5. 仔细阅读出错信息
  • 程序员常犯的一个错误是:当我们敲入的命令报错后,我们中的大多数人只是对错误信息一瞥而过,而不 会去认真的读一读,很多时候,错误信息里就包含了解决办法
  • 有时候我们修改了某个错误并再次运行后,系统依旧会报错。然后我们再次修改,但系统再次报错。这可能会持续很长时间。但实际上,旧的错误可能已经被纠正,只是由于出现了其它一些新错误才导致系统再次报错。而我们依旧在怀疑为什么修改好的代码依然不能正常运行。
  • 因此,请你养成仔细阅读错误信息的习惯。
1.6.6. 文件名以sh结尾
  • shell脚本文件名应见名知义 ,扩展名位sh,如:backup_mysql.sh
1.6.7. 代码缩进
  • shell没有强制要求,但建议缩进,这样可以提高阅读性,程序更有层次感
for ((i=1;i<10;i++))
do
    echo -ne "$i\t"
done

echo

for ((i=1;i<70;i++))
do
    echo -n "@"
done

echo

for ((i=1;i<10;i++))
do
    for ((j=1;j<=i;j++))
    do
        echo -en "$i*$j=$[i*j]\t"
    done
    echo
done

1.7. shell脚本执行

1.7.1. 方法1
  • 使用shbash命令执行脚本,不需要执行权限(建议使用),脚本中可以不指定解释器
[root@redhathost20230911 ~]# vim test.sh
#!/bin/bash
echo "china"
[root@redhathost20230911 ~]# bash test.sh
china
[root@redhathost20230911 ~]# sh test.sh
china
  • 可以使用bash -n 脚本名 ,进行语法检测,且不执行脚本
  • 可以使用bash -x 脚本名 ,进行脚本执行跟踪,逐条语句的跟踪执行
1.7.2. 方法2
  • 切换到脚本所在目录使用./执行脚本,需要执行权限,要临时赋权
[root@redhathost20230911 ~]# ./test.sh
-bash: ./test.sh: 权限不够
[root@redhathost20230911 ~]# chmod +x test.sh
[root@redhathost20230911 ~]# ./test.sh
1.7.3. 方法3
  • 绝对路径执行脚本,需要执行权限
[root@redhathost20230911 ~]# vim /t1.sh
#!/bin/bash
echo "china"
[root@redhathost20230911 ~]# /t1.sh
-bash: /t1.sh: 权限不够
[root@redhathost20230911 ~]# chmod +x /t1.sh
[root@redhathost20230911 ~]# /t1.sh
china
1.7.4. 方法4
  • 使用点(.)或者source 执行脚本,不需要执行权限
[root@redhathost20230911 ~]# source /t1.sh
china
[root@redhathost20230911 ~]# . test.sh
china

source的别名就是.        

1.7.5. 注意
  • 123都是启动一个子shell,在子shell中执行此脚本,脚本中设置的变量在脚本执行完毕后不会保存
  • 4 都是在当前shell进程中执行此脚本,而不是重新启动一个shell,在子shell进程中执行此脚本,并且脚本中设置的变量在脚本执行完毕后会保存下来。

前三种都是开辟一个子进程,每个子进程都有它独立的工作环境,如果你在脚本里面涉及到变量的申请定义的话,用前三种方法执行那些变量只会在子进程中生效,执行脚本的主进程不生效,在命令行窗口定义的变量是子进程里面的变量,不会影响主进程中定义的变量,但法4会影响。

也就是说,法1、2、3只会在子进程中生效,而法4会在当前进程中生效。

从安全的角度来讲,尽量用前三种,因为前三种会有一个环境变量级,相当于数据保护了。

1.8. bash shell基本功能

1.8.1. echo打印命令

格式:echo -参数 内容

参数

  • -n :取消输出后行末的换行符号
  • -e :启用转义字符

转义字符是在编程中使用的特殊字符序列,用于表示一些特殊字符或执行特定的操作。在许多编程语言和正则表达式中,反斜线(\)通常用作转义字符的前缀。

以下是几个常见的转义字符示例:

  1. \n:表示换行符。
  2. \t:表示制表符。

    在大多数编辑器和文本处理软件中,默认情况下,一个制表符(\t)通常相当于四个空格的宽度。这是一种常见的缩进风格,但实际上,制表符的显示宽度可以根据编辑器的设置而有所变化。

    有些编辑器允许用户自定义制表符的显示宽度,所以在特定的编辑器中,一个制表符可能等于两个空格、八个空格或其他数量。这种显示宽度设置通常用于满足个人或团队的偏好和需求。

  3. \r:表示回车符。
  4. \:表示反斜线本身。
  5. ":表示双引号。
  6. ':表示单引号。
  7. \xhh:表示一个以十六进制表示的字符,其中hh是两位十六进制数。
  8. \uhhhh:表示一个以Unicode编码表示的字符,其中hhhh是四位十六进制数。

可以输出带颜色的字体:

格式:echo -e "\e[字体控制;字体颜色或背景色 字符串内容 \e[0m"

  • \e[表示控制开始,\e[0m表示控制结束
  • 字体控制选项:1表示高亮,4表示下划线,5颜色闪烁
  • 颜色如下:字颜色:30-37 , 背景色:40-47
[root@redhathost20230911 ~]# vim color.sh
echo -e "\e[30m 黑色字\e[0m"
echo -e "\e[1;31m 紅色字\e[0m"
echo -e "\e[32m 綠色字\e[0m"
echo -e "\e[33m 黃色字\e[0m"
echo -e "\e[34m 藍色字\e[0m"
echo -e "\e[35m 紫色字\e[0m"
echo -e "\e[36m 天藍字\e[0m"
echo -e "\e[37m 白色字\e[0m"
echo -e "\e[40;37m 黑底白字\e[0m"
echo -e "\e[41;37m 紅底白字\e[0m"
echo -e "\e[42;37m 綠底白字\e[0m"
echo -e "\e[43;37m 黃底白字\e[0m"
echo -e "\e[44;37m 藍底白字\e[0m"
echo -e "\e[45;37m 紫底白字\e[0m"
echo -e "\e[46;37m 天藍底白字\e[0m"
echo -e "\e[47;30m 白底黑字\e[0m"
[root@redhathost20230911 ~]# bash color.sh
1.8.2. printf 命令
  • printf 命令模仿 C 程序库(library)里的 printf() 程序, 由 POSIX 标准所定义,因此使用 printf 的脚本比使用 echo 移植性好,printf 使用引用文本或空格分隔的参数,外面可以在 printf 中使用格式化字符串,还可以制定字符串的宽度、左右对齐方式等。默认 printf 不会像 echo 自动添加换行符,我们可以手动添加 \n
  • 格式:
printf 格式控制字符串 参数列表
  • 例:
[root@redhathost20230911 ~]# echo "Hello, Shell"
[root@redhathost20230911 ~]# printf "Hello, Shell\n"
[root@redhathost20230911 ~]# printf "%d %s\n" 1 "abc"
[root@redhathost20230911 ~]# printf '%d %s\n' 1 "abc" # 单引号双引号效果一样
[root@redhathost20230911 ~]# printf %s abcdef # 没有双引号也可输出,没有\n会续连下一个提示符
[root@redhathost20230911 ~]# printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg
# %s %c %d %f 都是格式替代符,%s 输出一个字符串,%d 整型输出,%c 输出一个字符,%f 输出实
数,以小数形式输出。
# %-10s 指一个宽度为 10 个字符(- 表示左对齐,没有则表示右对齐),任何字符都会被显示在 10 个字符
宽的字符内,如果不足则自动以空格填充,超过也会将内容全部显示出来。
[root@redhathost20230911 ~]# printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234
# %-4.2f 指格式化为小数,其中 .2 指保留2位小数
1.8.3. history历史命令
格式:
history [参数] [历史命令保存文件]

参数

  • -c :清空历史命令记录
  • -w:把缓存中的历史命令写入历史命令保存文件。如果不手工指定历史命令保存文件,则放入默认历史命令保存文件~/.bash_history
修改默认记录历史命令条数:
[root@redhathost20230911 ~]# vim /etc/profile
HISTSIZE=1000

显示写历史命令的时间

[root@redhathost20230911 ~]# vim ~/.bashrc
export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S: "

配置文件修改后,要使修改的东西生效,即刷新配置:

  • 重启
  • source ~/.bashrc
1.8.4. 命令与文件名补全:tab
1.8.5. 命令别名
  • 格式:alias 别名=原命令
  • 例:
[root@redhathost20230911 ~]# alias hi=history
[root@redhathost20230911 ~]# hi
  • 注意:别名的优先级比命令高,命令执行时的顺序如下:
第一顺位:执行用绝对路径或相对路径执行的命令。
第二顺位:执行别名。
第三顺位:执行 Bash 的内部命令。
第四顺位:执行按照 $PATH 环境变量定义的目录查找顺序找到的第一个命令。
  • 为了让这个别名永久生效,可以把别名写入环境变量配置文件“~/.bashrc”
[root@redhathost20230911 ~]# cat ~/.bashrc # 在最下面增加
1.8.6. 命令执行顺序
  • 顺序执行:
[root@redhathost20230911 ~]# date ; ls -l /etc/passwd
  • 前面命令执行不成功,后面的命令不执行: &&
[root@redhathost20230911 ~]# mkdir /mnt/iso && mount /dev/sr0 /mnt/iso
  • 前面命令成功,后面就不执行,如果前面不成功后面就执行: ||
[root@redhathost20230911 ~]# mkdir tt || ls /
[root@redhathost20230911 ~]# mkdir tt || ls / # 可以再次执行
1.8.7. 管道符
  • 符号:| ,当在两个命令之间设置管道时,管道符|左边命令的输出就变成了右边命令的输入。只要第一个 命令向标准输出写入,而第二个命令是从标准输入读取,那么这两个命令就可以形成一个管道
1.8.8. exit退出程序
  • 作用:终止Shell程序的执行
  • 格式:exit 状态码
  • 状态码:该参数是一个整数值,其取值范围为0~255
  • 注意:Shell程序的退出状态码储存在系统变量$?中,因此,用户可以通过该变量取得Shell程序返回给父进程的退出状态码
  • 常见状态码:
0 ---------------- 命令运行成功
1 ---------------- 通知未知错误
2 ---------------- 误用 shell 命令
126 ------------- 命令不可执行
127 ------------- 没有找到命令
128 ------------- 无效退出参数
128 + x-----------linux 信号 x 的严重错误
130 -------------- 命令通过 Ctrl + C 终止
255 -------------- 退出状态码越界
  • 演示在不同的情况下,程序返回不同的状态码
[root@redhathost20230911 ~]# echo "china"
china
[root@redhathost20230911 ~]# echo $?
0
[root@redhathost20230911 ~]# ehco "china"
bash: ehco: command not found...
Similar command is: 'echo'
[root@redhathost20230911 ~]# echo $?
127

第二章 变量和引用

1.1. 深入认识变量

1.1.1. 什么是变量
  • 变量是在程序中保存用户数据的一段内存存储空间,变量名是内存空间的首地址
1.1.2. 变量的名称

组成:字母、数字、下划线组成,不能以数字开头

变量名称的长度,shell没有明确规定,但是为了增加可读性,建议使用较短的、见名知意的名称命名

规则:

  • 首字符必须为:字母(a-z,A-Z)或下划线(_)
  • 中间不能有空格,可以使用下划线(_
  • 不能使用标点符号
  • 不能使用bash中关键字,输入help查看bash的保留字
  • 例:下面的变量名都是很好的选择
[root@server ~]# JAVA_HOME=/usr/bin/jvm/jre-1.6.0-openjdk.x86_64
[root@server ~]# SUM=0
[root@server ~]# back_up=/root
1.1.3. 变量的类型
  • 原则:shell是一种动态类型语言和弱类型语言,变量是不分数据类型的,统一都使用字符串存储,但根据变量的上下文环境,允许程序执行一些不同的操作,如:比较、整数加减
  • shell的变量数据类型
[root@server ~]# vim test1.sh

#!/bin/bash

# 定义变量x,输入初始值123
x=123

# 变量x加1

let "x+=1"

# 输出变量x的值
echo "x=$x"

# 替换x中1的值为abc,并赋值给变量y
y=${x/1/abc}

# 使用declare -i 声明变量y为整型变量(-i 表示整型)
declare -i y

# 输出y的值
echo "y=$y"

[root@server ~]# bash test1.sh
x=124
y=abc24
1.1.4. 变量的定义
  • 原则:直接使用,不需要变量声明
  • 格式:变量名=变量的值
  • 例:
[root@server ~]# vim test2.sh

#!/bin/bash
# 定义变量a
a=1

# 定义变量b
b="hello"

# 定义变量c
c="hello world"

# 定义路径
bak_dir=/data/backup
  • = 前后不能有空格
[root@server ~]# a= 3
bash: 3: command not found...
[root@server ~]# b =5
bash: b: command not found...
  • 字符串类型建议使用双引好作为定界符引起,尤其是字符串中有空格
[root@server ~]# stu_name=zhang san
bash: san: command not found...
[root@server ~]# stu_name="zhang san"
[root@server ~]# stu_name='zhang san'
1.1.5. 自定义变量
概念:上述以赋值形态形成的变量定义形式称为自定义变量
引用变量的值:
  • $变量名
  • ${表达式或变量名}
  • 例:
[root@server ~]# a=1024
[root@server ~]# echo $a
1024
[root@server ~]# echo ${a}
1024
  • 查看变量
[root@server ~]# set
[root@server ~]# declare
  • 取消变量:unset 变量名
[root@server ~]# a=256
[root@server ~]# echo $a
256
[root@server ~]# unset a
[root@server ~]# echo $a
  • 作用范围:只在当前shell起效
1.1.6. 环境变量
  • 环境变量又称为全局变量,可以在任意子shell生效,环境变量又分为自定义环境变量和bash内置的环境变量,用户退出命令后改变量会丢失,若需要永久保存就必须写在文件中
  • 定义环境变量
# 法1
export 环境变量=值
# 法2
变量名=值
export 变量名
# 法3
declare -x 变量名=值
  • ​​​​​​​​​​​​​​例:
[root@server ~]# export back_dir1=/home/backup
[root@server ~]# NAME="zhang san"
[root@server ~]# export NAME
[root@server ~]# declare -x AGE=20
[root@server ~]# env # 显示当前用户的环境变量
[root@server ~]# printenv # 同上
[root@server ~]# export # 同上
  • 注意:以上定义的环境变量都是临时的,重启后会失效,若要永久生效,则需要写入到配置文件中
  • 对比:
C 语言 局部变量 全局变量
shell 自定义变量 环境变量
shell 环境变量存储的文件:
  • bash shell 初始化文件有:/etc/profile ~/.bash_profile ~/.bash_login ~/.profile~/.bashrc/etc/bashrc
  • 如图:
  • /etc/profile :存放一些全局(共有)变量,不管哪个用户,登录时都会读取该文件。通常设置一些Shell变量PATH,USER,HOSTNAMEHISTSIZE
  • ~/.bash_profile:每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次,默认情况下,此文件通过脚本执行同目录下用户的.bashrc文件
  • ~/.bashrc:该文件包含专用于你的bash shellbash信息,当登录时以及每次打开新的shell,该该文件被读取
  • /etc/bashrc:为每一个运行bash shell的用户执行此文件.bash shell被打开时,该文件被读取
  • /etc/inputrc文件为特定的情况处理键盘映射
  • 执行顺序:/etc/profile-->/etc/profile.d/*.sh--> ~/.bash_profile -->/etc/bashrc-->~./.bashrc
结论:
  • 对于用户的环境变量设置,常见的是用户家目录下的.bashrc.bash_profile
  • 对于全局环境变量设置,常见的文件有:/etc/profile /etc/bashrc /etc/profile.d 这三个配置文件,常用方法是直接在/etc/profile文件中写入全局变量,如果想要在登陆后初始化或者显示加载的内容,只需要把脚本文件放在 /etc/profile.d 文件下即可
1.1.7. 位置变量
  • 概念:当一条命令或脚本执行时,后面可以跟多个参数,可以使用位置变量来表示该参数
sh test1.sh hello world 123 456
  • 当执行test1.sh 脚本时,第一个参数为hello到第四个参数可以使用特殊的符号表示,如:$1 $2 $3……
  • 常见的位置变量
$0 : 脚本名
$1-$9 : 1-9个参数
${10} :10以上的参数需要大花括号括起
$* : 所有参数
$@ : 所有参数
$# : 参数个数
$$ : 当前进程的PID
$! : 上一个后台进程的PID
$? : 上一个命令的返回值状态码,0为成功
  • 例:
[root@server ~]# vim test3.sh
#!/bin/bash
echo "第2个位置参数是: $2"
echo "第1个位值参数是: $1"
echo "第4个位置参数是: $4"
echo "所有参数是: $*"
echo "所有参数是: $@"
echo "参数的个数是: $#"
echo "当前进程的PID值: $$"
[root@server ~]# bash test3.sh 1 2 3 4 5
第2个位置参数是: 2
第1个位值参数是: 1
第4个位置参数是: 4
所有参数是: 1 2 3 4 5
所有参数是: 1 2 3 4 5
参数的个数是: 5
当前进程的PID值: 2986
  • $* 与 $@区别
当$* 和 $ @没有被引用的时候,它们确实没有什么区别,都会把位置参数当成一个个体, "$*"会把所有位置参
数当成一个整体(或者说当成一个单词),如果没有位置参数,则"$* "为空,如果有两个位置参数并且IFS为空
格时,"$*"相当于"$1 $2"
"$@"会把所有位置参数当成一个单独的字段,如果没有位置参数($#为0),则"$@"展开为空(不是空字符串,
而是空列表),如果存在一个位置参数,则"$@"相当于"$1",如果有两个参数,则"$@"相当于"$1" "$2"等等
[root@server ~]# set -- I am test command
[root@server ~]# for i in "$@"; do echo $i; done
I
am
test
command
[root@server ~]# for i in "$*"; do echo $i; done
I am test command

1.2. 变量赋值和作用域

1.2.1. 显示赋值:变量名=变量值
  • 例:
ip1=192.168.1.251
school="Peking University"
today1=`date +%F` # 注意为反引号``
today2=$(date +%F)
1.2.2. read 从键盘读入变量值
read 命令从标准输入中读取一行,并把输入行的每个字段的值指定给 shell 变量
格式: read - 参数 变量名
参数
  • -p “提示语句:” 屏幕打印出一行提示语句。
  • -n数字:当输入的字符数目达到预定数目时,自动退出,并将输入的数据赋值给变量,如:-n1 , 要接受到一个字符就退出。只要按下一个字符进行回答,read命令立即接受输入并将其传给变量。无需按回车键
  • -t 等待时间 :计时输入,使用read命令存在着潜在危险。脚本很可能会停下来一直等待用户的输入。如果无论是否输入数据脚本都必须继续执行,那么可以使用-t选项指定一个计时器。-t选项指定read命令等待输入的秒数。当计时满时,read命令返回一个非零退出状态
  • -s : 关闭回显,使read命令中输入的数据不显示在监视器上(实际上,数据是显示的,只是read令将文本颜色设置成与背景相同的颜色)
  • 常用格式:
read 变量名
read -p "提示信息:" 变量名
  • 例:
[root@server ~]# read -p "Enter Numbers: " num
Enter Numbers: 9527
[root@server ~]# echo $num
9527
[root@server ~]# read -t 3 n1 # 不要输入内容,等待3秒后自动结束输入
[root@server ~]# read -s -p "Enter your password: " passw
Enter your password: [root@server ~]# echo $passw
123456

[root@server ~]# echo $REPLY

[root@server ~]# read
100
[root@server ~]# echo $REPLY # 当输入时没有指定变量接收,会默认存储到REPLY变量中
100

# 一次性输入多个变量的值
[root@server ~]# read t1 t2
12 35
[root@server ~]# echo $t1 $t2
12 35
  • 面试题:总结4中赋值方式
1.直接赋值:name="li si"
2.read命令: read name
3.使用位置参数($1 $2 $3…) : name=$1
4.命令输入:name=$(whoami)
1.2.3. 变量和引号
  • 双引号:除了$ 、单引号、反引号、发斜线之外,其它被引起的内容保持字面意思
  • 单引号:所有字符保持字面意思
  • 反引号:被引起的字符串转为shell命令
  • 反斜线:转义符(\),屏蔽后面字符的特殊含义
1.2.4. 变量的作用域
  • 全局变量:全局变量定义在脚本中,也可以定义在函数中,作用范围:从定义的开始处到shell脚本结束或者被显示的去除
  • 例:
[root@server ~]# vim test5.sh

#!/bin/bash

func() # 定义函数
{
    echo "$v1"
    v1=200
}
v1=100
func
echo "$v1"


[root@server ~]# bash test5.sh
100
200
  • 函数内部定义全局变量
# 上例修改
[root@server ~]# vim test5.sh
#!/bin/bash

func() # 定义函数
{
    v2=200
}
func
echo "$v2"


[root@server ~]# bash test5.sh
200
  • 局部变量:范围更小,仅限于某个程序段中,如:函数、shell等,通过local关键字定义,注意:函数的参数也是局部变量
# 上例修改
[root@server ~]# vim test5.sh

#!/bin/bash

func() # 定义函数
{
    local v3=200 # 使用local关键字声明为局部变量
}
func
echo "$v3"
[root@server ~]# bash test5.sh
  • 全局变量和局部变量区别
# 上例修改
[root@server ~]# vim test5.sh

#!/bin/bash
func()
{
    #输出全局变量v1的值
    echo "global variable v1 is $v1"
    #定义局部变量v1
    local v1=2
    #输出局部变量v1的值
    echo "local variable v1 is $v1"
}

#定义全局变量v1
v1=1
#调用函数
func
#输出全局变量v1的值
echo "global variable v1 is $v1"
1.2.5. 变量的运算
  • 运算符与命令
Shell  中常见的算术运算符号
算术运算符意义(*表示常用)
+、-加法(或正号)、减法(或正号) *
*、/、%乘法、除法、取余(取模) *
**幂运算*
++、--增加及减少、可前置也可放在变量结尾  *
!、&&、||逻辑非(取反)、逻辑与(and)、逻辑或(or)*
<、<=、>、>=比较符号(小于、小于等于、大于、大于等于)
==、!=、=比较符号(相等、不相等、对于字符串“=”也可以表示相当于) *
<<、>>向左移位、向右移位
~、|、&、^按位取反、按位异或、按位与、按位或
=、+=、-=、*=、/=、%=赋值运算符,例如a+=1相当于a=a+1  *

<<、>>

在shell编程中,“<<” 和 “>>” 运算符具有不同的用途:

  1. “<<” 运算符(也称为 Here Document)用于输入重定向,允许将多行文本作为命令的输入。它的语法如下:
command << delimiter
    content
delimiter

在这个语法中,“delimiter” 是一个用户定义的标记,用于标识文本的开始和结束。“content” 是要作为命令输入的文本内容。在执行命令时,shell会将 “content” 中的文本作为输入传递给命令。
示例:

cat << END
这是一段文本
可以包含多行
END

上述代码将输出两行文本:“这是一段文本” 和 “可以包含多行”。

  1. “>>” 运算符用于输出重定向,将命令的输出追加到指定文件的末尾。它的语法如下:
command >> file

在这个语法中,“command” 是要执行的命令,它的输出将被追加到名为 “file” 的文件的末尾。如果文件不存在,则会创建一个新文件。

示例:

echo "这是追加到文件的文本" >> output.txt

上述代码将把文本 “这是追加到文件的文本” 追加到名为 “output.txt” 的文件的末尾。

~、|、&、^

在shell编程中,以下是有关特殊字符和运算符的解释:

  1. “~”(波浪号):在shell中,波浪号代表用户的家目录路径。例如,“~” 表示当前用户的家目录,而 “~username” 表示名为 “username” 的用户的家目录。

  2. “|”(竖线,管道符):竖线或称为管道符用于将一个命令的输出传递给另一个命令的输入,实现命令之间的协作。通过使用管道符,可以将多个命令串联在一起,实现复杂的操作。例如,使用 command1 | command2 将命令 command1 的输出作为 command2 的输入。

  3. “&”(和符号):和符号一般用于将命令放到后台执行。如果在命令的末尾添加 “&” 符号,该命令将在后台运行,允许你继续在终端中输入其他命令,而不需要等待该命令执行完毕。

  4. “^”(异或符号):在shell中,异或符号用于按位异或运算。它对两个操作数的对应位进行异或操作,结果为 1 表示不同,结果为 0 表示相同。例如,a=$((5 ^ 3)) 将得到 a 的值为 6,因为 5 的二进制表示为 101,3 的二进制表示为 011,异或运算结果为 110,转换为十进制为 6。

Shell  中常见的算术运算命令
运算操作符与运算命令意义
(())用于整数运算的常用运算符,效率很高
let用于整数运算,类似于“(())”
expr可用于整数运算,但还有很多其他的额外功能
bcLinux 下的一个计算器程序(适合整数及小数运算)
$[]用于整数运算
awkawk既可以用于整数运算,也可以用于小数运算
declare定义变量值和属性,-i参数可以用于定义整形变量,做运算
  • 示例1
[root@redhathost20230911 ~]# expr 1 + 1 # 注意+左右必须要有空格
2
[root@redhathost20230911 ~]# expr 1+1 # 否则原样显示
1+1
[root@redhathost20230911 ~]# a=1
[root@redhathost20230911 ~]# b=2
[root@redhathost20230911 ~]# expr $a + $b # 支持变量
3
[root@redhathost20230911 ~]# let num=1+2 # let+echo 等价于expr
[root@redhathost20230911 ~]# echo $num
3
[root@redhathost20230911 ~]# let num=1 + 2 # 注意let中运算符左右不能由空格
-bash: let: +:语法错误: 需要操作数 (错误符号是 "+")
[root@redhathost20230911 ~]# echo $((1+2))
3
[root@redhathost20230911 ~]# echo $((5%3))
2
[root@redhathost20230911 ~]# echo $((3%5))
3
[root@redhathost20230911 ~]# echo $((1-5))
-4
[root@redhathost20230911 ~]# echo $((2 * 5)) # 可以有空格
10
[root@redhathost20230911 ~]# echo $((5.2-5)) # 只支持整数运算
-bash: 5.2-5:语法错误: 无效的算术运算符 (错误符号是 ".2-5")
[root@redhathost20230911 ~]# echo $[2+3] # [] 等价于 (())
5
[root@redhathost20230911 ~]# echo $[2.5+3] # # 只支持整数运算
-bash: 2.5+3:语法错误: 无效的算术运算符 (错误符号是 ".5+3")
[root@redhathost20230911 ~]# bc
bc 1.07.1
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software
Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
1.1+2
3.1
2.5-3.9
-1.4
1.5>4
0
5>3
1
quit # 退出交互模式
# 不进入交互模式直接使用,bc需要放在最后
[root@redhathost20230911 ~]# echo "scale=3;11/3" | bc
3.666
# scale=3 表示保留运算精度
  • 示例2:
# 返回变量长度
[root@server ~]# str1="hello world"
[root@server ~]# echo ${#str1}
11
# 变量截取
[root@redhathost20230911 ~]# echo ${str1:0:3} # 从左边第1个字符开始截取3个
hel
[root@redhathost20230911 ~]# echo ${str1::3} # 可以省略起始0
hel
[root@redhathost20230911 ~]# echo ${str1:1} # 从下标1开始截取到尾部
ello world
[root@redhathost20230911 ~]# echo ${str1:0-1:1} # 从右边第一个字符开始截取1个,左边第一个为0,右边第
一个为0-1
d
[root@redhathost20230911 ~]# echo ${str1:0-5} # 从右边第5个开始截取到尾部
world
[root@redhathost20230911 ~]# echo ${str1: -5} # 使用空格替代0,同上
world
[root@redhathost20230911 ~]# echo ${str1:-5} # 没有空格表示提取整串
hello world

本文所涉及的他人内容包括但不限于文字、图片、音频、视频等,来源于各个渠道和资源,并非本文作者原创。在使用他人内容时,本文作者已经尽力确保遵循适用的版权法律和相关规定,并尽力寻找和确认素材的合法来源。

在此声明中的他人内容的使用,仅出于分享和传播信息的目的,并不代表本文作者对其内容的观点或立场进行支持或认可。本文作者对他人内容的准确性、完整性或时效性不承担责任,并且不保证这些素材没有侵犯任何第三方的知识产权。

素材来源:

  • 西安鸥鹏

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我和程序有一个能跑就行

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值