Linux之19——Shell编程基础详解
第一部分: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 函数返回值
函数执行完毕或者基于某个测试语句返回时,可作两种处理:
- 让函数正常执行到末尾,然后返回脚本中调用函数的控制部分
 - 使用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编程基础详解的更多相关文章
- Linux Shell 编程基础详解——吐血整理,墙裂推荐!
		
第一部分:Linux Shell 简介 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言. Shell 是指一种应用程序, ...
 - Linux学习之Shell编程基础
		
转自:http://my.oschina.net/itblog/blog/204410 1 语法基本介绍1.1 开头 程序必须以下面的行开始(必须方在文件的第一行): #!/bin/sh 符号#!用来 ...
 - 基于Linux系统的Shell编程-基础篇
		
1. Shell基础介绍 1.1 Shell编程的意义 为什么使用shell编程 节约时间 1.2 显示脚本执行过程 前面有+表示执行过的命令的 前面没有东西,表示输出到屏幕上的内容. [root@C ...
 - linux学习19 shell脚本基础-bash脚本编程基础及配置文件
		
一.shell脚本编程 1.编程语言的分类,根据运行方式 a.编译运行:源代码 --> 编译器(编译) --> 程序文件 C语言: b.解释运行:源代码 --> 运行时启动解释器,由 ...
 - linux中的shell编程----基础
		
1,运行shell脚本有两种办法一般有两种: 先给可执行权限,再进入文件所在的目录,输入:./name.sh: 运行解释器再执行脚本:/bin/sh name.sh,这种情况下,脚本中可以没有#!/b ...
 - Linux学习之二十一-shell编程基础
		
Shell编程基础 Shell 是一个用 C 语言编写的程序,它是用户使用 Linux 的桥梁.Shell 既是一种命令语言,又是一种程序设计语言.Shell 是指一种应用程序,这个应用程序提供了一个 ...
 - Linux Shell系列教程之(八)Shell printf命令详解
		
本文是Linux Shell系列教程的第(八)篇,更多shell教程请看:Linux Shell系列教程 在上一篇:Linux Shell系列教程之(七)Shell输出这篇文章中,已经对Shell p ...
 - Linux shell tr 命令详解
		
该随笔摘自 https://www.jb51.net/article/103892.htm Linux shell tr 命令详解 1. 用途 tr,translate的简写,主要用于压缩重复字符,删 ...
 - (转)Linux 开机引导和启动过程详解
		
Linux 开机引导和启动过程详解 编译自:https://opensource.com/article/17/2/linux-boot-and-startup作者: David Both 原创:LC ...
 
随机推荐
- 上传靶机实战之upload-labs解题
			
前言 我们知道对靶机的渗透可以提高自己对知识的掌握能力,这篇文章就对上传靶机upload-labs做一个全面的思路分析,一共21个关卡.让我们开始吧,之前也写过关于上传的专题,分别为浅谈文件上传漏洞( ...
 - (重磅)Internal: Failed to call ThenRnnForward with model config问题的解决(Keras 2.4.3和Tensorflow2.0系列)
			
与此问题斗争了整整十天.win10,keras2.4.3,CUDA 10.1,CUDNN 7.6, tensorflow 2.3.0,驱动程序nvida 452 该问题出现在BiLSTM(GPU加速) ...
 - Seata分布式事务框架Sample
			
前言 阿里官方给出了seata-sample地址,官方自己也对Sample提供了很多类型,可以查看学习. 我这里选择演示SpringBoot+MyBatis. 该聚合工程共包括5个module: sb ...
 - 深入理解java虚拟机笔记Chapter7
			
虚拟机类的加载机制 概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类的加载机制. 类加载的时机 J ...
 - 菜鸟刷题路(随缘刷题):leetcode88
			
lc88 class Solution { public void merge(int[] nums1, int m, int[] nums2, int n) { int i = m - 1, j = ...
 - 【题解】【洛谷 P1967】 货车运输
			
目录 洛谷 P1967 货车运输 原题 题解 思路 代码 洛谷 P1967 货车运输 原题 题面请查看洛谷 P1967 货车运输. 题解 思路 根据题面,假设我们有一个普通的图: 作图工具:Graph ...
 - 提高GUI自动化测试稳定性解决方案
			
针对"GUI自动化测试稳定性问题"这个问题,最典型的情景就是:同样的测试用例,在同样的测试执行环境下,测试的结果有时是Success,有时是Fail,这严重降低了GUI测试的可信度 ...
 - UF_CSYS 坐标系操作
			
Open C UF_CSYS_ask_csys_info 获取WCS坐标系的原点坐标和矩阵标识UF_CSYS_ask_matrix_of_object 获得对象 ...
 - 终于放弃了单调的swagger-ui了,选择了这款神器—knife4j
			
knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案(在非Java项目中也提供了前端UI的增强解决方案),前身是swagger-bootstrap-ui,取名knife ...
 - 『言善信』Fiddler工具 — 17、Fiddler常用插件(Willow)
			
目录 1.Traific Difer插件 2.PDF View插件 3.JavaScript Formatter插件 4.CertMaker for iOS and Android插件 5.Synta ...