shell编程初步

Posted Overboom

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了shell编程初步相关的知识,希望对你有一定的参考价值。

0. shell的作用

Shell就是命令行工具的胶水,没有任何语言能像Shell一样方便地将一大堆命令行工具组合起来。原则上来说,Shell做什么都可以,但显然它最适合的是自动化,因为只需要将你原来手动敲的命令都复制到一个文件里面就行了。
Shell跟标准的编程语言区别很大,它基本上是一个面向字符串的编程语言,组合用好awk/sed/grep,偶尔配合eval,有时候会发挥奇效。

1. shell基本语法

1.1 变量

按照惯例,Shell变量通常由字母加下划线开头,由任意长度的字母、数字、下划线组成。有两种类型的Shell变量:
1.环境变量
环境变量可以从父进程传给子进程,因此Shell进程的环境变量可以从当前Shell进程传给fork出来的子进程。用printenv命令可以显示当前Shell进程的环境变量。
2.本地变量
只存在于当前Shell进程。
3. 变量赋值
环境变量是任何进程都有的概念,而本地变量是Shell特有的概念。在Shell中,环境变量和本地变量的定义和用法相似。在Shell中定义或赋值一个变量:

VAR_NAME=value

注意等号两边都不能有空格,否则会被Shell解释成命令和命令行参数。
一个变量定义后仅存在于当前Shell进程,它是本地变量,用export命令可以把本地变量导出为环境变量
4. 用unset删除变量

unset VAR_NAME

5. 引用变量

$VAR_NAME

1.2 单引号和双引号

被双引号用括住的内容,将被视为单一字串。它防止通配符扩展,但允许变量扩展。这点与单引号的处理方式不同

1.3 条件测试

shell中提供两种条件判断的方法。
1. test指令

overboom@overboom:~/Work $ var=2
overboom@overboom:~/Work $ test $var -gt 1
overboom@overboom:~/Work $ echo $?
0
overboom@overboom:~/Work $ 
overboom@overboom:~/Work $ 
overboom@overboom:~/Work $ test $var -eq 3
overboom@overboom:~/Work $ echo $?
1
overboom@overboom:~/Work $ test $var -eq 2
overboom@overboom:~/Work $ echo $?
0

$? 代表上一个命令执行后的退出状态

2. [ condition ] 注意condition前后要有空格。非空返回0,0为 true,否则为 false 。

overboom@overboom:~/Work$ var=2
overboom@overboom:~/Work$ [ $var -gt 3 ] 
overboom@overboom:~/Work$ echo $?
1
overboom@overboom:~/Work$ [ $var -eq 2 ]
overboom@overboom:~/Work$ echo $?
0
overboom@overboom:~/Work$ 

常见测试指令如下:
[ -d DIR ] 如果DIR存在并且是一个目录则为真
[ -f FILE ] 如果FILE存在且是一个普通文件则为真
[ -z STRING ] 如果STRING的长度为零则为真
[ -n STRING ] 如果STRING的长度非零则为真
[ STRING1 = STRING2 ] 如果两个字符串相同则为真
[ STRING1 != STRING2 ] 如果字符串不相同则为真
[ ARG1 OP ARG2 ] ARG1和ARG2应该是整数或者取值为整数的变量,OP是-eq(等于)-ne(不等于)-lt(小于)-le(小于等于)-gt(大于)-ge(大于等于)之中的一个
3. 条件测试与逻辑运算符
和C语言类似,测试条件之间还可以做与、或、非逻辑运算:
[ ! EXPR ] EXPR可以是上表中的任意一种测试条件,!表示“逻辑反(非)”
[ EXPR1 -a EXPR2 ] EXPR1和EXPR2可以是上表中的任意一种测试条件,-a表示“逻辑与”
[ EXPR1 -o EXPR2 ] EXPR1和EXPR2可以是上表中的任意一种测试条件,-o表示“逻辑或”

1.4 分支结构

1.4.1 if/then/elif/else/fi

和C语言类似,在Shell中用if、then、elif、else、fi这几条命令实现分支控制。
下面看一段示例code

#! /bin/sh

if [ -f /bin/bash ]
then 
	echo "/bin/bash is a file"
else 
	echo "/bin/bash is NOT a file"
fi
if :; then echo "always true"; fi

与C类似的语法,并不难理解。
“:”是一个特殊的命令,称为空命令,该命令不做任何事,但Exit Status总是真。此外,也可以执行/bin/true或/bin/false得到真或假的Exit Status。

1.4.2 case/esac

case命令可类比C语言的switch/case语句,esac表示case语句块的结束。C语言的case只能匹配整型或字符型常量表达式,而Shell脚本的case可以匹配字符串和Wildcard,每个匹配分支可以有若干条命令,末尾必须以;;结束,执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac之后,不需要像C语言一样用break跳出。

#! /bin/sh

echo "Is it morning? Please answer yes or no."
read YES_OR_NO
case "$YES_OR_NO" in
yes|y|Yes|YES)
	echo "Good Morning!";;
[nN]*)
	echo "Good Afternoon!";;
*)
	echo "Sorry, $YES_OR_NO not recognized. Enter yes or no."
	return 1;;
esac
return 0

1.5 循环

1.5.1 for/do/done

Shell脚本的for循环结构和C语言很不一样,它类似于某些编程语言的foreach循环。例如:

#! /bin/sh

for FRUIT in apple banana pear; do
	echo "I like $FRUIT"
done

1.5.2 while/do/done

while的用法和C语言类似。比如一个验证密码的脚本:

#! /bin/sh

echo "Enter password:"
read TRY
while [ "$TRY" != "secret" ]; do
	echo "Sorry, try again"
	read TRY
done

1.5.3 break和continue

break[n]可以指定跳出几层循环;continue跳过本次循环,但不会跳出循环。
即break跳出,continue跳过。

2 位置参数和特殊变量

shell中常用的位置参数和特殊变量总结:

$0 			相当于C语言main函数的argv[0]
$1$2...	这些称为位置参数(Positional Parameter),相当于C语言main函数的argv[1]、argv[2]...
$# 			相当于C语言main函数的argc - 1,注意这里的#后面不表示注释
$@ 			表示参数列表"$1" "$2" ...,例如可以用在for循环中的in后面。
$* 			表示参数列表"$1" "$2" ...,同上
$? 			上一条命令的Exit Status
$$ 			当前进程号
#!/bin/bash


echo ------start--------
echo $0 $1 $2 
echo $*
echo $@
echo $$
echo $#
echo ------end--------

输出如下:

可以在shell脚本中使用shift丢弃一些参数

#!/bin/bash

echo ------start--------

echo ------before shift--------

echo $0 $1 $2 $3 $4 $5
echo $#

shift
shift
shift

echo ------after shift--------
echo $0 $1 $2 $3 $4 $5
echo $#

echo ------end--------

输出如下:

3. 输入和输出

3.1 echo

显示文本行或变量,或者把字符串输入到文件。

echo [option] string
-e 解析转义字符
-n 不回车换行。默认情况echo回显的内容后面跟一个回车换行。

3.2 tee

tee命令把结果输出到标准输出,另一个副本输出到相应文件。

overboom@overboom:~/Work$ ls -al
total 52
drwxrwxr-x 11 overboom overboom 4096 912 22:47 .
drwxr-xr-x 22 overboom overboom 4096 912 22:47 ..
drwxr-xr-x  7 root     root     4096 74 18:39 beanCode
drwxrwxr-x  2 overboom overboom 4096 327 23:45 c_code
drwxrwxr-x  3 overboom overboom 4096 810 21:07 fdbus
drwxrwxr-x  3 overboom overboom 4096 810 23:16 git_usage
drwxrwxr-x  4 overboom overboom 4096 62 00:08 jsoncpp
drwxrwxr-x  2 overboom overboom 4096 84 21:20 minixml
-rwxrwxr-x  1 overboom overboom  213 912 22:45 position.sh
drwxrwxr-x  2 overboom overboom 4096 527 23:43 test_C++Sqlite
drwxrwxr-x  6 overboom overboom 4096 525 21:37 test_find
-rwxrwxr-x  1 overboom overboom  141 912 21:23 test.sh
drwxrwxr-x  3 overboom overboom 4096 410 17:36 xwp_linux
overboom@overboom:~/Work$ ls -al | tee out_file
total 52
drwxrwxr-x 11 overboom overboom 4096 912 23:02 .
drwxr-xr-x 22 overboom overboom 4096 912 22:47 ..
drwxr-xr-x  7 root     root     4096 74 18:39 beanCode
drwxrwxr-x  2 overboom overboom 4096 327 23:45 c_code
drwxrwxr-x  3 overboom overboom 4096 810 21:07 fdbus
drwxrwxr-x  3 overboom overboom 4096 810 23:16 git_usage
drwxrwxr-x  4 overboom overboom 4096 62 00:08 jsoncpp
drwxrwxr-x  2 overboom overboom 4096 84 21:20 minixml
-rwxrwxr-x  1 overboom overboom  213 912 22:45 position.sh
drwxrwxr-x  2 overboom overboom 4096 527 23:43 test_C++Sqlite
drwxrwxr-x  6 overboom overboom 4096 525 21:37 test_find
-rwxrwxr-x  1 overboom overboom  141 912 21:23 test.sh
drwxrwxr-x  3 overboom overboom 4096 410 17:36 xwp_linux
overboom@overboom:~/Work$ ll
total 56
drwxrwxr-x 11 overboom overboom 4096 912 23:02 ./
drwxr-xr-x 22 overboom overboom 4096 912 22:47 ../
drwxr-xr-x  7 root     root     4096 74 18:39 beanCode/
drwxrwxr-x  2 overboom overboom 4096 327 23:45 c_code/
drwxrwxr-x  3 overboom overboom 4096 810 21:07 fdbus/
drwxrwxr-x  3 overboom overboom 4096 810 23:16 git_usage/
drwxrwxr-x  4 overboom overboom 4096 62 00:08 jsoncpp/
drwxrwxr-x  2 overboom overboom 4096 84 21:20 minixml/
-rw-rw-r--  1 overboom overboom  793 912 23:02 out_file
-rwxrwxr-x  1 overboom overboom  213 912 22:45 position.sh*
drwxrwxr-x  2 overboom overboom 4096 527 23:43 test_C++Sqlite/
drwxrwxr-x  6 overboom overboom 4096 525 21:37 test_find/
-rwxrwxr-x  1 overboom overboom  141 912 21:23 test.sh*
drwxrwxr-x  3 overboom overboom 4096 410 17:36 xwp_linux/
overboom@overboom:~/Work$ cat out_file 
total 52
drwxrwxr-x 11 overboom overboom 4096 912 23:02 .
drwxr-xr-x 22 overboom overboom 4096 912 22:47 ..
drwxr-xr-x  7 root     root     4096 74 18:39 beanCode
drwxrwxr-x  2 overboom overboom 4096 327 23:45 c_code
drwxrwxr-x  3 overboom overboom 4096 810 21:07 fdbus
drwxrwxr-x  3 overboom overboom 4096 810 23:16 git_usage
drwxrwxr-x  4 overboom overboom 4096 62 00:08 jsoncpp
drwxrwxr-x  2 overboom overboom 4096 84 21:20 minixml
-rwxrwxr-x  1 overboom overboom  213 912 22:45 position.sh
drwxrwxr-x  2 overboom overboom 4096 527 23:43 test_C++Sqlite
drwxrwxr-x  6 overboom overboom 4096 525 21:37 test_find
-rwxrwxr-x  1 overboom overboom  141 912 21:23 test.sh
drwxrwxr-x  3 overboom overboom 4096 410 17:36 xwp_linux

3.3 文件重定向

cmd > file 				把标准输出重定向到新文件中
cmd >> file 			追加
cmd > file 2>&1 		标准出错也重定向到1所指向的file里
cmd >> file 2>&1
cmd < file1 > file2 	输入输出都定向到文件里
cmd < &fd 				把文件描述符fd作为标准输入
cmd > &fd 				把文件描述符fd作为标准输出
cmd < &- 				关闭标准输入	

4.函数

和C语言类似,Shell中也有函数的概念,但是函数定义中没有返回值也没有参数列表。
Shell函数没有参数列表并不表示不能传参数,事实上,函数就像是迷你脚本,调用函数时可以传任意个参数,在函数内同样是用$0、$1、$2等变量来提取参数,函数中的位置参数相当于函数的局部变量,改变这些变量并不会影响函数外面的$0、$1、$2等变量。函数中可以用return命令返回,如果return后面跟一个数字则表示函数的Exit Status。

5. shell脚本调试方法

Shell提供了一些用于调试脚本的选项,如:
-n 读一遍脚本中的命令但不执行,用于检查脚本中的语法错误。
-v 一边执行脚本,一边将执行过的脚本命令打印到标准错误输出。
-x 提供跟踪执行信息,将执行的每一条命令和结果依次打印出来。
这些选项有三种常见的使用方法:

1.在命令行提供参数。如:
$ sh -x ./script.sh
2.在脚本开头提供参数。如:
#! /bin/sh -x
3.在脚本中用set命令启用或禁用参数。如:
#! /bin/sh

if [ -z "$1" ]; then
	set -x
	echo "ERROR: Insufficient Args."
	exit 1
	set +x
fi

set -x和set +x分别表示启用和禁用-x参数,这样可以只对脚本中的某一段进行跟踪调试。

以上是关于shell编程初步的主要内容,如果未能解决你的问题,请参考以下文章

Shell编程初步

初步shell脚本编程

第5天(半天)shell编程初步grep及正则表达式

shell编程初步

linux之shell编程初步

2.5-shell编程初步