Shell Script之Bash基本概念及特性

本章内容包括有:终端打印,变量与环境变量,通过shell进行数学运算,文件描述符与重定向,数组和关联数组,使用别名等等内容。

1.1 简介

(1)shell脚本不过就是一些文件,我们能将一系列需要执行的命令写入其中,然后通过shell来执行这些脚本。

(2)打开终端后,会出现一个提示符(如username@hostname$或root@hostname#),$表示普通用户,#表示超级用户(root user)。

(3)shell脚本通常是以一个#!起始的文本文件:
#!/bin/bash

(4)有两种运行脚本的方式。一种是将脚本作为sh命令参数,另一种是将脚本作为具有执行权限的可执行文件。
1)$ sh script.sh    #假设脚本位于当前目录下
2)$ /home/path/script.sh    #使用script.sh的完整路径

(5)为了使shell脚本能够自己独立运行,需要具备可执行权限。要使脚本独立运行,必须利用shebang行(#!/bin/bash)。它通过使用位于#!之后的解释器来运行脚本。至于脚本的可执行权限可通过以下方式设置:
$ chmod a+x script.sh

(6)在Bash中,每个命令或是命令序列是通过使用分号换行符来分隔的(明白这点有助于理解if等语句中用分号或换行符的等价转换):
$cmd1 ; cmd2
等同于:
$ cmd1
$ cmd2

1.2 终端打印

echo是用于终端打印的基本命令。在默认情况下,echo在每次调用后会添加一个换行符。

(1)使用带双引号的文本,结合echo命令就可以将文本在终端打印出来。类似地,不带双引号的文本也可以得到同样的输出结果:

使用单引号也可以完成同样的任务,这些方法看起来相似,但还是存在些差异。

1)考虑如下示例即可明白:

2)使用单引号的echo时,Bash不会对单引号中的变量(如$var)求值,而只是照原样显示。

另一个可用于终端打印的命令是printf。printf使用的参数和C语言中的printf函数一样。

(2)例如:$ printf “Hello world”
printf使用引用文本或由空格分隔的参数。我们可以在printf中使用格式化字符串。还可以指定字符串的宽度、左右对齐方式等。在默认情况下,printf并不像echo命令一样会自动添加换行符,如需,我们需要手动添加:

输出:

1)%s, %c, %d和%f都是格式替代符,其所对应的参数可以置于带引号的格式字符串之后。
2)%-5s指明了一个格式为左对齐且宽度为5的字符串替代(-表示左对齐)。如果不用-指定对齐方式,字符串则采用右对齐形式。
3)宽度指定了保留给某个变量的字符数。对Name而言,保留宽度是10。如果内容不足10字符,余下以空格符填充
4)对于浮点数,可以对小数进行舍入。对于Mark字段,%-4.2f,其中.2指定保留2个小数位。\n表示换行。

(3)在默认情况下,echo会将一个换行符追加到输出文本的尾部。可以使用选项-n来忽略结尾的换行符。

1.3 变量和环境变量

变量是任何一种编程语言必不可少的组成部分,用于存放各类数据。脚本语言通常不需要再使用变量之前声明其类型,只需要直接赋值就可以了。在Bash中,每一个变量的值都是字符串。

有一些特殊的变量会被shell环境和操作系统环境用来存储一些特别的值,这类变量被称为环境变量。

(1)可以使用env命令在终端中查看所有与此终端进程相关的环境变量。对于每个进程,在其运行时的环境变量可以使用如下命令来查看:
cat /proc/$PID/environ

1)假设有一个叫做mysql的应用程序在运行。首先可以使用pgrep命令获得mysql的进程ID

2)上述每一个变量以name=value的形式来描述,彼此之间有null字符(\0)分割。如果我们将\0替换成\n,那么就可以将输出重新格式化:

(2)一个变量可以通过以下方式进行赋值:
var=value
var是变量名,value是赋给变量的值。如果value不包含任何空白字符,那么它不需要使用引号,反之,则必须使用单引号或双引号。

1)注意,var  =  value不同于var=value。把var=value写成var  =  value是一个常见的错误,但前者是赋值操作,后者是相等操作。

2)在变量名前加上$就可以打印出变量的内容:
var=”value”
echo $var
或者
echo ${var}

3)环境变量是未在当前进程中定义,而从父进程中继承而来的变量。export命令用来设置环境变量。在默认情况下,有很多标准环境变量可供shell使用。PATH就是其中之一。

在给出所要执行的命令后,shell自动在PATH环境变量所包含的目录列表中查找对应的可执行文件。

4)如果需要在PATH中添加一条新路径,可以使用:

还有一些众所周知的环境变量:HOME, PWD, USER, UID, SHELL等。

(4)获得字符串长度,可以使用下面的方法获得变量值长度:
length=${#var}

1.4 通过shell进行数学计算

在Bash shell环境中,可以利用let(())[ ]执行基本的算术操作。而在进行高级操作时,expr和bc这两个工具也会非常有用。

(1)可以用普通的变量赋值方法定义数值,这时,他会被存储为字符串。然而,我们可以用一些方法使它像数字一样进行处理。
#!/bin/bash
no1=4;
no2=5;

1)let命令可以直接执行基本的算数操作,当使用let时,变量名前不需要再加$,例如:

此外,自加操作:
let no1++
自减操作:
let no1–
简写形式:
let no+=6    #等同no=no+6
let no-=6    #等同no=no-6

2)操作符[ ]的使用和let命令类似:

3)expr同样可以用于基本算数操作:

以上这些方法只能用于整数运算,而不支持浮点数。

4)bc是一个用于数学运算的高级工具,可以利用它执行浮点数运算并应用一些高级函数:

1.5 文件描述符和重定向

文件描述符是与文件输入、输出相关联的整数。文件描述符0、1、2是系统预留的。

0——stdin(标准输入)
1——stdout(标准输出)
2——stderr(标准错误)

(1)如下,将输出文本重定向或保存到一个文件中:
$ echo “This is a sample text 1” > temp.txt
这种方式会先使temp.txt里的内容被清空。如下方法是将文本追加到目标文件中:
$ echo “This is a sample text 2” >> temp.txt

和>>并不相同,前者会清空文件在写入内容;后者将内容追加到现有文件的尾部。并且1>等同于>;1>>等同于>>。

(2)标准错误,当命令输出标准错误信息时,stderr信息就会被打印出来:

标准错误重定向:

1.6 数组和关联数组

数组是shell脚本非常重要的组成部分,它借助索引将多个独立的数据存储为一个集合。

(1)Bash同时支持普通数组和关联数组。普通数组只能使用整数作为数组索引,而关联数组可以使用字符串作为数组索引。

(2)可以在单行中使用一列值来定义一个数组:
array_var=(1 2 3 4 5 6)    #注意,此处是( )而非{ },并且元素间是以空格分隔!
也可以将数组定义成一组索引-值:

(3)关联数组

1)在关联数组中,我们可以用任意的文本作为数组索引。而在普通数组中,只能用整数作为数组索引。
首先,需要使用单独的声明语句将一个变量名声明为关联数组:
$ declare -A ass_array

2)可以利用如下两种方式将元素添加到关联数组中:
$ ass_array=([index1]=val1 [index2]=val2)
或者
$ ass_array[index1]=val1
$ ass_array[index2]=val2

3)列出数组索引
$ echo ${ !array_var[*] }
$ echo ${ !array_var[@] }

1.7 使用别名

(1)可以按照如下方式创建别名:
$ alias new_command=’command sequence’
删除别名使用unalias命令。

(2)alias命令的作用只是暂时的。为了使别名设置一直保持作用,可以将它放入~/.bashrc文件中。因为每一个新的shell进程生成时,都会执行~/.bashrc中的命令。

1.8 获取终端信息

tput和stty是两个终端处理工具。

(1)获取终端的行数和列数
tput cols
tput lines

(2)打印当前终端名:
tput longname

(3)设置终端背景色:
tput setb no
其中,no可以在0到7之间取值

(4)在输入密码的时候,不能让输入的内容显示出来,stty来实现如下:

运行:

其中,选项-echo禁止将输出发送到终端,而选项echo则允许发送输出。倒数第二行的echo的目的是换行。

1.9 调试脚本

调试脚本不需要是特殊工具,Bash本身就包含了一些选项,能够打印出脚本接受的参数和输入。

(1)选项-x,启动跟踪调试shell脚本:
$ bash -x script.sh
或者
$ sh -x script.sh

-x标识将脚本中执行过的每一行都输出到stdout。不过,我们也可以要求只关注脚本某些部分的命令及参数的打印输出。因此,可以在脚本中使用set built-in来启动或禁止调试打印:

  • set -x:在执行时显示参数和命令
  • set +x:禁止调试
  • set -v:当命令进行读取时显示输入
  • set +v:禁止打印输入

例如:

该脚本中,仅在-x和+x所限制的区域内,echo $i的调试信息才会被打印出来。

(2)上述方法是由Bash内建功能提供的。很多情况下我们需要以自定义格式显示调试信息,可以通过传递_DEBUG环境变量来建立这类调试风格。

如果没有把_DEBUG=on传递给脚本,那么调试信息就不会打印出来。在Bash中,命令’:’告诉shell不要进行任何操作。

1.10 函数和参数

和其他脚本语言一样,Bash同样支持函数。

(1)定义函数

function fname()
{
statements;
}

或者

fname()
{
statement;
}

(2)只需要使用函数名就可以调用某个函数:
$ fname;    #执行函数

参数可以传递给函数,并由脚本进行访问:
fname arg1 arg2;    #传递参数

(3)以下是函数fname的定义。在函数fname中,包含了各种访问函数参数的方法:

(4)递归函数

在Bash中,函数同样支持递归,例如:
F()
{
echo $1;
F hello;
sleep 1;
}

(5)导出函数

函数也能像环境变量一样用export导出,如此,函数的作用域就可以扩展到子进程中:
export -f name

(6)读取命令返回值(状态)
cmd;
echo $?;

1.11 读取命令序列输出

shell脚本最棒的特性之一就是可以将多个命令或工具组合起来生成输出。一个命令的输出可以作为另一个命令的输入,而这个命令的输出又会传递至另一个命令…

(1)使用管道(pipe)来连接每一个过滤器:
$ cmd1 | cmd2 | cmd3

例如,
$ ls | cat -n > out.txt

(2)可以用下面的方法读取命令序列的输出:
cmd_output=$(COMMANDS)
这种方法也被称为子shell(subshell)

例如,
cmd_output=$(ls | cat -n)
echo $cmd_output

(3)另一种被称为反引用(back-quote)的方法也可以用于存储命令输出:

1.12 read相关

read是一个重要的Bash命令,用于从键盘或标准输入中读取文本。

(1)从输入中读取n个字符并存入变量variable_name:
read -n number_of_chars variable_name

例如,
$ read -n 2 var
$ echo $var

(2)用不回显的方式读取密码等:
read -s var

(3)显示提示信息:
read -p “Enter input: ” var

(4)在特定时间内读取输入:
read -t timeout var

例如,
$ read -t 2 var    #在2秒钟内将键入的字符串读入变量var

(5)用定界符结束输入行:
read -d delim_charvar

例如,
$ read -d “:” var
hello:    #var被设置为hello

1.13 字段分隔符和迭代器

(1)内部字段分隔符(Internal Field Separator,IFS)是shell脚本中的一个重要概念。考虑一种情形:我们需要迭代一个字符串,使用IFS“,”

运行:

IFS的默认值为空白符(换行符、制表符或者空格)。当IFS被设置为逗号时,shell将逗号解释成一个定界符。

(2)Bash提供了多种类型的循环:

1)for循环

for var in list    #list can be a string or sequence
do
commands;    #使用变量$var
done

我们可以生成不同的序列:
echo {1..50}生成一个从1到50的数字列表
echo {a..z}或{A..Z}等等

for循环也可以采用C语言中for循环格式,例如:
for ((i=0;i<10;i++))
{
commands;
}

2)while循环

while condition
do
commands;
done

3)until循环

在Bash中还可以使用一个特殊的循环until,它会一直执行循环直到给定的条件为真,例如:
x=0;
until [ $x -eq 9 ]
do
let x++;
echo $x;
done

1.14 比较与测试

可以使用if、if else以及逻辑运算符来执行测试,而用一些比较运算符来比较数据项。另外,test命令也可以用来进行测试。

(1)if

if condition
then
commands
fi

(2)else if 和else

if condition
then
commands
elif condition
then
commands
else
commands
fi

也可以使用逻辑运算符使得更简洁:
[ condition ] && action;    #如果condition为真,则执行action
[ condition ] || action;       #如果condition为假,则执行action

(2)算术比较

条件通常被放置在封闭的括号内。但要注意在[ ] 内的不要忘记两边的空格!否则脚本会报错。

对变量或值进行算术条件判断:
[ $var -eq 0 ]    #当#var等于0时,返回真
[ $var -ne 0 ]    #当$var为非0时,返回真

除此还有如下操作符:

  • -gt:大于
  • -lt:小于
  • -ge:大于或等于
  • -le:小于或等于

(3)字符串比较

使用字符串比较时,最好用双中括号,因为有时采用单个中括号会产生错误

(4)示例

使用逻辑运算符&&和||能够很容易地将多个条件组合起来:

输出:

test命令可以用来执行条件检测。用test有助于避免使用过多的括号。之前交过的[ ]中的测试条件同样可以用于test命令:
if [ $var -eq 0 ]
then
echo “True”
fi

等同于

if test $var -eq 0
then
echo “True”
fi

发表评论

电子邮件地址不会被公开。 必填项已用*标注