shell编程
在Linux环境运行的系统,日常开发中我们要经常使用shell,除了常用的命令还要掌握shell编程。
构建基本脚本
创建shell脚本文件
- shell文件的第一行指定要使用的shell,我们这里假定都是bash shell,如:
#!/bin/bash
- 运行
shell会通过PATH 环境变量来查找命令。
- 将shell脚本文件所处的目录添加到PATH环境变量中
- 用绝对或相对文件路径来引用shell脚本文件
$ chmod u+x scritpname
使用变量 定义 赋值 获取
- 使用等号将值赋给用户变量。在变量、等号和值之间不能出现空格。
var3=testing 12
var1=10
var2=-57
var4="still more testing"
- 通过美元符引用获取变量。
var1="abc"
echo $var1
- 从命令输出中提取信息,并将其赋给变量
- 反引号字符(
cmd
) - $()格式
#!/bin/bash
for file in $(ls)
#for file in `ls`
do
echo $file
done
数组变量
$ mytest=(one two three four five)
# 直接获取变量,只显示第一个
$ echo $mytest
one
- 索引引用变量
$ echo ${mytest[2]}
- 整个数组变量,可用星号作为通配符放在索引值的位置
$ echo ${mytest[*]}
one two three four five
重定向输入输出
- 将命令的输出发送到一个文件中。bash shell用大于号(>)来完成这项功能。
$ echo 111 > a.log
- 输入重定向将文件的内容重定向到命令,而非将命令的输出重定向到文件。输入重定向符号是小于号(<)。
$ wc < a.log
管道
进程间通信的一种方式。管道符 |
$ sort a.log | uniq
数学运算
将一个数学运算结果赋给某个变量时,可以用美元符和方括号($[ operation ])将数学表达式围起来。
bc计算浮点数。
#!/bin/bash
var1=100
var2=50
var3=45
var4=$[$var1 * ($var2 - $var3)]
echo The final result is $var4
退出脚本
shell中运行的每个命令都使用退出状态码(exit status)告诉shell它已经运行完毕。
专门的变量 $? 来保存上个已执行命令的退出状态码。0 一般指正常结束。
结构化命令
if then
使用if-then语句 嵌套if语句 test命令 复合条件测试 使用双方括号和双括号 case命令
if 后边的命令返回状态码是0,则执行then
if command
then
commands
fi
或者
if command; then
commands
fi
if-then-else
if command
then
commands
else
commands
fi
或者
if command1
then
commands
elif command2
then
more commands
fi
test命令
if test condition
then
commands
fi
或者使用 [ condition ]
注意,第一个方括号之后和第二个方括号之前必须加上一个空格,否则就会报错。
if [ condition ]
then
commands
fi
数值比较
-eq、-ge、-gt、-le、-lt、-ne
if [ $value -gt 5 ]
then
echo "ok"
fi
字符串比较
比较 | 描述 |
---|---|
str1 = str2 | 相同 |
str1 != str2 | 不同 |
-n str1 | 检查str1的长度是否非0 |
-z str1 | 检查str1的长度是否为0 |
if [ -z $val2 ]
then
echo "The string '$val2' is empty"
else
echo "The string '$val2' is not empty"
fi
文件比较
比较 | 描述 |
---|---|
-d file | file是否存在且是一个目录 |
-e file | file是否存在 |
-f file | file是否存在且是一个文件 |
-r/-w/-x file | 可读可写可执行 拆开用 |
if [ -d $jump_directory ]
then
echo "The $jump_directory directory exists"
cd $jump_directory
ls
else
echo "The $jump_directory directory does not exist"
fi
复合条件测试
if-then语句允许你使用布尔逻辑来组合测试。有两种布尔运算符可用:
- [ condition1 ] && [ condition2 ]
- [ condition1 ] || [ condition2 ]
if [ -d $HOME ] && [ -w $HOME/testing ]
then
echo "The file exists and you can write to it"
else
echo "I cannot write to the file"
fi
双括号
双括号命令允许在比较过程中使用高级数学表达式。
(( expression ))
双方括号
双方括号命令提供了针对字符串比较的高级特性。
[[ expression ]]
case
#!/bin/bash
# using the case command #
case $USER in
rich | barbara)
echo "Welcome, $USER"
echo "Please enjoy your visit";;
testing)
echo "Special testing account";;
jessica)
echo "Do not forget to log off when you're done";;
*)
echo "Sorry, you are not allowed here";;
esac
循环处理命令
for命令
读列表中的值
for var in list
do
commands
done
for var in Alabama Alaska Arizona Arkansas California Colorado
do
echo "The next state is $var"
done
读列表中的复杂值
如果有单引号等文本,需要转义处理
从变量读取列表
#!/bin/bash 13
list="Alabama Alaska Arizona Arkansas Colorado"
list=$list" Connecticut"
for state in $list
do
echo "Have you ever visited $state?"
done
从命令读取值
for中使用一个命令的输出。
#!/bin/bash
# reading values from a file
file="states"
for state in $(cat $file)
do
echo "Visit beautiful $state"
done
通配符读目录
for file in /home/rich/test/*
do
if [ -d "$file" ]
then
echo "$file is a directory"
elif [ -f "$file" ]
then
echo "$file is a file"
fi
done
C语言风格for命令
#!/bin/bash
# testing the C-style for loop
for (( i=1; i <= 10; i++ ))
do
echo "The next number is $i"
done
while循环
#!/bin/bash
var1=10
while [ $var1 -gt 0 ]
do
echo $var1
var1=$[ $var1 - 1 ]
done
嵌套循环
#!/bin/bash
for (( a = 1; a <= 3; a++ ))
do
echo "Starting loop $a:"
for (( b = 1; b <= 3; b++ ))
do
echo "Inside loop: $b"
done
done
控制循环
-
break
break 遇到嵌套,只能退出当前循环
-
break n
n指定了要跳出的循环层级。默认情况下,n为1,表明跳出的是当前的循环。如果n设为2,break命令就会停止下一级的外部循环。
-
continue
跳过该次循环
处理用户输入
$ ./addem 10 30
读参数
读取参数
位置参数变量是标准的数字: $0是程序脚本的名,$1是第一个参数,$2是第二个参数,依次类推,直到第九个参数$9。
工具脚本中经常使用。
脚本名
$0 表示脚本名字
basename可以去掉路径
测试参数存在与否
当脚本认为参数变量中会有数据而实际上并没有时,脚本很有可能会产生错误消息。这种写脚本的方法并不可取。在使用参数前一定要检查其中是否存在数据。
# 使用参数前 先检查是否存在
if [ -n "$1" ]
then
echo Hello $1, glad to meet you.
else
echo "Sorry, you did not identify yourself. "
fi
特殊参数
-
获取变量个数
特殊变量 $# 含有脚本运行时携带的命令行参数的个数。可以在脚本中任何地方使用这个特殊变量,就跟普通变量一样。
-
抓取所有参数
-
$*
$* 变量会将命令行上提供的所有参数当作一个单词保存。
-
$@
$@变量会将命令行上提供的所有参数当作同一字符串中的多个独立的单词。这样你就能够遍历所有的参数值,得到每个参数。这是 for遍历命令行参数的一个绝妙方法。
-
用户输入
基本读取
read命令从标准输入(键盘)或另一个文件描述符中接受输入。在收到输入后,read命令会将数据放进一个变量。
#!/bin/bash
# testing the read command
echo -n "Enter your name: "
# read把数据放到了name变量
read name
echo "Hello $name, welcome to my program. "
带超时读取
对于用户长时间不输入,可设置超时读取
#!/bin/bash
# timing the data entry
#
if read -t 5 -p "Please enter your name: " name
then
echo "Hello $name, welcome to my script"
else
echo
echo "Sorry, too slow! "
fi
隐藏方式读取
对于不希望展示在shell控制台上的输入,可以这样。
-s选项可以避免在read命令中输入的数据出现在显示器上(实际上,数据会被显示,只是read命令会将文本颜色设成跟背景色一样)。
read -s -p "Enter your password: " pass
创建函数
创建函数
脚本中定义的每个函数都必须有一个唯一的名称。
function name {
commands
}
使用函数
要在脚本中使用函数,在行中指定函数名就可以。
- 脚本编写,先定义函数,再使用
- 注意函数名唯一,编写时小心覆盖
#!/bin/bash
function func1 {
echo "This is an example of a function"
}
func1
echo "Now this is the end of the script"
返回值
shell会把函数当作一个小型脚本,运行结束时会返回一个退出状态码。
-
默认状态码
取自函数最后一个命令的运行状态码
-
使用return命令
return的值作为函数状态码。注意:
- 记住,函数一结束就取返回值,$? 是上一个命令的状态码
- 记住,退出状态码必须是0~255。return的值不要超过。
-
使用函数输出
#!/bin/bash
# using the echo to return a value
function dbl {
read -p "Enter a value: " value
echo $[ $value * 2 ]
}
result=$(dbl)
echo "The new value is $result"
使用echo命令,result=$(dbl),把函数返回值输出。
函数中使用变量
向函数传递参数
函数可以使用标准的参数环境变量来表示命令行上传给函数的参数。例如,函数名会在$0 变量中定义,函数命令行上的任何参数都会通过$1、$2等定义。也可以用特殊变量$#来判断传给函数的参数数目。
在脚本中指定函数时,必须将参数和函数放在同一行。
#!/bin/bash
# passing parameters to a function
function addem {
if [ $# -eq 0 ] || [ $# -gt 2 ]
then
echo -1
elif [ $# -eq 1 ]
then
echo $[ $1 + $1 ]
else
echo $[ $1 + $2 ]
fi
}
echo -n "Adding 10 and 15: "
value=$(addem 10 15)
echo $value
函数中处理变量
-
全局变量
注意,函数内外使用共享的全局变量的风险。
-
局部变量
无需在函数中使用全局变量,函数内部使用的任何变量都可以被声明成局部变量。只要在变量声明的前面加上local关键字就可以了。
如果脚本中在该函数之外有同样名字的变量,那么shell将会保持这两个变量的值是分离的。
#!/bin/bash
# demonstrating the local keyword
function func1 {
local temp=$[ $value + 5 ]
result=$[ $temp * 2 ]
}
其他awk、sed
这些命令比较独立于shell编程,用法会单独再开篇。
参考
- 《Linux命令行与shell脚本编程大全》