UNIX再学习 -- shell编程

UNIX环境高级编程看了三章,遇到不少重定向等shell命令。本想到Linux时再讲,看来有必要提前了。之前有看过一本《嵌入式Linux软硬件开发详解》这本书里有简单介绍了一部分shell常用命令,就结合它来简单介绍下shell编程。毕竟没有详细看过shell相关的书籍,等以后看过了再详细讲吧。

首先,推荐一款 shell 在线工具

扩展学习:Shell 教程

相关书籍:Linux命令行与Shell脚本编程大全

常用快捷键:

Ctrl+C强制终止当前命令  
Ctrl+L 清屏,相当于clear  
Ctrl+U 删除或者剪切光标之前的所有命令,比退格方式更快捷  
Ctrl+K 删除或者剪切光标之后的所有命令  
Ctrl+Y 粘贴Ctrl+U或者Ctrl+K剪切的内容  
Ctrl+R 实现搜索历史命令,先输入Ctrl+R,然后回车再输入需要搜索的历史命令  
Ctrl+D 退出当前终端  
Ctrl+Z 暂停命令并且放入后台,不能经常使用  
Ctrl+S 暂停屏幕输出  
Ctrl+Q 恢复屏幕输出 

一、bash shell 简介

1、在UNIX基础知识这章中,有简单介绍shell:

shell 是一个命令行解释器,它读取用户输入,然后执行命令。shell 用户输入通常来自终端(交互式 shell)有时则来自于文件(称为 shell 脚本)

UNIX系统中常见的 shell:
Bourne shell  路径:/bin/sh 
Bourne-again shell  路径:/bin/bash
C shell  路径:/bin/csh 
Korn shell  路径:/bin/ksh 
TENEX C shell  路径:/bin/tcsh

其中的 Bourne-again shell 就是我们要讲的简称 bash shell,它是 GNU shell,所有 Linux 系统都是提供这种 shell 。它的设计遵循 POSIX 标准,同时保留了与 Bourne shell 的兼容性。它支持 C shell 和 Korn shell 两者的特色。
系统从口令文件中相应的用户登录项的最后一个字段中了解到应该为该登录用户执行哪一个 shell。
我用的虚拟机是 Ubuntu 12.04,内核是 Linux 3.2.0,使用 root 超级用户。查看用户登录文件 /etc/passwd 
查看 /etc/passwd :
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
可以看到:root:x:0:0:root:/root:/bin/bash   所以,我所执行的是 bash shell
其他字段含义,可参看:UNIX再学习 -- 再识

2、与其他 shell 比较

bash shell 是 Linux 操作系统中标准的 shell,当前几乎所有 Linux 版本都使用 bash shell 作为系统管理的核心,相比其他 shell ,bash shell 具有更加强大的功能:

(1)命令记忆功能

我们通过按键盘上的 【上下键】 可以查看到之前使用过的指令。每次登陆后执行的指令都被暂存在缓冲区中,成功退出系统后,该指令便会记录到 bash_history 文件当中。通过这一功能,我们可以很方便地修改错误的执行命令。

(2)命令与文件补全功能

使用此功能,我们可以少打很多字并且确保输入的数据是正确的。【Tab】 接在一串命令的第一个字的后面,为“命令补全”;【Tab】接在一串命令的第二个字的后面,则为“文件补全”。通过这一功能,我们可以快速查看或匹配当前目录下相关命令或文件。

(3)命令别名设置功能

Linux 系统中包含有千差万别的命令名及参数,既不方便使用也不方便管理。bash shell 中提供了利用 Alias 自定义命令别名的功能。

(4)编程功能

shell 不仅可以作为命令解释器用来定制工作环境,还可以作为一门高级编程语言编写执行用户指令的脚本,从而更加快速有效地处理复杂的任务。

二、bash shell 常用命令

用户登录到 Linux 系统时,便开始于 bash 进行互动,一直到用户注销为止(以后讲 Linux启动时会详细介绍这部分)。如果是普通用户,则 bash 的默认提示符为“$”(代表普通用户),如果是 root 超级用户,提示符则变为“#”。用户与系统互动的过程便是通过在提示符后面输入操作命令来完成的。
为了加强 shell 的处理能力,bash shell 除本身内置一部分命令,如 cd 等,还增加了对外部应用命令的支持,如 ls、ps等。
在 shell 的命令提示符后面输入的命令,如果是 bash shell 内置的命令,则有它自己负责回应;如果是外部应用命令,则 shell 会找出对应的外部应用程序,然后将控制权交给内核,由内核执行该应用程序之后再讲控制权交回给 shell。
常见命令如下:
这部分更多内容,搜索:Linux命令大全  Shell内建命令

1、type

命令格式:type 参数命令
功能:判断一个命令是内置命令还是外部命令
选项:
-t:输出“file”、“alias”或者“builtin”,分别表示给定的指令为“外部指令”、“命令别名”或者“内部指令”;
-p:如果给出的指令为外部指令,则显示其绝对路径; 
-a:在环境变量“PATH”指定的路径中,显示给定指令的信息,包括命令别名。
实例:
# type ls
ls 是 `ls --color=auto' 的别名

# type -t ls
alias

# type -a ls
ls 是 `ls --color=auto' 的别名
ls 是 /bin/ls

可以看出,ls为命令别名
# type cd
cd 是 shell 内嵌

# type -t cd
builtin

# type -a cd
cd 是 shell 内嵌

可以看出,cd为内部命令
# type which
which 是 /usr/bin/which

# type -t which
file

# type -p which
/usr/bin/which

# type -a which
which 是 /usr/bin/which
which 是 /bin/which

可以看出,which为外部命令
通过,type 命令的用法,我们可以知道每个命令是否为 bash 内置命令。此外,使用 type 搜索后面的名称时,如果后接的名称不能以执行文件的状态找到,那么该名称不会显示。

2、echo

命令格式:echo arg
功能:在屏幕上显示出有 arg 指定的字符串
实例:
简单显示:
# echo hello
hello
创建shell脚本:
gedit hello.sh
#!/bin/bash
echo "hello world!"

执行 ./hello.sh
bash: ./hello.sh: 权限不够
添加权限:chmod +x *.sh  或者  chmod 777 *.sh

# ./hello.sh 
hello world!
扩展部分:echo命令
选项:
   -e:激活转义字符。
使用-e选项时,若字符串中出现以下字符,则特别加以处理,而不会将它当成一般文字输出:
\a 发出警告声; 
\b 删除前一个字符; 
\c 最后不加上换行符号; 
\f 换行但光标仍旧停留在原来的位置; 
\n 换行且光标移至行首; 
\r 光标移至行首,但不换行;
\t 插入tab; \v 与\f相同; 
\\ 插入\字符; 
\nnn 插入nnn(八进制)所代表的ASCII字符;
实例:
# echo -e "\e[1;31mThis is red text\e[0m"
This is red text

# echo -e "\e[1;42mGreed Background\e[0m"
Greed Background

3、export

这部分再讲环境变量时讲过了,参看:UNIX再学习 -- 环境变量
命令格式1:export variable
功能:shell 可以用 export 把它的变量向下带入 子 shell,从而让子进程继承父进程中的环境变量。但子 shell 不能用 export 把它的变量向上带入父进程。
实例:
# export HELLO="hello"  
# echo $HELLO  
hello 
命令格式2:export
功能:显示当前所有环境变量及其内容。
实例:
# export
declare -x COLORTERM="gnome-terminal"
declare -x DBUS_SESSION_BUS_ADDRESS="unix:abstract=/tmp/dbus-Kss6b0aquA,guid=297ad74aed4e17b7f89f981d0000003c"
declare -x DEFAULTS_PATH="/usr/share/gconf/ubuntu-2d.default.path"
declare -x DESKTOP_SESSION="ubuntu-2d"
declare -x DISPLAY=":0"
declare -x GDMSESSION="ubuntu-2d"
declare -x GNOME_DESKTOP_SESSION_ID="this-is-deprecated"
declare -x GNOME_KEYRING_CONTROL="/tmp/keyring-qyAxFa"
declare -x GPG_AGENT_INFO="/tmp/keyring-qyAxFa/gpg:0:1"
declare -x GTK_IM_MODULE="ibus"
declare -x HOME="/root"
.....

4、readonly

命令格式1:readonly variable
功能:将一个用户自定义的 shell 变量标识为不可变
实例:
# export HELLO="hello"  
# readonly HELLO="hello"  
# export HELLO="hello" world  
bash: HELLO: 只读变量  
# unset HELLO  
bash: unset: HELLO: 无法反设定: 只读 variable  
命令格式2:readonly
功能:显示出所有只读的 shell 变量
实例:
# readonly
declare -r BASHOPTS="checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath"
declare -ir BASHPID
declare -ar BASH_VERSINFO='([0]="4" [1]="2" [2]="24" [3]="1" [4]="release" [5]="i686-pc-linux-gnu")'
declare -ir EUID="0"
declare -ir PPID="2590"
declare -r SHELLOPTS="braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor"
declare -ir UID="0"

5、read

命令格式:read variable
功能:从标准输入设备读入一行,分解成若干行,赋值给 shell 程序定义的变量。
实例:
创建shell脚本:
gedit hello.sh
#!/bin/bash
echo -e "Please enter: \c"
read x
echo "you enter: $x"

执行脚本:
# ./hello.sh 
Please enter: hello world!
you enter: hello world!
再例如,终端输入密码时候,不让密码显示出来,可创建如下脚本:
方法一:
创建脚本:
#!/bin/bash 
read -p "输入密码:" -s pwd 
echo echo password read, is "$pwd"

执行脚本:
./hello.sh
输入密码:
password read, is 12345
方法二:
创建脚本:
#!/bin/bash 
stty -echo
read -p "输入密码:" pwd
stty echo
echo
echo 输入完毕

执行脚本:
./hello.sh
输入密码:
输入完毕
注意: stty -echo 
选项 -echo 禁止将输出发送到终端,而选项 echo 则允许发送输出。

6、env

命令格式:env
功能:显示环境变量及其内容。
实例:
# env
LC_PAPER=en_US.UTF-8
LC_ADDRESS=en_US.UTF-8
SSH_AGENT_PID=1749
LC_MONETARY=en_US.UTF-8
GPG_AGENT_INFO=/tmp/keyring-qyAxFa/gpg:0:1
TERM=xterm
SHELL=/bin/bash
.....

7、set

命令格式:set
功能:显示所有变量及其内容
实例:
# set
BASH=/bin/bash
BASHOPTS=checkwinsize:cmdhist:expand_aliases:extquote:force_fignore:histappend:hostcomplete:interactive_comments:progcomp:promptvars:sourcepath
BASH_ALIASES=()
BASH_ARGC=()
BASH_ARGV=()
BASH_CMDS=()
BASH_LINENO=()
BASH_SOURCE=()
.....

8、unset

命令格式:unset
功能:从环境中删除变量或函数。这个命令不能删除 shell 本身定义的只读变量。
实例:
# export HELLO="hello"  
# unset HELLO  
# echo $HELLO  
  
#  

9、grep

命令格式:grep 参数 string 目标文件
功能:在指定文件一堆文件中查找一个特定的字串并将字串所在行输出到终端或平台。
参看:grep 命令
选项:
-a 不要忽略二进制数据。 
-A<显示列数> 除了显示符合范本样式的那一行之外,并显示该行之后的内容。 
-b 在显示符合范本样式的那一行之外,并显示该行之前的内容。 
-c 计算符合范本样式的列数。 
-C<显示列数>或-<显示列数> 除了显示符合范本样式的那一列之外,并显示该列之前后的内容。 
-d<进行动作> 当指定要查找的是目录而非文件时,必须使用这项参数,否则grep命令将回报信息并停止动作。 
-e<范本样式> 指定字符串作为查找文件内容的范本样式。 
-E 将范本样式为延伸的普通表示法来使用,意味着使用能使用扩展正则表达式。 
-f<范本文件> 指定范本文件,其内容有一个或多个范本样式,让grep查找符合范本条件的文件内容,格式为每一列的范本样式。 
-F 将范本样式视为固定字符串的列表。 
-G 将范本样式视为普通的表示法来使用。 
-h 在显示符合范本样式的那一列之前,不标示该列所属的文件名称。 
-H 在显示符合范本样式的那一列之前,标示该列的文件名称。 
-i 胡列字符大小写的差别。 
-l 列出文件内容符合指定的范本样式的文件名称。 
-L 列出文件内容不符合指定的范本样式的文件名称。 
-n 在显示符合范本样式的那一列之前,标示出该列的编号。 
-q 不显示任何信息。 
-R/-r 此参数的效果和指定“-d recurse”参数相同。 
-s 不显示错误信息。 
-v 反转查找。 
-w 只显示全字符合的列。 
-x 只显示全列符合的列。 
-y 此参数效果跟“-i”相同。 
-o 只输出文件中匹配到的部分。
常用方法:
当前所有含 hello 的文件
# grep "hello" * -Rn 

当前所有不含 hello 的文件 
# grep "hello" * -vn 

10、wc

命令格式:wc 参数文件1 文件2 ....
功能:统计指定文件中的字节数、字数、行数并将统计结果显示输出。
选项:
-c或--bytes或——chars:只显示Bytes数; 
-l或——lines:只显示列数; 
-w或——words:只显示字数。
实例:
# wc aio.h 
 246  967 7502 aio.h
分别是: 列数  字数  字节数 文件名

三、重定向与管道

shell 命令在执行时,会自动打开三个标准文件,标准输入文件(stdin,一般对应终端的键盘),标准输出文件(stdout)和标准出错输出文件(stderr,对应终端的屏幕),参看:C语言再学习 -- 文件
在实际应用中,这三个文件常常需要按照新的格式进行定向,从其他文件中导入内容或将内容导出到其他文件中,这个过程就是重定向使内容按照一定格式输出,这就是管道

1、重定向

重定向可分为输出重定向、错误重定向与输入重定向

(1)输出重定向

通过重定向符“>”或“>>”将命令的标准输出重新定向到指定文件中。
一般形式:命令 > 文件名
“>”与“>>”都能将内容重新写入到文件中,但如果文件中有内容,执行“>”后新的内容将会覆盖掉原来的内容,而“>>”则是将新的输出内容附加到原来内容的结尾。
实例:
新建text.txt
# touch text.txt

将ps内容输出到 text.txt 文件中
# ps > text.txt 

查看 text.txt 内容
# cat text.txt 
  PID TTY          TIME CMD
 2600 pts/0    00:00:00 bash
 3231 pts/0    00:00:00 ps

如果再将 ls内容使用 ">" 输出到 text.txt 文件中
# ls > text.txt 
再查看 text.txt内容,则覆盖掉了原来的内容
# cat text.txt 
text.txt

如果再将 ps内容使用 ">>" 输出到 text.txt 文件中
# ps >> text.txt 
再查看 text.txt内容,则附加到原来的内容的结尾
# cat text.txt 
text.txt
  PID TTY          TIME CMD
 2600 pts/0    00:00:00 bash
 3236 pts/0    00:00:00 ps

(2)错误重定向

通过重定向符“2>”或“2>>”将命令的标准错误输出重定向到指定文件中。
“2>”和 “2>>”区别同上面的“>”和“>>”这里就不做说明了。
一般形式:
命令 2> 文件名
命令 2>> 文件名
实例:
hello没有这个文件,查看会出错
# cat hello
cat: hello: 没有那个文件或目录

将错误保存到 text.txt 文件中
# cat hello 2> text.txt 
# cat text.txt 
cat: hello: 没有那个文件或目录

(3)输入重定向

通过重定向符“<”将命令的标准输入重新定位到指定文件中。
一般形式:命令 < 文件名
实例:
查看脚本
# cat sh.sh 
echo “you working directory is $(pwd)”
echo "the time is $(date)"

shell命令解析程序从脚本程序 sh.sh 中读取命令行并加以执行
# bash < sh.sh 
“you working directory is /home/tarena/project/c_test”
the time is Wed Mar 22 10:07:45 CST 2017

2、管道

在 Linux 下我们可以采用管道操作符 “|”来连接多个命令或进程,在连接的管道线两边,每个命令执行时都是一个独立的进程。前一个命令的输出正是下一个命令的输入。这些进程可以同时进行,而且随着数据流在它们之间的传递可以自动地进行协调,从而能够完成较为复杂的任务。管道我们也并不陌生,之前讲 xargs 用法时有用到的。
参看:C语言再学习 -- Xargs用法详解
一般形式:[命令1] | [命令2] | [命令3]
实例:
ls 命令查看
# ls
sh.sh  text.txt

可以可以指定查找脚本文件
# ls | grep *sh
sh.sh

四、shell简单应用

shell 除了作为命令编译器用于管理命令外,还可以用来进行程序设计。它提供了定义变量和参数的手段以及丰富的过程控制结构。使用 shell 编程类似于使用 DOS 中的批处理文件,称为 shell 脚本,又叫 shell 程序 或 shell 命令文件。

1、基本用法

(1)开头

程序必须以下面的行开始,且必须放在文件的第一行。
#!/bin/bash
符号“#!”用来告诉系统它后面的参数是用来执行该文件的程序,在这个例子中使用 /bin/bash 来执行程序。
而 /bin/bash 正是 bash shell  的路径。 当编译好脚本时,如果要执行该脚本,我们还必须使其可执行。要使脚本可执行,我们需赋予该文件可执行的权限,可以使用如下命令文件:
chmod +x [文件]  或者  chmod 777 [文件]
修改文件权限,之前也有讲可参看:C语言再学习 -- 修改linux文件权限

(2)注释

在进行 shell 编程时,以“#”开头的句子表示注释,直到这一行的结束,我们建议在程序中使用注释。使用注释,即使相当长的时间内没有使用该脚本,我们也能在很短的时间内明白该脚本的作用及工作原理。

(3)提示符

在脚本中使用提示符“$”,后面使用圆括号括住命令,则可以执行该命令。
如果是环境变量,则直接跟在“$”后面。
实例:
查看脚本
# cat sh.sh 
#!/bin/bash
echo $(ls)
echo $SHELL

执行脚本
# ./sh.sh 
sh.sh text.txt
/bin/bash

2、脚本演示

(1)创建脚本

#!/bin/bash
#program date
#show the date in this way
echo "Mr.$USER,Today is:"
#echo $(date)
echo $(date)
echo Wish you a lucky day !	

(2)设置可执行权限

# chmod 777 sh.sh 

(3)执行程序

# ./sh.sh 
Mr.root,Today is:
Wed Mar 22 10:47:33 CST 2017
Wish you a lucky day !

五、shell编程语法

shell 编程语法主要有变量、控制结构和函数三部分。
其中控制结构和函数,与C语言类似。
参看:C语言再学习 -- 分支与跳转语句
参看:C语言再学习 -- 循环语句
参看:C语言再学习 -- 函数

1、变量

与各种高级程序设计语言相似,shell 环境下我们也可以使用一组文字或符号,来替换一些设置或者一串保留的数据,这组文字或符号便是 shell 变量根据使用功能不同我们将 shell 变量分为用户自定义变量、位置参数与环境变量

(1)自定义变量

定义:变量名=变量值
在使用变量之前不需要事先声明,我们只需要通过“=”,给它们赋初始值便可使用。注意,等号两边不能留空格,如果一定要出现空格,就要用双引号括起来
实例:
# here=beijing
此时系统便定义了 here 这个内容 为 beijing 的变量。查看变量内容我们可以在变量前面加上一个“$”符号,再用 echo 命令将其内容输出到终端上。
# here=beijing
# echo $here
beijing
这部分类似于环境变量,在此不多讲,参看:UNIX再学习 -- 环境变量

(2)位置参数变量

在 Linux/UNIX 系统中,shell 脚本执行时是可以带实参的。这些实参在脚本执行期间将会被赋予系统中自动定义好的一类变量中,这类变量就是位置参数变量
命令行实参与脚本中位置参数变量的相对应关系如下所示:
$0 $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11}

$0:脚本名称
$1-$9:第 1 至第 9 个参数
${}:获取第 9 个以上参数
$#:表示传给脚本或者函数的位置参数的个数(不包括“$0”)
$*:所有位置参数的列表,形式是一个单个字符串,串中第 1 个参数由第 1 个字符串分隔
$@:所有位置参数被分别表示为双引号中的N(参数个数,不含$0)个字符串

$* 和 $@ 均可以表示所有位置参数,但它们之间却存在着很大的不同,这种不同允许用两种方式来处理命令行参数。
第一种:“$*”,因为它是一个单个字符,所以可以不需要很多 shell 代码来显示它,相比之下更加灵活。
第二种:“$@”,它允许我们独立处理每个参数,因为它的值是 N 个分离参数。

再有,在执行 shell 程序时,位置参数变量并不是固定不变的,利用 set 命令可以为位置参数赋值或重新赋值
实例:
# echo 1 2 3
1 2 3

分析:有 4 个位置参数
1 个命令名(echo) + 3 个参量 (1、2、3),$0 = ehco $1 = 1 $2 = 2 $3 = 3 
#!/bin/bash
echo "one=$1"
echo "two=$2"
echo "three=$3"
echo "count=$#"
echo "all=$*"
echo "all=$@"
set A B C
echo "all=$*"

执行脚本:# ./sh.sh 1 2 3
one=1
two=2
three=3
count=3
all=1 2 3
all=1 2 3
all=A B C

(3)环境变量

这部分不多讲,参看:UNIX再学习 -- 环境变量

2、控制结构

shell 程序设计语言的基础是对条件进行测试判断,根据不同的测试结果采取相应的程序处理。下面我们先分析在 shell 脚本程序中可以使用的判断条件,然后讲解使用这些条件的控制结构。

(1)判断条件

条件测试

条件测试有两种常用形式:第一种是用 test 命令与系统运算符一起使用第二种是用一对方括号与系统运算符一起使用。这两种形式是完全等价的。注意,利用一对方括号表示条件测试时,左方括号“[”之后、右方括号“]”之前各应有一个空格。 在大多数系统中,两者没有太大的区别,都使得程序设计语法看起来更加简单明了。
实例:
#!/bin/bash
#if test -f "$1"
if [ -f "$1" ]
then echo "$1 is an ordinary file."
else echo "$1 is not ordinary file."
fi

执行脚本:# ./sh.sh hello.txt
hello.txt is not ordinary file.

shell 编程中的 test 与 [] 命令 常见用法:
参看:test 命令
判断表达式:
if test #表达式为真 
if test ! #表达式为假 
test 表达式1 –a 表达式2 #两个表达式都为真 
test 表达式1 –o 表达式2 #两个表达式有一个为真 
test 表达式1 ! 表达式2 #条件求反

判断字符串:
test –n 字符串 #字符串的长度非零 
test –z 字符串 #字符串的长度是否为零 
test 字符串1=字符串2 #字符串是否相等,若相等返回true 
test 字符串1!=字符串2 #字符串是否不等,若不等反悔false
判断整数:
test 整数1 -eq 整数2 #整数相等 
test 整数1 -ge 整数2 #整数1大于等于整数2 
test 整数1 -gt 整数2 #整数1大于整数2 
test 整数1 -le 整数2 #整数1小于等于整数2 
test 整数1 -lt 整数2 #整数1小于整数2 
test 整数1 -ne 整数2 #整数1不等于整数2
判断文件:
test File1 –ef File2 两个文件是否为同一个文件,可用于硬连接。主要判断两个文件是否指向同一个inode。 
test File1 –nt File2 判断文件1是否比文件2新 
test File1 –ot File2 判断文件1比是否文件2旧 
test –b file #文件是否块设备文件 
test –c File #文件并且是字符设备文件 
test –d File #文件并且是目录 test –e File #文件是否存在 (常用) 
test –f File #文件是否为正规文件 (常用) 
test –g File #文件是否是设置了组id 
test –G File #文件属于的有效组ID 
test –h File #文件是否是一个符号链接(同-L) 
test –k File #文件是否设置了Sticky bit位 
test –b File #文件存在并且是块设备文件 
test –L File #文件是否是一个符号链接(同-h) 
test –o File #文件的属于有效用户ID 
test –p File #文件是一个命名管道 
test –r File #文件是否可读 
test –s File #文件是否是非空白文件 
test –t FD #文件描述符是在一个终端打开的 
test –u File #文件存在并且设置了它的set-user-id位 
test –w File #文件是否存在并可写 
test –x File #文件属否存在并可执行

条件测试应用:

在实际 shell 程序设计中,使用单一判断条件的情况很少,它往往与 &&、|| 等组成符合判断条件来使用。
语法结构如下:
判断条件1 && 判断条件2
判断条件3 || 判断条件4
语法分析如下: 
判断条件1 为真时才会执行 判断条件2,否则忽略 判断条件2
判断条件3 为假时才会执行 判断条件4,否则忽略 判断条件4
#!/bin/bash
if [ $1 -ge 0 ] && [ $1 -lt 10 ];
then echo "0 < $1 < 10"
fi

执行脚本:# ./sh.sh 3
0 < 3 < 10

(2)条件判断结构

if 语句

功能:用于条件控制结构中
语法格式1:
if [条件判断表达式]; then
当条件表达式成立时,可以执行命令 fi
语法格式2:
if [条件判断表达式]; then
当条件表达式成立时,可以执行命令 else
当条件表达式不成立时,可以执行命令 fi
语法格式3:
if [条件判断表达式1]; then
当条件表达式1成立时,可以执行命令
elfi [条件判断表达式2]; then
当条件表达式2成立时,可以执行命令 else
当条件表达式1与2均不成立时,可执行命令 fi
实例:
#!/bin/bash
#if test -f "$1"
if [ -f "$1" ]
then echo "$1 is an ordinary file."
else echo "$1 is not ordinary file."
fi

执行脚本:# ./sh.sh hello.txt
hello.txt is not ordinary file.

case语句

功能:允许进行多重条件选择
语法结构:
case $变量名称 in
第一个变量内容)
程序段 (满足第一个变量内容)
;;
第二个变量内容)
程序段二(满足第二个变量内容)
;;
...
*)
程序段(均不满足前面的条件下)
...
;;
esac
语法分析:
该语句执行过程是用“字符串”的值依次与各模式字符串进行比较,如果发现同某个匹配,那么就执行该模式字符串之后的各个命令,直至遇到两个分号为止;如果没有任何模式字符串与该字符串的值相符合,则不执行任何命令。
在使用 case 语句时我们应该注意以下几点:
    每个模式字符串后面可有一条或多条命令,它的最后一条命令必须以两个分号(即;;)结束
    模式字符串中可以使用通配符。
    如果一个模式字符串中包含多个模式,那么各模式之间应以竖线(|)隔开,表示各模式是“或”的关系,即只要给定字符串与其中一个模式相配,就会执行其后的命令表。
    各模式字符串应是唯一的,不应重复出现,因为“*”可以与任何字符串匹配,它如果第一个出现,就不会再检查其他模式了。
    case 语句以关键字 case 开头,以关键字 esac (是 case 倒过来写)结束
    case 的退出(返回)值是整个结构中最后执行的那个命令的退出值;如果没有执行任何命令,那么退出值为零。
实例:
#!/bin/bash
echo "a) choice a"
echo "b) choice b"
echo "c) choice c"
echo -e "Please enter your choice:\c"
read menu_choice
case "$menu_choice" in
a) echo "you choice a" ;;
b) echo "you choice b" ;;
c) echo "you choice c" ;;
*) echo "sorry, choice not exist" ;;
esac

执行脚本# ./sh.sh
a) choice a
b) choice b
c) choice c
Please enter your choice:a
you choice a

(3)循环结构

循环可以不断执行某个程序段,直到用户设置的条件实现为止。下面介绍几种常用的循环结构:

while do done 语句

语法结构:
while [条件判断表达式]
do
程序段
done
语法分析:
当条件判断表达式成立时,就进行循环,直到条件判断表达式不成立才停止。
实例:
#!/bin/bash
echo "a) choice a"
echo "b) choice b"
echo "c) choice c"
echo -e "Please enter your choice:\c"
read menu_choice
while [ "$menu_choice" != "a" ] && [ "$menu_choice" != "b" ] && [ "$menu_choice" != "c" ]
do
echo -e "Please enter your choice (a/b/c) to stop this programe:\c"
read menu_choice
done
执行脚本# ./sh.sh
a) choice a
b) choice b
c) choice c
Please enter your choice:d
Please enter your choice (a/b/c) to stop this programe:a

until do done 语句

语法结构:
until [条件判断表达式]
do 
程序段
done
语法分析:
与前面的 while do done 刚好相反,它说的是“当条件判断表达式成立时,就终止循环,否则就持续执行循环的程序段”
实例:
#!/bin/bash
echo "a) choice a"
echo "b) choice b"
echo "c) choice c"
echo -e "Please enter your choice:\c"
read menu_choice
until [ "$menu_choice" = "a" ] || [ "$menu_choice" = "b" ] || [ "$menu_choice" = "c" ]
do
echo -e "Please enter your choice (a/b/c) to stop this programe:\c"
read menu_choice
done

执行脚本:# ./sh.sh
a) choice a
b) choice b
c) choice c
Please enter your choice:d
Please enter your choice (a/b/c) to stop this programe:a

for do done 语句

语句结构:
for [条件判断表达式]
do
程序段
done
语法分析:
for 语句是最常用的建立循环结构的语句,其条件判断表达式更是形式多样,同 while 一样,是当满足条件判断时,就进行循环,知道条件不成立才停止。
实例:
#!/bin/bash
for num in 1 2 3 4 5 
do 
echo "$num" 
done

执行脚本:# ./sh.sh
1
2
3
4
5

(4)其他结构:break 命令 和 continue 命令

break 命令

功能:可以使我们从循环体重退出来
语法格式:
break [ n ]
命令中 n 表示要跳出几层循环,默认值是 1,表示只跳出一层循环

continue 命令

功能:跳出循环体在它之后的语句,回到本层循环的开头,进行下次循环。
语法格式:
continue [ n ]
命令中 n 表示从包含 continue 语句的最内层循环体向外跳到第几层循环,默认值为 1,循环层数由内向外编号。

3、函数

到目前为止所编写的 shell 程序都是非常短小的。在实际应用中,有时为项目所编写的脚本程序时非常大型的,这时我们该如何构造自己的代码呢?可能想到说讲大型脚本按照功能模块拆分成多个小型脚本,但这种做法存在如下几个缺点.
    在一个脚本程序中运行另外一个脚本程序要比执行一个函数慢得多。
    返回执行结果变得更加困难,而且可能存在非常多的小脚本。
基于上面原因及拆分思想,我们可以定义并使用 shell 函数
语法格式:
[function]函数名()
{
	命令表 (statements)
}
语法分析:
关键字 function 可以默认。通常,函数中的最后一个命令执行之后,就退出被调用函数。
我们也可以利用 return 命令立即退出函数
语法格式:
return [ n ]
命令中,n 值是退出函数时的退出值(退出状态),即 $? 的值。当 n 值缺省时,退出值是最后一个命令执行结果。
函数应先定义,后使用调用函数时,我们可以直接利用函数名,如 foo 不必带圆括号,就像一般命令那样使用。使用函数的最大作用就是可以简化很多代码,这在较大的 shell 脚本设计中国可能会更加明显。
实例:
#!/bin/bash
first ()
{
	echo "*****************************"
}
second ()
{
	echo "============================="
}
trird ()
{
	echo -e "\fhello world!\f"
}
first
second
trird
second
first

执行脚本:# ./sh.sh
*****************************
=============================

hello world!

=============================
*****************************

六、实例展示

#!/bin/sh
e=""
m=""
n="xxxx"
while [ 1 ]
do
	n="xxxx"
	e=`fdisk -l | grep /dev/sdb1 | awk '{print $1}'`
	if [ -z "$e" ];then
		echo "####no sd ####"
	else
		echo "####have sd !####"
		m=`df | grep media | awk '{print $1}'`
		if [ -n "$m" ];then
			umount $m
		fi
		#umount $e
		echo "####start to format sd with ext3 filesystem!####"
		mkfs.ext3 "$e"
		echo "####end format sd!####"
		#echo "copy zslf_app_dir"
		mount $e /media/mmcblk0p1
		
		mkdir /media/mmcblk0p1/bak
		mkdir /media/mmcblk0p1/video
		mkdir /media/mmcblk0p1/video/temp
		umount $e
	
		echo "please remove sd!"
	fi
	sleep 5
	if [ -n "$e" ];then
		while [ -n "$n" ]
		do
			n=`fdisk -l | grep /dev/sdb1 | awk '{print $1}'`
			sleep 5
		done
	fi
done 																					
#!/bin/sh
php_config_copy ()
{
	echo "start copy the config file from jyzc to thttpd/www."
	cp /jyzc/config/encodeCfg.xml /jyzc/thttpd-php/www/config/encodeCfg.xml
	cp /jyzc/config/devInfo.txt /jyzc/thttpd-php/www/config/devInfo.txt
	cp /etc/network/interfaces /jyzc/thttpd-php/www/config/interfaces
}
ppp_config_copy
{
	echo "start copy th config file from jyzc/config to /etc/ppp/peers."
	cp /jyzc/config/ppp-off /etc/ppp/peers/
	cp /jyzc/config/ppp0_on_state.txt /etc/ppp/peers/
}
echo "############## jyzc  ##################"
php_config_copy
ppp_config_copy
route del default
mknod /dev/pio c 203 0
cd /etc/ppp/information/
rm -rf addr.txt
echo " " > /etc/ppp/information/state.txt

#mknod -m 660 /dev/ttyUSB0 c 188 0
#mknod -m 660 /dev/ttyUSB1 c 188 1
#mknod -m 660 /dev/ttyUSB2 c 188 2
#mknod -m 660 /dev/ttyUSB3 c 188 3
#mknod -m 660 /dev/ttyUSB4 c 188 4
#mknod -m 660 /dev/ttyUSB5 c 188 5

/jyzc/ntpdate_first.sh &
./state.sh &
/jyzc/create_mem &
sleep 5
#/jyzc/3gStrength &
/jyzc/thttpd-php/sbin/thttpd -C /jyzc/thttpd-php/thttpd.conf
sleep 1
vsftpd 
sleep 2
mkdir /media/mmcblk0p1/bak
cd /jyzc/config/
./srv 8000 &
./pio_led &
/jyzc/rm_pic.sh &

#4G LTE Setup Script
#/jyzc/LteSetUp.sh &
#sleep 10
#i=`cat /proc/bus/usb/devices | grep ProdID=1573`
#./MU609-3gState &
insmod /jyzc/usbnet.ko
insmod /jyzc/cdc_encap.ko
insmod /jyzc/cdc_ether.ko
sleep 2
/jyzc/ME3760Connect

echo "######## encode #########"
cd /jyzc
./initial.sh
./get_ntptime &
./encode &
./thttpd.sh &

你可能感兴趣的:(UNIX再学习,UNIX再学习)