1. 首页
  2. 技术小贴

Shell脚本编程

简介

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

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

技术小贴
技术小贴

业界所说的 shell 通常都是指 shell 脚本,Shell 脚本(shell script),是一种为 shell 编写的脚本程序。

shell种类很多,包括sh、bash、xsh、ksh,其中bash是大多数Linux系统默认的shell,下面也将以bash为例。

简单的shell脚本如下:

#!/bin/bash
echo "hello world!"

运行shell脚本的两种方式:

  1. 作为可执行程序
    chmod +x ./test.sh	#赋予权限
    ./test.sh   #执行脚本
    #注意,一定要写成 ./test.sh,而不是 test.sh,即告诉编译器在当前目录执行test.sh!
  2. 作为解释器参数
    /bin/sh test.sh

    这种方式运行的脚本,不需要在第一行指定解释器信息,写了也没用。

变量

命名

变量命名时需要注意变量名和等号之间不能有空格,这点和其它编程语言都不一样。

显式赋值:

var=123

隐式赋值:

for file in `ls /etc`

for file in $(ls /etc)

使用

使用一个定义过的变量,只要在变量名前面加美元符号即可!

var=123
echo $var
echo ${var}

变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,比如下面这种情况:

for skill in Ada Coffe Action Java; do
    echo "I am good at ${skill}Script"
done

如果不给skill变量加花括号,写成echo "I am good at $skillScript",解释器就会把$skillScript当成一个变量(其值为空),代码执行结果就不是我们期望的样子了。

推荐给所有变量加上花括号,这是个好的编程习惯。

readonly

使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。

#!/bin/bash
var=123
echo ${var}
readonly var
var=456
echo ${var}

删除

使用 unset 命令可以删除变量。

变量被删除后不能再次使用。unset 命令不能删除只读变量。

类型

shell存在三种变量类型:

  1. 局部变量:局部变量在脚本或命令中定义,仅在当前shell实例中有效。
  2. 环境变量:所有的程序,包括shell启动的程序,都能访问环境变量。
  3. shell变量:由shell程序设置的特殊变量,包括局部变量和全局变量。

字符串

定义

字符串可以用单引号,也可以用双引号,也可以不用引号。

  • 单引号里的任何字符都回原样输出;单引号字符串中的单引号必须成对出现。
  • 双引号里可以出现变量和转义字符。
#!/bin/bash
name='bob'
str='this is $name'
echo ${str}
str="this is $name"
echo ${str}

s输出结果为:

➜  shell ./3_str.sh
this is $name
this is bob

拼接

name="zhangzhen"
#使用双引号拼接
a="hello,"$name""
b="hello,${name}"
#使用单引号拼接
c='hello,'$name''
d='hello,${name}'
echo $a $b $c $d

输出结果:

hello,zhangzhen hello,zhangzhen hello,zhangzhen hello,${name}

长度

str="hi,jerk morning!!"
echo ${#str}	  #输出17
expr length "$str" #输出17,因为str字符串中含有空格,因此需要添加双引号

截取

shell截取字符串通常有两种方式:从指定位置开始截取和从指定字符(子字符串)开始截取。

从指定位置开始截取:

#!/bin/bash
str="ilovefish"
echo ${str:1:4}  #love
echo ${str:4}    #efish
echo ${str:0-4:3}    #fis
echo ${str:0-4}  #fish

str:1:4表示从第二个字符开始,截取四个字符,故输出love

str:4表示从第四个字符开始到最后。

str:0-4:3表示从倒数第4个开始,往后截取3个字符。

str:0-4表示从倒数第4个开始到最后。

从指定字符开始截取:

var="http://www.baidu.com/1.html"
echo ${var#*//}	    #www.baidu.com/1.html
echo ${var##*/}	    #1.html
echo ${var%/*}      #http://www.baidu.com
echo ${var%%/*}     #http:

# 号是运算符,*// 表示从左边开始删除第一个 // 号及左边的所有字符。

##*/ 表示从左边开始删除最后(最右边)一个 / 号及左边的所有字符。

%/* 表示从右边开始,删除第一个/ 号及右边的字符。

%%/* 表示从右边开始,删除最后(最左边)一个 / 号及右边的字符。

借助shell命令做字符串截取:

var="http://www.baidu.com/123.com"
echo $var | cut -c1-4   #http
echo $var | cut -c8-    #www.baidu.com/123.com
echo $var | cut -d ":" -f 1 #http

查找

str="ilove china"
echo `expr index "$str" io`

查找字符 io 的位置(哪个字母先出现就计算哪个)

数组

bash支持一维数组(不支持多维数组),并且没有限定数组的大小。

定义

数组名=(值1 值2 ....)
arr=(1 2 3 4)
arr=(
1
2
3
4
)
arr[0]=1
arr[1]=2
...

读取

echo ${arr[0]}

长度

length=${#arr[@]}        #获取元素个数
length=${#arr_name[*]}   #获取元素个数
length=${#arr[n]}        #获取单个元素的长度
#!/bin/bash

arr=(1 2 3 "hello")
echo ${arr[3]} #hello
length1=${#arr[*]}
length2=${#arr[@]}
length3=${#arr[3]}
echo ${length1} $length2 $length3 # 4 4 5

注释

#

单行注释。

EOF

多行注释。

:<<EOF
注释内容...
...
EOF

注意,注释符号EOF可以使用其他符号,如'!


参数

传递参数

我们可以在执行 Shell 脚本时,向脚本传递参数,脚本内获取参数的格式为:$nn 代表执行脚本的第n个参数。

如下实例:

#!/bin/bash

echo "Shell传参实例!"
echo "执行的文件名:$0"
echo "第一个参数:$1"
echo "第二个参数:$2"

执行结果如下:

➜  shell ./5_para.sh 1 2
Shell传参实例!
执行的文件名:./5_para.sh
第一个参数:1
第二个参

参数处理

以下,可以使用一些特殊字符来处理参数:

参数处理 说明
$# 传递到脚本的参数个数
$* 以一个单字符串显示所有向脚本传递的参数。 如”$*”用「”」括起来的情况、以”$1 $2 … $n”的形式输出所有参数。
$$ 脚本运行的当前进程ID号
$! 后台运行的最后一个进程的ID号
$@ 与$*相同,但是使用时加引号,并在引号中返回每个参数。 如”$@”用「”」括起来的情况、以”$1” “$2” … “$n” 的形式输出所有参数。
$- 显示Shell使用的当前选项,与set命令功能相同。
$? 显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。
#!/bin/bash

echo "Shell传参示例!"
echo "第一个参数为:$1"
echo "参数个数为:$#"
echo "参数为:$*"

执行结果为:

➜  shell ./5_para_1.sh 1 2 3 4 5
Shell传参示例!
第一个参数为:1
参数个数为:5
参数为:1 2 3 4 5

$* 与 $@ 区别:

  • 相同点:都是引用所有参数。
  • 不同点:只有在双引号中体现出来。假设在脚本运行时写了三个参数 1、2、3,,则 “ * “ 等价于 “1 2 3”(传递了一个参数),而 “@” 等价于 “1” “2” “3”(传递了三个参数)。
#!/bin/bash

echo "-- \$*演示 --"
for i in "$*"; do
        echo $i
done
echo "-- \$@演示 --"
for i in "$@"; do
        echo $i
done

执行结果如下:

➜  shell ./5_para_2.sh 1 2 3
-- $*演示 --
1 2 3
-- $@演示 --
1
2
3

中括号

Shell 里面的中括号(包括单中括号与双中括号)可用于一些条件的测试:

  • 算术比较, 比如一个变量是否为0, [ $var -eq 0 ]
  • 文件属性测试,比如一个文件是否存在,[ -e $var ], 是否是目录,[ -d $var ]
  • 字符串比较, 比如两个字符串是否相同, [[ $var1 = $var2 ]]

算术比较

对变量或值进行算术条件判断:

[ $var -eq 0 ]  # 当 $var 等于 0 时,返回真
[ $var -ne 0 ]  # 当 $var 不等于 0 时,返回真

需要注意的是 [ 与 ] 与操作数之间一定要有一个空格,否则会报错。比如下面这样就会报错:

[$var -eq 0 ]  或 [ $var -ne 0]
操作符 意义
-gt 大于
-lt 小于
-ge 大于或等于
-le 小于或等于

可以通过 -a (and) 或 -o (or) 结合多个条件进行测试:

[ $var1 -ne 0 -a $var2 -gt 2 ]  # 使用逻辑与 -a
[ $var1 -ne 0 -o $var2 -gt 2 ]  # 使用逻辑或 -o

文件属性测试

使用不同的条件标志测试不同的文件系统属性。

操作符 意义
[ -f $file_var ] 变量 $file_var 是一个正常的文件路径或文件名 (file),则返回真
[ -x $var ] 变量 $var 包含的文件可执行 (execute),则返回真
[ -d $var ] 变量 $var 包含的文件是目录 (directory),则返回真
[ -e $var ] 变量 $var 包含的文件存在 (exist),则返回真
[ -c $var ] 变量 $var 包含的文件是一个字符设备文件的路径 (character),则返回真
[ -b $var ] 变量 $var 包含的文件是一个块设备文件的路径 (block),则返回真
[ -w $var ] 变量 $var 包含的文件可写(write),则返回真
[ -r $var ] 变量 $var 包含的文件可读 (read),则返回真
[ -L $var ] 变量 $var 包含是一个符号链接 (link),则返回真

使用方法如下:

fpath="/etc/passwd"
if [ -e $fpath ]; then
  echo File exits;
else
  echo Does not exit;
fi

字符串比较

在进行字符串比较时,最好使用双中括号 [[ ]]. 因为单中括号可能会导致一些错误,因此最好避开它们。

检查两个字符串是否相同:

[[ $str1 = $str2 ]]

当 str1等于str1等于str2 时,返回真。也就是说,str1 和 str2 包含的文本是一样的。其中的单等于号也可以写成双等于号,也就是说,上面的字符串比较等效于 [[ $str1 == $str2 ]]。

注意 = 前后有一个空格,如果忘记加空格, 就变成了赋值语句,而非比较关系了。

字符串的其他比较情况:

操作符 意义
[[ $str1 != $str2 ]] 如果 str1 与 str2 不相同,则返回真
[[ -z $str1 ]] 如果 str1 是空字符串,则返回真
[[ -n $str1 ]] 如果 str1 是非空字符串,则返回真

使用逻辑运算符 && 和 || 可以轻松地将多个条件组合起来, 比如:

str1="Not empty"
str2=""
if [[ -n $str1 ]] && [[ -z $str2 ]];
then
  echo str1 is nonempty and str2 is empty string.
fi

test 命令也可以从来执行条件检测,用 test 可以避免使用过多的括号,[] 中的测试条件同样可以通过 test 来完成。

if [ $var -eq 0 ]; then echo "True"; fi

等价于:

if test $var -eq 0; then echo "True"; fi

原创文章,作者:小嵘源码,如若转载,请注明出处:https://www.lcpttec.com/shelljb/

联系我们

176-888-72082

在线咨询:点击这里给我发消息

邮件:2668888288@qq.com

工作时间:周一至周五,9:00-18:00,节假日休息

QR code