linux操作系统——shell编程

Shell编程是Linux运维人员必备技能,本文介绍了Shell脚本的基础知识,包括编写第一个脚本、变量、条件测试、循环、函数等。通过实例解析,帮助读者掌握Shell脚本的基本语法和常用操作。

Shell是一个命令解释器。与Python,R等脚本语言类似,我们既可以在命令解释器上把命令一行一行敲出来执行,也可以把很多行保存到一个文件(即脚本/script),再让命令解释器执行这个文件。Window里管这个叫批处理(Batch)。因此凡是可以在终端上运行的命令,都可以写到shell脚本里。另外,shell支持变量、分支语句、循环、自定义函数,因此可以用shell脚本实现比较复杂的操作,从而将常规系统管理的操作变成可复用的模块。Shell编程是Linux运维人员必备能力。通过本实验仅能掌握其基本语法,但是想达到企业级应用的水平,需要掌握更多命令,尤其是文本处理命令sed和awk最为重要,以及计划任务、各种系统管理命令等。

编写第一个shell脚本

新建一个shell脚本文件,请注意,文件名后缀一定为.sh。

vi hello.sh

打开vi编辑模式

#!/bin/sh
echo 'Hello World!'

echo命令用于将一个字符串在终端上打印出来。

有两种方法可以用来执行这个脚本。

最简单的方法,用sh命令去解释它。

sh hello.sh

当然,也可以把它作为一个可执行程序运行,但需要添加可执行权限。

chmod +x hello.sh
./hello.sh

Sharp-Band(#!)和注释

Shell脚本的首行要写上“#!/bin/sh”,前面最开始的两个字符“#!”正好组成一个16bit的幻数即0x2321。在unix中执行一个可执行程序时,操作系统将查看该文件的前几个字节,即所谓的“幻数”。它可以用来决定程序的格式以及如何执行它。“#!/bin/sh”表明该script由/bin/sh进行解释执行。——当然,对于大部分Linux系统,你不写同样可以顺利执行,但100%建议你一定要写。

和其他脚本语言一样,Shell脚本只支持单行注释,以“#”开头。

#!/bin/sh
#My first shell script
echo 'Hello World!'

分号的作用

使用分号可以将本应写在两行的内容写在一行上,除此之外并没有使用的必要。

变量

shell中的变量只有一种,就是字符串(当然稍后我们会讲也可以将其作为数值处理)。脚本语言的变量不需要声明,直接赋值即可。

HELLO='Hello World'

注意,“=”的两边绝对不能出现空格。shell脚本是逐句、直接进行解释,如果写了空格,就会被shell认为是一个命令。

单引号和双引号

在shell里,字符串通常并不需要用引号引用才能表示。但如果字符串包含空格的话,就需要引号了,否则就会被认为是多个字符串——语法就会发生错误。

单引号和双引号都可以用来表示字符串,区别在于单引号引用的字符串中的全部字符,包括shell中的特殊字符/语法,都作为普通字符进行处理。而用双引号引用的字符串中的shell符号/语法会进行转义/解释。

变量的引用和打印变量

用$ {变量名}或 $ 变量名引用一个变量。而且可以用${变量名:index:length}轻松地截取字串。

注意$HELLO是我们上面赋值过的那个“Hello World”字符串,而HELLO只是一个内容为“HELLO”的字符串。

echo用来在终端上打印一个字符串。

echo ${HELLO}
echo $HELLO
echo ${HELLO:0:5}
echo ${HELLO:2:5}
echo ${HELLO:2}

请大家通过运行以上命令,自己掌握字符串截取。

set命令可以显示当前工作空间全部的变量和它们的值。

unset 变量名用来清除一个变量。

特殊的变量

变量说明
$#参数的个数
$number第number个参数,例:./my.sh abc,$0的值就是./my.sh,$1的值就是abc
$$当前进程的PID
$? 上一次命令的exit值(0-255),0代表没有错误,其他值(1-255)代表有错误
$@ 代表了参数的内容
$! Shell最后运行的后台Process的PID

变量的组合

把字符串放在一起就可以了: $ {variable-name1}${variable-name2}。

例:

TMP_VAR1='Hello, '
TMP_VAR2='World'
echo ${TMP_VAR1}${TMP_VAR2}

变量的默认值/自动初始化

如果变量已经被初始化,则使用它;如果没有被初始化,则使用默认值,但不会对该变量进行初始化。语法:${variable:-value}

例:

echo "This color is ${COLOR:-green}"
#可以打印出green,但是COLOR并没有初始化
echo $COLOR

COLOR=blue
echo "This color is ${COLOR:-green}"
#这次会打印出blue

如果希望使用默认值的同时又进行初始化,则使用:${COLOR:=green}。

要一直使用绝对路径

要一直使用绝对路径,并应该用一个变量保存它。因为你的脚本可能会被移动,或是在其他机器上使用。通过绝对路径及变量,增加你的脚本的可重用性。

例:

SRC_FILE='/tmp/message1'
DST_FILE='/tmp/message2'
cp ${SRC_FILE} ${DST_FILE}

只读变量

MAXVAL=65535
echo ${MAXVAL}
#65535
readonly MAXVAL
MAXVAL=32767
echo ${MAXVAL}

环境变量

学过Java的同学都知道,安装Java之后要设置一个叫PATH的环境变量:要把java和javac的安装目录增添到这个目录里,否则运行java或javac命令时无法找到这两个命令。这个就是环境变量。

环境变量是shell普通变量的一个子集,即环境变量仍是一个“变量”。

有三种方式可以配置环境变量。

方法一:首先已存在一个变量,再用export将其声明为环境变量。(注意:这个环境变量只在当前shell进程中临时创建,登出shell则不复存在。)

VARIABLE-NAME=value
export VARIABLE-NAME

或者一步到位。

export VARIABLE-NAME=value

方法二:修改/etc/profile文件:这个文件在启动shell时会运行,或者在shell运行后使用source /etc/profile重新运行,这个会修改所有用户的环境变量。

方法三:修改自己主目录下的.bash_profile。
环境变量与普通变量最大的区别是:环境变量能传递给子进程。

准备以下脚本testenv.h:

#!/bin/sh
echo $$:$HAHA

运行testenv.sh时,新进程是原来shell的子进程。当HAHA只是普通的变量时,子进程无法访问父进程的普通变量;而当我们把HAHA声明为环境变量后,子进程就可以从父进程那继承环境变量了。

env命令可以查看全部环境变量,比如PATH。

PATH环境变量中用“:“隔开多个路径。当我们在shell上执行一个命令时,就是从PATH中的每个路径中寻找该目录,如果都没有找到,则显示”command not found“。

一般来说,PATH中都不会包含”.“(当前目录),这也是为什么我们运行a.out时需要写”./a.,out“。当然,我们可以把”.“加入到PATH。

此时运行a.out就不必加”.“了。一般情况下,我们不建议把”.“加入PATH。通过下面方法就可以在当前环境下改回去,或重新读取profile文件或重新登录shell亦可。

因为环境变量也是变量,因此也使用unset清除一个环境变量。

I/O

在终端上打印字符串用echo命令。

read

read用于在终端上读取一个字符串,例:

#!/bin/sh
echo 'please input your name age'
read name age
echo "Your name is $name. Your age is $age"

文件重定向

命令功能
command > filename等价于command 1> filename,将命令的标准输出重定向到文件
command >> filename将命令的标准输出追加到到文件
command 2 > filename将命令的标准错误输出重定向到文件

条件测试(conditional testing)

我们在写分支语句或循环都需要逻辑判断才能确定确定是否执行,逻辑表达式的基本元素就是条件测试。条件测试有两种语法:test condition 或 [ condition ],我们推荐使用后者,更容易嵌入到分支语句和循环中。

Shell中的条件测试本质上是利用进程的返回值,上文已述,0为正常退出,1~255为有错误——因此,在shell的条件测试中,0为真,非0为假——这与C语言正好相反。

测试文件状态
例:tmp.txt可写吗?

[ -w tmp.txt ]
echo $?

也可以写作:

test -w tmp.txt
echo $?

测试字符串

有五种方式用来测试字符串:
[ string1 = string2 ] 判断字符串string1和string2是否相等
[ string1 != string2 ] 判断字符串string1和string2是否不相等
[ -z string ] 判断string字符串是否是空串
[ -n string ] 判断string是否是非空串
[ string ] 判断指定的字符串是否为空

也可以改写为 test 语法。

数值的比较测试

-eq 相等(equal)
-ne 不等(not equal)
-gt 大于(greater than)
-lt 小于(less than)
-ge 大于等于 (greater than or equal)
-le 小于等于 (less than or equal)

例:

NUM=100
[ $NUM -eq 100 ]
echo $?

循环和分支

for each循环

for循环有两种,一种是for each循环,一种是计数型循环。

例:for each循环

#!/bin/sh
for i in 1 3 5 7 9; do
	echo $i
done

For each循环如果跟命令替换相结合,功能非常强大。

命令替换的语法有两种:

`命令语句`
$(命令语句)

注意上面第一种那个是反引号,不是引号。

命令替换是指把命令运行的结果替换到你需要进行“命令替换”的位置。

比如

ls -l /

会把该命令的结果直接“复制”“粘贴”到当前位置。

例:For each循环如果跟命令替换相结合

#!/bin/sh
for i in `ls *.c`
do
	mv $i sk_$i
done

计数型for循环

例:20次的for循环

#!/bin/sh
for ((i=0;i<20;i=i+1)); do
	echo $i
done

while循环

while循环语法比较简单。

while <condition>; do
 	<stmts>
done

例:输入Ctrl+D(end of file)退出

#!/bin/sh
while read string
do
echo "Your string is $string"
done
echo 'Thank you. Bye!'

直到型循环:until

直到型循环与while的区别是,当condition为真时,while循环一直做;当conditon为假时,until循环才一直做,为真时,循环结束。

until <condition>; do
 	<stmts>
done

break

break的使用跟其他编程语言无区别。

例:使用break,将上一个shell脚本改写。”:“代表空语句,以便写一个死循环。

#!/bin/sh
while :; do
	read string
	if [ $? != 0 ]; then
		echo 'Thank you. Bye!'
		break
	fi
	echo "Your string is $string"
done

continue

continue的使用跟其他编程语言无区别。

例:

#!/bin/sh
for ((i=0 ;i<20 ;i=$i+1))
do
	echo $i
	if [ $i -lt 10 ] ; then
		continue
	fi
	echo 'This is a two digital numeric'
done

if then else

shell中的if分支语句只有两点比较特殊:1.代码块的结尾使用”fi“——就是if倒着写;2.else if简写为elif。

例:

#!/bin/sh
# Usage: ./grade.sh grade
# grade should be between 0 and 100, including 0 and 100
if [ $1 -lt '0' ] ; then
	echo "Error: invalid grade"
elif [ $1 -lt '60' ] ; then
	echo 'no pass'
elif [ $1 -lt '70' ] ; then
	echo "pass"
elif [ $1 -lt '80' ] ; then
	echo 'good'
elif [ $1 -le '100' ] ; then
	echo 'excellent'
else
	echo'Error: invalid grade'
fi

case

case语句的语法有些繁复,但通过下面例子就可以掌握。

#!/bin/sh
echo -n "Do you want to continue?"
read yesno
case $yesno in
y | Y | Yes | yes)
	echo "We will continue"
	;;
n | N | no | NO)
	echo "Game over"
	;;
*)
	echo "Incorrect input"
	exit 1
	;;
esac

算数计算

数学运算符只是一个一般的字符而已,因此无法计算1+2。

算数计算可以有两种方法。

例:计算4+5的两种方法,后者更适合写到赋值语句中。

expr 4 + 5 
$((4+5))
NUM=$((4+5))

自定义函数

语法:

function_name(){
	#CMD list
}

例:无参和无返回值的函数

#!/bin/sh
message(){
	echo "message" 
}

for((i=0;i<3;i++))
do
	message
done

例:有参数和返回值的函数

#!/bin/sh
sum() {
	x=$1
	y=$2
	result=$((x+y))
	return $result
}

echo "Enter two numbers" 
read num1 num2
sum $num1 $num2
echo "The sum is $?"

在shell中,函数本质上就是一个命令。因此,参数即命令的参数:$1, 2,...返回值即2, ... 返回值即2,...? 也是由于这个原因,返回只能是0~255之间的数,如果超过这个返回,会取其模256后的值。

函数内的变量都是全局变量,如果想声明为函数内的局部变量需使用typeset关键字。

#!/bin/sh
easy(){
	typeset a
	a=$(($1+$2))
	b=$(($1+$2))
	echo "easy a is $a"
	echo "easy b is $b"
}

a=1
b=2
easy $a $b
echo "global a is $a"
echo "global b is $b"

文件引用

“.”用来引用其他文件。

例:

sum.sh

#!/bin/sh
sum(){
	return $(($1+$2))
}

usesum.sh

#!/bin/sh
. ./sum.sh
sum 1 2
echo $?

如有侵权,请立即与博主联系,会尽快删除本文

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Env1sage

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值