bash 脚本编程基础–变量

bash 脚本编程基础–变量

shell脚本语言是实现Linux/UNIX系统自动化运维的重要工具

bash 基础知识介绍

shell 与 shell 脚本

  • shell
    shell位于操作系统最外层,是用户与操作系统交互的工具。shell 是一种命令解释器,将用户输入的命令解释给操作系统执行,并处理操作系统的各种输出结果。

  • shell脚本
    当我们将多条命令保存到文件中,便可方便、快速的执行多条命令。该文件便是shell脚本,shell脚本里不仅可以包含多条命令,还可以使用变量,流程控制语句(选择、循环、顺序)。

程序编程风格

程序是由指令+数据组成,程序编程风格有两种,过程式和对象式。

  • 过程式:以指令为中心,数据服务于指令,面向过程首先分析解决问题的所有可能情况,然后将这些解决方法整理成各个步骤,然后按步骤通过调用函数等方式顺序执行。
  • 对像式:以数据为中心,指令服务于数据,面向对象是将一切事物看做对象,甚至解决问题的思维也抽象成对象,对象并不是解决问题的具体方法,而是解决问题的模板,解决具体问题再将对象实例化。

shell脚本属于过程式编程风格,过程式编程语言有如下特点

  • 顺序执行
  • 选择执行
  • 循环执行

编译执行与解释执行

计算机只能识别二进制的文件,执行二进制指令,所有人类编好的程序源码(文本文件)必须翻译成二进制数据后,计算机才能理解。翻译的方式有两种,一种是由编译器将源码翻译成目标二进制文件后由计算机统一执行,这个过程称为编译执行;另一种是每输入一行命令,解释器就解释执行一行命令,这个过程称为解释执行。解释执行操作方便,但执行效率相对较低。C,C++,JAVA都是编译执行,Bash脚本是解释执行。

另外需澄清的一点是,低级语言与高级语言之间并不存在好坏之分,所谓低级与高级是指相对于人类而言,还是相对机器而言,越接近机器的语言越低级,越接近人类的语言越高级。高级语言让人类更容易理解,但执行效率相对较低,而低级语言反之。比如汇编语言相对C语言属于低级语言,而C语言相对Python是低级语言,但他们的执行效率汇编是最高的。

变量、变量名与内存的关系

所有程序在未启动为进程之前都是存储在磁盘上的文件,CPU要执行程序,必须把程序启动为进程,即将磁盘上的程序包括数据加载到内存中,然后按程序的流程执行指令。显然CPU要能够从内存中将指令和数据读取到CPU内部才能执行它,CPU必然要知道要从内存中的哪块区域去读取下一条要执行的指令。为了达到此目的,内存将自己划分成了许多区域,每块区域都有一个地址编码,CPU就是根据该编码找到指令和数据的。内存中的某些区域的数据可能会要经常被使用到,我们可以记住这些区域的地址,使用到的时候就可以按地址去查找,这些区域便是变量,变量其实就是内存区域。这些地址都是数字,对于计算机而言很容易,但对人类来说简直就是痛苦,就像人们喜欢记住网站的域名,不喜欢记网站服务器的IP地址一样,所以我们将这些地址起一个名字,便是变量名。实际上为了安全起见,操作系统一般也不允许我们编写的代码里自己规划、使用内存空间,而是由操作系统分配给我们。

创建bash脚本

创建脚本的一般步骤为

  1. 编写脚本,一般使用vim作为编写脚本的编辑器
  2. 调试脚本
    bash -n script_name #检查语法
    bash -x script_name #调试
#如下为一个四则运算,判断用户输入的数字及运算符是否符合要求,符合要求再进行运算
#!/bin/bash

#---------------------------------------------
# Author: zhuenbiao
# File Name: arithmetic_operation.sh
# Create Time: 2017-07-28 22:50:08
# Last Modified: 2017-07-28 22:50:08
# Description:
# Version: 0.1
#---------------------------------------------

## check if interager of input number
#check_int() {
# if [ -n "`echo "$1" | sed 's/[[:digit:]]//g'`" ]; then
# echo "please input one interager "
# exit 1
# fi
#}

# check if interager of input number(next method)
check_int() {
if [ `expr match "$1" "[[:digit:]]\+$"` = 0 ];then
echo "please input one interager "
exit 1
fi
}

# check weather in "+,-,*,/" of input operator
check_operator() {
if [ "${#1}" -ne "1" ];then
echo "please input only one operator"
exit 1
fi

if [ -z "`echo "$1" | egrep -o '[-\+\*/]' `" ]; then
echo "please input operator in '+,-,*,/' "
exit 1
fi
}

# read first number
read -p "please input first number:" num1
check_int "$num1"

# read operator
read -p "please input one operator:" operator
check_operator "$operator"

# read next number
read -p "please input next number: " num2
check_int "$num2"

# calculation
echo "$num1 $operator $num2 = $((${num1}${operator}${num2}))"

----------------------------------------------------------
[centos7@root math]# bash -n arithmetic_operation <=检查语法
[centos7@root math]# bash -x arithmetic_operation <=调试
+ read -p 'please input first number:' num1
please input first number:10 <=输入运算的第一个整数
+ check_int 10
++ expr match 10 '[[:digit:]]\+$'
+ '[' 2 = 0 ']'
+ read -p 'please input one operator:' operator
please input one operator:+ <=输入运算符
+ check_operator +
+ '[' 1 -ne 1 ']'
++ echo +
++ egrep -o '[-\+\*/]'
+ '[' -z + ']'
+ read -p 'please input next number: ' num2
please input next number: 5 <=输入运算的第二个整数
+ check_int 5
++ expr match 5 '[[:digit:]]\+$'
+ '[' 1 = 0 ']'
+ echo '10 + 5 = 15'
10 + 5 = 15 <=执行结果
  1. 执行脚本,执行脚本之前一般给脚本执行权限,执行脚本有如下方式

    • bash script_name 不需执行权限
    • path/script_name,或 ./script_name 需要执行权限
    • source script_name. script_name 需要执行权限

    其中前面两种是新建子进程,在子进程中执行脚本,不会影响当前进程的环境(变量,操作),而第三钟是在当前进程中执行脚本,直接影响当前shell进程的环境。

  2. 可以将经常执行脚本的目录添加到PATH路径中,这样就可以直接执行脚本

[centos7@root tools]# tail -n 2 /etc/bashrc
# Define PATH
PATH="$PATH:/app/scripts/tools:/app/scripts/test"

bash 脚本的组成结构

  1. 脚本首行声明使用何种shell解释执行该脚本,虽然在bash环境中执行bash脚本不声明也能执行,但需按照编程规范声明。
    linux系统中常见的脚本解释器
    /bin/bash
    /usr/bin/perl
    /usr/bin/python

  2. 规范的注释,声明了shebang机制后,接着写该脚本的一些注释信息,如作者,文件名,创建时间,描述信息,版本等。

  3. 后面接着便是脚本的正文部分,该部分包括命令,变量,流程控制语句,我们编写脚本应该养成良好的习惯,比如规范的缩进,详细的注释,见名知义的命名。

#!/bin/bash

#---------------------------------------------
# Author: zhuenbiao
# File Name: arithmetic_operation.sh
# Create Time: 2017-07-28 22:50:08
# Last Modified: 2017-07-28 22:50:08
# Description:
# Version: 0.1
#---------------------------------------------

## check if interager of input number
#check_int() {
# if [ -n "`echo "$1" | sed 's/[[:digit:]]//g'`" ]; then
# echo "please input one interager "
# exit 1
# fi
#}

# check if interager of input number(next method)
check_int() {
if [ `expr match "$1" "[[:digit:]]\+$"` = 0 ];then
echo "please input one interager "
exit 1
fi
}

bash 变量

相对于其他语言,bash脚本是不区分变量类型的,包括数字,bash也将其当做字符来存储。

bash中根据变量的作用范围不同可分为:

  • 环境变量
    环境变量也叫全局变量,一旦声明,当前进程及子进程中都生效。但父进程中是无效的。
  • 本地变量
    本地变量的生效范围仅在当前进程中有效,其子进程中都无效。
  • 局部变量
    bash脚本中的某段代码片段中有效。

变量的赋值
普通变量

  • 直接赋值:var_name=”hello everyone”
  • 变量引用:var_name=$another_var
  • 命令引用:var_name=`date +%F`

环境变量的赋值

  • export var_name=value
  • declare -x var_name=value
# 方法一
[centos7@root math]# export num1=10

#方法二
[centos7@root math]# num2=20
[centos7@root math]# export num2

#方法三
[centos7@root math]# declare -x num3=30
[centos7@root math]# echo $num3

变量引用

  • $var_name
  • ${var_name}

删除变量
unset var_name

定义只读变量
只读变量一旦定义,将无法重新赋值,删除,只有退出当前进程才可以消除

  • readonly var_name=value
  • declare -r var_name=value
    查看只读变量: readonly -p

查看变量

  • 查看系统变量 env,printenv,declare -x
  • 查看所有变量 set

bash 脚本实例

题一
当我们创建脚本时,总有一些重复性的工作,比如声明shebang,脚本头部说明信息,给脚本执行权限,这些重复性的工作有了脚本就应该告别

如下,我们编写脚本实现执行命令init_scripts.sh script_name就自动创建初始化好相应信息的脚本script_name

[centos7@root tools]# cat init_scripts.sh 
#!/bin/bash

#---------------------------------------------
# Author: zhuenbiao
# File Name: init_scripts
# Create Time: 2017-07-28 17:45:00
# Last Modified: 2017-07-28 17:45:00
# Description: automatically create head of
# a script file
# Version: 0.1
#---------------------------------------------

# Check if input the file name
if [ "$#" -ne "1" ]; then
echo "Input one file name"
exit 1
fi

# Check if the file already exists
if [ -a $1 ]; then
echo "The file \"$1\" already exists"
exit 1
fi

# automatically create head of a script file
cat > $1 <<EOF
#!/bin/bash

#---------------------------------------------
# Author: zhuenbiao
# File Name: `echo $1`
# Create Time: `date +"%F %T"`
# Last Modified: `date +"%F %T"`
# Description:
# Version: 0.1
#---------------------------------------------


EOF

chmod a+x $1
vim + $1

执行结果

[centos7@root test]# init_scripts.sh new_script
[centos7@root test]# cat new_script
#!/bin/bash

#---------------------------------------------
# Author: zhuenbiao
# File Name: new_script
# Create Time: 2017-07-29 21:29:26
# Last Modified: 2017-07-29 21:29:26
# Description:
# Version: 0.1
#---------------------------------------------