第一部分:Linux Shell 简介

Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁。Shell 既是一种命令语言,又是一种程序设计语言。

Shell 是指一种应用程序,这个应用程序提供了一个界面,用户通过这个界面访问操作系统内核的服务。

Ken Thompson 的 sh 是第一种 Unix Shell,Windows Explorer 是一个典型的图形界面 Shell。

当一个用户登陆linux 系统后,系统就会为该用户创建一个shell程序。

Shell的版本:

  • Bourne Shell:是贝尔实验室开发的,unix普遍使用的shell,在编程方面比较优秀,但在用户交互方面没有其他shell优秀。
  • BASH:是GNU的Bourne Again Shell,是GNU操作系统上默认的shell,在bourne shell基础上增强了很多特性,如命令补全,命令历史表等等
  • Korn Shell:是对Bourne Shell 的发展,在大部分内容上与Bourne Shell兼容,集成了C Shell和Bourne shell优点。
  • C Shell:是SUN公司Shell的BSD版本,语法与c语言相似,比bourne shell 更适合编程

第二部分 shell 程序设计基础

2.1 shell输入输出

2.1.1 echo

echo命令可以显示文本行或变量取值,或者把字符串输入到文件中

格式: echo string

echo的常用功能:\c 不换行 \f 不进纸 \t 跳格 \n 换行

note:

对于linux系统,必须使用-e选项来使以上转义符生效

例:

$ echo  -e  "hello\tboy"
hello boy

echo命令对特殊字符敏感,如果要输出特殊字符,需要用\屏蔽其特殊含义。

常用的特殊字符:双引号"" 反引号`` 反斜线\

例:

$ echo "\"\""      //想输出""
""

2.1.2 read

read命令从键盘或者文件的某一行文本中读入信息,并将其赋给一个变量。

如果只指定了一个变量,read会把所有的输入赋给该变量,直至遇到第一个文件结束符或回车

格式: read var1 var2 …

例1:

chenshifengdeMacBook-Pro:~ chenshifeng$ read name
Hello I am superman
chenshifengdeMacBook-Pro:~ chenshifeng$ echo $name
Hello I am superman

如果输入的值个数多于变量个数,多余的值会赋给最后一个变量:

例2:

chenshifengdeMacBook-Pro:~ chenshifeng$ read name surname
John Mike Kate
chenshifengdeMacBook-Pro:~ chenshifeng$ echo $surname
Mike Kate
chenshifengdeMacBook-Pro:~ chenshifeng$

2.1.3 cat

cat可以用来显示文件,并且支持将多个文件串连接后输出

note:该命令一次显示完整个文件,若想分页查看,需使用more

格式: cat [ options ] filename1 … filename2 …

常用options:

  • -v 显示控制字符
  • -n 对所有输出行进行编号
  • -b 与-n相似,但空白行不编号

例:

$ cat  file1 file2 file3       // 同时显示三个文件
$ cat –b file1 file2 file3

2.1.4 管道 |

可以通过管道把一个命令的输出传递给另外一个命令做为输入

格式: 命令1 | 命令2

例:

$ cat test.txt | grep 'hello'

2.1.5 tee

把输出的一个副本输送到标准输出,另一个副本拷贝到相应的文件中

如果想看到输出的同时,把输出也同时拷入一个文件,这个命令很合适

格式: tee -a file

  • -a 表示文件追加到末尾
  • file 表示保存输出信息的文件

tee命令一般和管道符|结合起来使用

例:

$ who | tee who.info      // 该命令的信息返回在屏幕上,同时保存在文件who.info中
$ who | tee who.info
chenshifeng console Jan 9 12:56
chenshifeng ttys000 Jan 9 13:27
chenshifeng ttys004 Jan 9 19:11
chenshifeng ttys005 Jan 10 00:12
$ cat who.info
chenshifeng console Jan 9 12:56
chenshifeng ttys000 Jan 9 13:27
chenshifeng ttys004 Jan 9 19:11
chenshifeng ttys005 Jan 10 00:12

2.1.6 标准输入,输出和错误

当我们在shell中执行命令的时候,每个进程都和三个打开的文件相联系,并使用文件描述符来引用这些文件,见下表

文件 文件描述符
输入文件-标准输入 0
输出文件-标准输出 1
错误输出文件-标准错误 2

系统中实际上有12个描述符,可以任意使用文件描述符3-9

标准输入 对应文件描述符0,是命令的输入,缺省键盘

标准输出 对应文件描述符1,是命令的输出,缺省屏幕或文件

标准错误 对应文件描述符2,是命令错误的输出,缺省屏幕或文件

利用文件重定向功能对命令的标准输入,输出和错误进行修改。

常用文件重定向命令:

command > file:                   标准输出重定向到一个文件,错误仍然输出屏幕
command >> file: 标准输出重定向到一个文件(追加)
command 1> file: 标准输出重定向到一个文件
command 2>> file: 标准错误重定向到一个文件(追加)
command >file 2>&1: 标准输出和标准错误一起重定向到一个文件
command >>file 2>&1: 标准输出和标准错误一起重定向到一个文件(追加)
command < file1 >file2: 以file1做为标准输入,file2做为标准输出
command <file: 以file做为文件标准输入  

结合使用标准输出和标准错误

$ cat hello 1>myfile.out  2>myerror.out

合并标准输出和标准错误

$ cat >>mylog.out  2>&1  <hello

2.2 shell后台执行命令

2.21 cron

cron是系统的调度进程,可在无人干预的情况下运行作业,通过crontab的命令允许用户提交,编辑或者删除相应的作业。

每个用户都可以有一个crontab文件来保存调度信息,通过该命令运行任意一个shell脚本或者命令

在大的系统中,系统管理员可以通过/etc/cron.allow和/etc/cron.deny这两个文件来禁止或允许用户拥有自己的crontab文件

crontab的域

第1列 分钟0~59

第2列 小时0~23(0表示子夜)

第3列 日1~31

第4列 月1~12

第5列 星期0~6(0表示星期天)

第6列 要运行的命令

crontab格式: 分<>时<>日<>月<>星期<>要运行的命令

<>表示空格

note:如果要表示范围的话,如周一到周五,可以用1-5表示

如果要列举某些值,如周一、周五,可以用1,5表示

例1:

30  21  *  *  *  /apps/bin/cleanup.sh

例2:

0,30  18-23  *  *  *  /apps/bin/dbcheck.sh

crontab的命令选项

格式:crontab [ -u user ] -e -l -r

其中

  • -u 用户名,如果使用自己的名字登陆,就不用使用-u选项
  • -e 编辑crontab文件
  • -l 列出crontab文件中的内容
  • -r 删除crontab文件

创建一个新的crontab文件

1 创建一个文件,建议名为cron,例shifengcron,在文件中假如如下内容:

0,10,20,30,40,50  *  *  *  *  /bin/echo "hello boy"

保存退出

2 提交刚刚创建的cron文件shifengcron

$ crontab shifengcron
$ ls /var/spool/cron/ 是否生成文件shifengcron

列出crontab文件

$ crontab –l
$ crontab –l > $HOME/mycron 可以通过这种方法对crontab进行备份

编辑crontab文件

$ crontab  -e

修改后保存退出,cron会对其进行必要的完整性检查

删除crontab文件

$ crontab –r

crontab文件的恢复

如果误删了crontab文件,假设在$HOME目录下还有备份,可以将这个备份文件拷贝到/var/spool/cron/ username是用户名,如果由于权限问题无法拷贝,可以使用

$ crontab <filename>

note:filename是备份的crontab文件的名字

2.22 at

at命令允许用户向cron守护进程提交作业,使其在稍后的时间运行,这个时间可以是10min以后,也可能是几天以后,但如果时间比较长,建议还是使用crontab

格式:at [ -f script ] [ -m -l -r ] [ time ] [ date ]

  • -f script 是要提交的脚本或命令
  • -m 作业完成后给用户发邮件
  • -r 清除某个作业,需要提供作业标识id
  • time 作业执行的时间格式可以为:HH. MM ,HH:MM
  • H代表小时,M代表分钟
  • date 日期格式可以是月份数或日期数,而且at命令可以识别诸如today,tomorrow这样的词

可以通过命令行方式或者at命令提示符方式来提交作业,一般来讲,如果提交多个命令,可以使用at命令提示符;如果提交的是shell脚本,可以使用命令行方式

例:提示符方式:

$ at 01:15
at > echo “hello”
at > echo “boy” >/home/wuxh/at.log
at > <EOT>

note:EOT是Ctrl+D,任务执行后,会给当前用户发送邮件,通过mail命令可以查看相关信息,也可以将信息重定向到文件

例:提交shell脚本方式

$ at  3:00pm  tomorrow –f   /home/wuxh/hello.sh

note:该脚本将在明天下午3点运行,使用脚本方式,要加参数-f

列出at任务,格式:at -l

例:

$ at  -l
5 2021-01-17 11:20 a root

note: 第一个是作业标识id;第二个是日期;第三个是时间;a代表at;第四个代表创建任务的用户

清除at任务

格式:at -r

$ at  –r  [ job no]

例:$ at -r 5

note:不接job no将清除所有未执行的任务,接具体job id将清楚对应的任务

2.23 &

当在前台运行某个作业时,终端被该作业占据;而当它在后台运行时,它不会占据终端

可以借助&命令把作业放到后台执行

格式: 命令 &

注:

1 .需要用户交互的命令不要放在后台执行,否则机器一直等待

2 .后台程序在执行时,执行结果仍然会输出到屏幕,干扰我们的工作,建议将这样的信息重定向到某个文件

即:command > out.file 2>&1 &

将标准输入错误输出都定向到一个out.file的文件中

例:$ find /etc/ -name "hello" -print >find.dt 2>&1 &

2.3 引号

"" 双引号
` 反引号
'' 单引号
\ 反斜线

2.31 双引号

可引用除字符$,`,\外的任意字符或者字符串,对$,`,\敏感

例1:

$ echo "hello"
hello

例2:

$ echo "$$"
8311 ///想输出字符$$ 结果看到的是数值8311
$ echo "\$$" //对特殊字符需要反斜线屏蔽其特殊含义
$$ //得到想要的结果

例3:

$ echo "`V_V`"  //想输出`V_V`字样    结果得到错误信息
$ echo "\`V_V\`" //得到`V_V`输出

2.32 单引号

单引号和双引号的用法基本类似,不同的是单引号对特殊字符不敏感,可以将其做为普通字符输出出来

例:

$ echo '$$'               //结果 $$  不用借助\进行屏蔽
$ echo '`V_V`' //结果`V_V`,和前面双引号比较

2.33 反引号

该命令用于设置系统命令的输出到变量,shell将反引号中的内容做为命令执行。

例1:

$ echo `hello`
-bash: hello: command not found

例2:

$ echo `date`
2021年 1月17日 星期日 23时40分18秒 CST

反引号可以和双引号结合起来使用:

例3:

$ echo "The date today is `date`"
The date today is 2021年 1月17日 星期日 23时41分15秒 CST

2.34 反斜线

如果一个字符有特殊含义,为防止shell误解其含义,可用\屏蔽该字符

具有特殊含义的字符

------------------------------------------------------------------------------------

& * ^ $ ` “ |

------------------------------------------------------------------------------------

例1 :

$ echo "$$"           //在屏幕上输出$$字符,结果显示3853
$ echo "\$$" //用反斜线屏蔽,防止shell误解,结果显示$$

例2:

$ echo  *         //在屏幕上输出*字符,结果输出当前目录下内容
$ echo \* //用反斜线屏蔽,防止shell误解,输出*字符

2.4 shell变量,参数

2.4.1 系统变量

系统变量适用于所有用户进程,可以在命令行中设置,但用户注销时这些值将丢失,最好在.bash_profile中进行定义,或者/etc/profile

传统上,所有环境变量都大写,且必须用export命令导出

设置环境变量:

var_name=value; export var_name

或者:

var_name=value
export var_name

又或者

export var_name=value

查看环境变量:

echo $var_name
  • env 该命令可查看所有系统环境变量
  • unset var_name 清除系统环境变量

嵌入shell变量

一般来讲,bourne shell有一些预留的环境变量名,这些变量名不能做其他用途,通常在/etc/profile中建立这些嵌入的环境变量,但这不绝对,取决于用户

shell的变量列表:

CDPATH; EXINIT; HOME; IFS; LOGNAME; MAIL;MAILCHECK; PATH; PS1; PS2; SHELL; TERMINFO;TERM; TZ

2.4.2 用户变量

在用户shell生命周期的脚本中使用,不同的用户可以定义各自的用户变量 ~/.bashrc

用法:

var_name=value

显示变量:

echo $var_name
or echo ${var_name} //建议使用

清除变量:

unset var_name

显示用户所有变量:set

测试变量是否设置:echo ${var:=value} 若未设置或未初始化,可用新值

使用变量保存系统命令参数

例:

$ SOURCE="/etc/passwd"
$ DEST="/home/chenshifeng/
$ cp $SOURCE $DEST

设置只读变量

可设置某个变量为只读方式,只读变量不可更改,否则系统返回错误

用法:

var_name=value
readonly var_name

例:

$ myvar="100"
$ readonly myvar
$ myvar="200"
$ -bash: myvar: readonly variable

2.4.3 位置变量

位置变量属于只读变量

作用:向shell脚本传递参数,参数个数可以任意多,但只有前9个被访问到,shift命令可以更改这个限制。

每个访问参数前加$,

第一个参数为0,表示预留保存实际脚本名字,无论脚本是否有参数,此值均可用,如:给脚本test传递信息:

Would you like to do it

$0 $1 $2 $3 $4 $5 $6 $7 $8 $9
脚本名字 would you like to do it

例:$ vi test

#!/bin/sh
echo "The full name is : $0 "
echo "The script name is : `basename $0`"
echo "The first parameter is :$1"
echo "The second parameter is :$2"
echo "The third parameter is :$3"
echo "The fourth parameter is :$4"
echo "The fifth parameter is :$5"
echo "The sixth parameter is :$6"
echo "The seventh parameter is :$7"
echo "The eighth parameter is :$8"
echo "The ninth parameter is :$9"

保存文件,执行 $ ./test would you like to do it

The full name is : ./test
The script name is : test
The first parameter is :would
The second parameter is :you
The third parameter is :like
The fourth parameter is :to
The fifth parameter is :do
The sixth parameter is :it
The seventh parameter is :
The eighth parameter is :
The ninth parameter is :

note:上例中$0返回的信息中包含路径名,如果只想得到脚本名称,可以借助basename,将脚本中第一句修改为:

echo "The script name is : \`basename \$0\` "

保存文件,执行 ./test would you like to do it

note:basename 用``向系统命令传递参数

可以在脚本中向系统命令传递参数

$ vi findfile
#!/bin/sh
find / -name $1

保存,执行

$ ./findfile  passwd

2.4.4 特定变量

特定变量属于只读变量,反映脚本运行过程中的控制信息

特定的shell变量列表:

变量 说明
$# 传递到脚本的参数个数(常用)
$* 以一个单字符串的形式显示所有向脚本传递的参数,与位置变量 不同,此项参数可超过9个
$$ 脚本运行的当前进程id号(常用)
$! 后台运行的最后一个进程的进程id号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数
$- 显示shell使用的当前变量,与set命令功能相同
$? 显示最后命令的退出状态,0表示正确,其他任何值表示错误(常用)

例:修改test脚本,追加特定变量信息:

#!/bin/sh
echo "The full name is : $0 "
echo "The script name is : `basename $0`"
echo "The first parameter is :$1"
echo "The second parameter is :$2"
echo "The third parameter is :$3"
echo "The fourth parameter is :$4"
echo "The fifth parameter is :$5"
echo "The sixth parameter is :$6"
echo "The seventh parameter is :$7"
echo "The eighth parameter is :$8"
echo "The ninth parameter is :$9"
echo "The number of arguments passed #"

echo "Show all arguments *"

echo "Show my process id :$$"

echo "Show me the arguments in quotes @"

echo "Did my script go with any errors ?"

最后的退出状态 $?

可以在任何脚本或者命令中返回此变量以获得返回信息,基于此信息,可以在脚本中做更进一步的研究,返回0为成功,1为失败

例1:

$ cp /etc/passwd /home/chenshifeng/myfile
$ echo $?
0

例2:

$ cp /etc/passwd /home/wuxh/mydir   //<mydir不存在>
$ echo $?
1

建议将返回值设置为一个有意义的名字,增加脚本的可读性

修改例2

$ cp_status=$?
$ echo $cp_status

第三部分 shell程序设计流程控制

3.1 test 测试命令

3.1.1 文件测试

测试文件状态:

用法:test condition 或者 [ condition ]

文件状态列表

  • -d 目录
  • -s 文件长度大于0,非空
  • -f 正规文件
  • -w 文件可写
  • -L 符号文件
  • -u 文件有uid设置
  • -r 文件可读
  • -x 文件可执行

例:

$ ls  -l  hello
$ [ -w hello ]
$ echo $?

使用逻辑操作符:

测试文件状态是否ok,可以借助逻辑操作符对多个文件状态进行比较

  • -a 逻辑与,操作符两边均为真,结果为真,否则为假
  • -o 逻辑或,操作符两边一边为真,结果为真,否则为假
  • ! 逻辑否,条件为假,结果为真

例1:

$ [  -r myfile1    -a   -w  myfile2   ]
$ echo $?

例2:

$ [  -w myfile1    -o   -x  myfile2   ]
$ echo $?

3.1.2 字符串测试

字符串测试是错误捕获很重要的一部分,特别是用户输入或比较变量时尤为重要

格式:

  • test "string"
  • test string_operator "string"
  • test "string" string_operator "string"
  • [ string_operator string ]
  • [ string string_operator string ]

注:string_operator 的取值:

= 等于 、 != 不等于 、 -z 空串 、 -n 非空串

例:测试变量string1是否等于string2

$ string1="hello"
$ string2="Hello"
$ [ "$string1" = "$string2" ]
$ echo $?

note:在进行字符串比较时,一定要加引号;等号前后要加空格。

3.1.3 数值测试

格式:"number" number_operator "number"

或者:[ "number" number_operator "number" ]

number_operator 的取值范围:

比较符 说明
-eq 数值相等
-gt 第一个数大于第二个数
-ne 数值不相等
-lt 第一个数小于第二个数
-le 第一个数小于等于第二个数
-ge 第一个数大于等于第二个数

例1:

[root@chenshifengdeLinuxServer ~]# NUM1=130
[root@chenshifengdeLinuxServer ~]# [ "$NUM1" -eq "130" ]
[root@chenshifengdeLinuxServer ~]# echo $?
0

例2:

[root@chenshifengdeLinuxServer ~]# [ "990" -le "996" -a "123" -gt "33" ]
[root@chenshifengdeLinuxServer ~]# echo $?
0

3.2 expr 语句-字符串测试和数值测试

一般用于整数值,也可以用于字符串;

格式:expr argument operator argument

expr 也是个手工命令行的计数器

$ expr  10 + 10                    #注意空格
$ expr 300 / 6 / 5
$ expr 30 \* 3 #注意:乘号必须用反斜线屏蔽其特定含义

expr在循环中用于增量计算,循环初始化为0,然后循环加1,常用的做法:从expr接受输出赋给循环变量

例:

$ LOOP=0
$ LOOP=`expr $LOOP + 1`

3.2.1 数值测试

可以用expr测试一个数,如果对非整数进行计算,则返回错误

例:

$ expr  1.1 + 1          #返回错误
$ expr 1 + 1 #返回2

3.2.2 字符串测试

注 expr 也有返回的状态,但与系统最后返回的值刚好相反,expr返回成功为1,其他值为失败。

例:

$ value=hello
$ expr $value = "hello"
1 # 这是expr执行成功的值
$ echo $?
0 # 这是系统返回的成功的值

3.3 if 条件判断

格式:

if 条件1
then 命令1
elif 条件2
then 命令2
else 命令3
fi

注意:使用if语句时,必须将then部分放在新行,否则会产生错误,如果要不分行,必须使用命令分割符,即:

if  条件1; then
命令1
fi

例:$ vi myfile

#!/bin/sh
DIRECTORY=$1
if [ "`ls -A $DIRECTORY`" = "" ] ; then
echo "$DIRECTORY is indeed empty"
else
echo "$DIRECTORY is not empty"
fi

3.4 for 循环

格式:

for 变量名 in 列表
do
命令1
命令2
done

说明:命令 可为任何有效的shell命令和语句

变量名可以为任何单词

in列表是可选的,如果没有列表,for循环会使用命令行的位置参数

in列表可以包含替换,字符串,文件名

例:

#!/bin/sh
for loop1 in 1 2 4 5 6 #数字列表
do
echo $loop1
done
for loop2 in he is a tall man #字符串列表
do
echo $loop2
done
for loop3 in `ls` #替换列表
do
echo $loop3
done

对for 循环使用参数,当循环中省去in列表选项时,它将接受命令行特定变量做为参数,即

for params in "$@"   或者 for params in "$*"

例1:

#!/bin/sh
for params in "$@"
do
echo "You supplied $params as a command line option"
done
echo $params

例2

#!/bin/sh
counter=0
for files in `ls`
do
counter=`expr $counter + 1`
done
echo "There are $counter files in `pwd`"

3.5 while和until循环

while循环

格式:

while  命令
do
命令1
命令2
……
done

note:do和done之间命令,只有前一个返回状态为0,后面命令才会被执行;否则则循环中止

until循环

格式:

until  条件
do
命令1
命令2
……
done

note:until执行一系列命令,只至条件为真时停止,循环至少执行一次。

例1:

#!/bin/sh
echo "Type <Ctrl-D> to terminate"
echo -n "enter your favorate film :"
while read FILM
do
echo "Yeah,great film the $FILM"
done

使用ctrl-D中断脚本的执行,整个循环中止

例2:

#!/bin/sh
IS_ROOT=`who | grep root`
until [ "$IS_ROOT" ]
do
sleep 5
IS_ROOT=`who | grep root`
done
echo "Watch it. Roots in " | mail chenshifeng

思考:为什么用sleep 5?

3.6 case 条件选择

格式:

case  值  in
模式1)
命令1
……
;;
模式2)
命令2
……
;;
esac

case 取值后面必须为in,每个模式必须以右括号结束,取值可以为变量或者常数,找到匹配模式后,执行相关命令直到;;

模式部分可以包含元字符,与命令行中文件扩展名中使用的匹配类型相符,如 * 、? 、 [..]

例:

#!/bin/sh
if [ $# != 1 ]; then
echo "Usage:`basename $0` [start|stop|help]"
exit 1
fi
OPT=$1
case $OPT in
start)
echo "starting..`basename $0`"
# code here to start a process
;;
stop)
echo "stopping..`basename $0`"
# code here to stop a process
;;
help)
# code here to display a help page
;;
*)
echo "Usage:`basename $0` [start|stop|help]"
;;
esac

3.7 break 和continue

有时需要某些准则退出循环或者跳过循环步,就需要break和continue来实现

break 允许跳出循环或者case语句,在嵌套循环里,可以制定跳出的循环个数,例在两层的嵌套循环内,break 2可以跳出整个循环

continue 类似于break,区别是continue只会跳过当前的循环步,而不会跳出整个循环

例子1:

#!/bin/sh
while :
do
echo -n "Enter any number [1..5] :"
read ANS
case $ANS in
1|2|3|4|5)
echo "great you entered a number between 1 and 5"
;;
*)
echo "wrong number..bye"
break
;;
esac
done

例子2 :

names2.txt 内容包含雇员名字,部门,及其id,如下所示:

------------------------------内容如下--------------------------------

---LISTING OF PERSONNEL FILE----

--- TAKEN AS AT 06/1999----------------

Louise Conrad:Accounts:ACC8987

Peter James:Payroll:PR489

Fred Terms:Customer:CUS012

James Lenod:Accounts:ACC887

Frank Pavely:Payroll:PR489

-------------------------------------------------------------------------------

要求:读取names2.txt文件,将在职员工的名字,部门,部门id读取打印出来

说明:Peter James已经离职

使用IFS读文件

输出时要去除冒号域分隔符,可使用变量IFS。在改变它之前保存IFS的当前设置。然后在脚本执行完后恢复此设置。

使用IFS可以将域分隔符改为冒号而不是空格或tab键,这里有3个域需要加域分隔,即NAME、DEPT和ID。脚本如下:

#!/bin/sh
# save the setting of IFS
SAVEDIFS=$IFS
# assign new separator to IFS
IFS=:
INPUT_FILE=names2.txt
NAME_HOLD="Peter James"
LINE_NO=0
if [ -s $INPUT_FILE ]; then
while read NAME DEPT ID
do
LINE_NO=`expr $LINE_NO + 1`
if [ "$LINE_NO" -le "2" ]; then
continue
fi
if [ "$NAME" = "$NAME_HOLD" ]; then
continue
else
echo "Now processing …$NAME $DEPT $ID"
fi
done < $INPUT_FILE
# restore the settings of IFS
IFS=$SAVEDIFS
else
echo "`basename $0 ` : Sorry file not found or there is no data in the file >&2"
exit 1
fi

第四部分 shell 函数

shell 允许将一组命令集或语句形成一个可用块,这些块称为shell函数,其组成部分:

函数标题,函数体

标题是函数名,应该唯一;函数体是命令集合

函数格式:

函数名()
{
命令1

}
或者
function 函数名()
{ ….
}

函数可以只放在同一个文件中做为一段代码,也可以放在只包含函数的单独文件中

4.1 在脚本中定义并使用函数

注:函数必须在使用前定义,一般放于脚本开始部分,直至shell解释器首次发现它时,才可以使用

例脚本func1:

#!/bin/sh
hello() {
echo "Hello,today’s date is `date`"
}
echo "now, going to the function hello"
hello
echo "back from the function"

4.2 向函数传递参数

向函数传递参数就象在一般脚本中使用特殊变量$1,$2..$9一样,函数取得所传参数后,将原始参数传回shell,可以在函数内定义本地变量保存所传的参数,一般这样的参数名称以_开头

例:脚本对输入的名字进行检查,只能包含字母

$ vi func2

#!/bin/sh
echo -n "what is your first name :"
read F_NAME
char_name()
{
_LETTERS_ONLY=$1
_LETTERS_ONLY=`echo $1|awk '{if($0~/[^a-z A-Z]/) print "1"}'`
if [ "$_LETTERS_ONLY" != "" ]
then
return 1
else
return 0
fi
}
if char_name $F_NAME; then
echo "ok"
else
echo "ERRORS"
fi

4.3 函数返回值

函数执行完毕或者基于某个测试语句返回时,可作两种处理:

  1. 让函数正常执行到末尾,然后返回脚本中调用函数的控制部分
  2. 使用return 返回脚本中函数调用的下一条语句,可以带返回值,
  • 0为无错误
  • 1为有错误

    格式:
return     从函数中返回,用最后状态命令决定返回值
return 0 无错误返回
return 1 有错误返回

4.4 函数返回值测试:

可以直接在脚本调用函数语句的后面使用最后状态命令来测试函数

调用的返回值

例:

hello            #这里是hello函数被调用
if [ $? = 0 ]
then
echo "it is ok"
else
echo "something is wrong with hello function"
fi

更好的办法是使用if语句测试返回0还是返回1,可以在if语句里面将函数调用用括号括起来,增加可读性,如 if hello ; then

如果函数将从测试结果中反馈输出,可以使用替换命令保存结果,函数调用的替换格式为

variable_name=function_name

函数function_name输出被设置到变量variable_name中

4.5 在shell中使用函数

常用的一些函数可以收集起来,放入函数文件,使用时,将文件载入shell中

文件头应该以#!/bin/sh开头,文件名可任意选取,但建议有说明性。

文件一旦载入shell,就可以在命令行或者脚本中调用函数,可以使用set产看所有定义的函数,输出列表包括已经载入shell的所有函数。

要想改动文件,首先用unset命令从shell中删除函数,注,这里不是真正的删除,修改完毕后,再将文件重新载入,有些shell会识别改动,不必使用unset,但建议改动时使用unset命令

4.6 创建函数文件

例:function.main

#!/bin/sh
findit() {
if [ $# -lt 1 ]; then
echo "Usage :findit file" # 思考:为什么用findit,不用$0?
return 1
fi
find / -name $1 -print
}

4.7 定位文件(载入文件)

格式:<点><空格><路径><文件名>

$.   ./function.main

4.8 检查载入的函数

使用set命令查看已经载入shell中的函数

$ set

4.9 执行shell函数

要执行函数,简单的键入函数名,如果需要参数,后跟参数即可

$ findit hello

4.10 修改shell函数

如果需要对函数做改动,需要借助unset命令先从shell中删除函数,修改后,再重新载入

$ unset findit
修改…
$ . ./function.main

第五部分 脚本调试——shell脚本调试

5.1 一般的shell脚本错误

  • a.循环错误

    • 如for,while,until,case等结构中漏写了某个保留字
  • b.漏写引号
  • c.测试错误
    • 如 在-eq 两边应该使用数字取值;[变量] 缺少空格
  • d.字符大小写

    -linux对大小写敏感

5.2 使用set命令进行调试

set  -n  读命令但不执行
set -v 显示读取的所有行
set -x 显示所有命令及其参数
set +x set选项关闭

例:vi error_file

#!/bin/sh
set –x
LIST="Peter Susan John Barry Lucy Norman Bill Leslie"
echo -n "Enter your name :"
read NAME
for LOOP in $LIST
do
if [ "$LOOP" = "$NAME" ];then
echo "you’re on the list, you’re in"
break
fi
done
set +x

运行脚本

$ ./error_file

执行结果:

error
+ error
+ LIST=Peter Susan John Barry Lucy Norman Bill Leslie
+ echo –n Enter your Name:
Harry
+ [ Peter = Harry ]
+ [ Susan = Harry ]
+ [ John = Harry ]
+ [ Barry = Harry ]
+ [ Lucy = Harry ]
+ [ Norman = Harry ]
+ [ Bill = Harry ]
+ [ Leslie = Harry ]

Linux之19——Shell编程基础详解的更多相关文章

  1. Linux Shell 编程基础详解——吐血整理,墙裂推荐!

    第一部分:Linux Shell 简介 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序, ...

  2. Linux学习之Shell编程基础

    转自:http://my.oschina.net/itblog/blog/204410 1 语法基本介绍1.1 开头 程序必须以下面的行开始(必须方在文件的第一行): #!/bin/sh 符号#!用来 ...

  3. 基于Linux系统的Shell编程-基础篇

    1. Shell基础介绍 1.1 Shell编程的意义 为什么使用shell编程 节约时间 1.2 显示脚本执行过程 前面有+表示执行过的命令的 前面没有东西,表示输出到屏幕上的内容. [root@C ...

  4. linux学习19 shell脚本基础-bash脚本编程基础及配置文件

    一.shell脚本编程 1.编程语言的分类,根据运行方式 a.编译运行:源代码 --> 编译器(编译) --> 程序文件 C语言: b.解释运行:源代码 --> 运行时启动解释器,由 ...

  5. linux中的shell编程----基础

    1,运行shell脚本有两种办法一般有两种: 先给可执行权限,再进入文件所在的目录,输入:./name.sh: 运行解释器再执行脚本:/bin/sh name.sh,这种情况下,脚本中可以没有#!/b ...

  6. Linux学习之二十一-shell编程基础

    Shell编程基础 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言.Shell 是指一种应用程序,这个应用程序提供了一个 ...

  7. Linux Shell系列教程之(八)Shell printf命令详解

    本文是Linux Shell系列教程的第(八)篇,更多shell教程请看:Linux Shell系列教程 在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中,已经对Shell p ...

  8. Linux shell tr 命令详解

    该随笔摘自 https://www.jb51.net/article/103892.htm Linux shell tr 命令详解 1. 用途 tr,translate的简写,主要用于压缩重复字符,删 ...

  9. (转)Linux 开机引导和启动过程详解

    Linux 开机引导和启动过程详解 编译自:https://opensource.com/article/17/2/linux-boot-and-startup作者: David Both 原创:LC ...

随机推荐

  1. 将TVM集成到PyTorch

    将TVM集成到PyTorch 随着TVM不断展示出对深度学习执行效率的改进,很明显PyTorch将从直接利用编译器堆栈中受益.PyTorch的主要宗旨是提供无缝且强大的集成,而这不会妨碍用户.PyTo ...

  2. Java swing JFrame用repaint出现闪烁的问题解决

    这几天用swing写登录页面背景动图的时候发现一直会有闪烁(我的类是继承JFrame),就来搜原因后发现好像是因为repaint会调用update()方法中的清屏操作导致闪烁. 我当时看的是这个文章 ...

  3. 【NX二次开发】用户出口函数介绍

    用户出口(User Exit)是NX Open 中的一个重要概念.NX在运行过程中某些特定的位置存在规定的出口,当进程执行到这些出口时,NX会自动检查用户是否在此处已定义了指向内部程序位置的环境变量: ...

  4. PTA4-6题目集总结与归纳

    前言: 继上篇blog所写的几种日期的求法,这次是把那几种聚合起来,即日期类的聚合设计.除下这类,一种是图形继承设计的3种变化,还有一种是3次对正则表达式的应用.当然,作为一个菜鸟,还是无法写成大佬的 ...

  5. MySQL数据的高效检索

    数据库操作中,常常需要完成既定数据的检索.少量数据存放在表中,只需使用基本的SQL语句即可检索得到.但当数据量较大时,受MySQL数据库底层实现原理的限制,缺省的SQL语句,检索效率较低. 例如:当执 ...

  6. bzoj2427 软件安装! 树dp

    软件安装 内存限制:128 MiB 时间限制:1000 ms 标准输入输出     题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一 些软 ...

  7. Java基础-封装和继承

    @ 目录 Java基础知识(封装和继承) 一. 封装 1.1 封装的目的 1.2 封装的好处 1.3 封装的步骤 1.4 封装的例子 1.5 小结 二. 继承 2.1 继承的介绍 2.2 生活中的继承 ...

  8. Linux常用命令详解上

    Linux常用命令详解上 目录 一.shell 二.Linux命令 2.1.内部命令与外部命令的区别 2.2.Linux命令行的格式 2.3.编辑Linux命令行的辅助操作 2.4.获得命令帮助的方法 ...

  9. ES6学习笔记之 this 详解

    1.非箭头函数下的 this var obj = { x: 0, f1: function () { console.log(this.x); } } var f1 = obj.f1; var x = ...

  10. js笔记15

    DOM2动态创建节点 1.生成节点的方法 document.createElement("div") 2.插入节点的方法 父元素.appendChild(新节点) 在父节点的子节点 ...