Shell编程
Posted 苏幕遮_凌枫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Shell编程相关的知识,希望对你有一定的参考价值。
shell用于解释执行用户命令,用法分为Interactive和Batch.
shell script中有多条shell命令,批量执行,同时加入编程语言中常用的变量,
流程控制语句,这样看起来写shell script就像编程,但是本质上只是一系列shell命令的集合.
常见的shell版本
- sh linux unix系统都有
- tcsh FreeBSD macOS x使用
- bash linux发行版标配
查看用户对应的shell类型
[email protected]:~$ more /etc/passwd|grep mark
mark:x:1000:1000:mark,,,:/home/mark:/bin/bash
# mark用户对应的shell是bash
输入命令后,一般shell会fork开启新进程exec该命令,shell的内建命令是例外.
执行builtin命令相当于调用shell进程中的一个函数,不fork.
内建命令不fork但是有Exit Status,0表示成功,非0表示失败,状态码可以用$?
读取.
常见内建命令:
export for while shift if eval [
which命令查不到程序文件所在位置的命令都是内建命令,这些命令没有man手册.
要在man手册中查这些命令
man bash-builtins
执行脚本
#! /bin/bash
#在shell中#是注释标识符
#在第一行的 #! 是shebang符号 表示以./xx.sh执行时由那个文件来执行
echo hello shell
###########################编写脚本
$ chmod a+x test.sh # 添加执行权限
$ ./test.sh # 由/bin/bash执行
hello shell # echo的输出结果
执行脚本的方式
- bash/sh test.sh
- source test.sh 交互式执行 不fork
. test.sh
这个.
和source一样是内建命令,同样也是Interactive执行
shell脚本一行可以有多个命令,用
;
分隔.命令使用()以fork方式执行,如(cd ..;ls -l ),如果没有()会以interactive执行
变量
变量名必须=大写字母+下划线
环境变量
可以从father进程传给son进程
环境变量可以从当前shell进程传给fork出来的进程
printenv
查看当前shell进程中的环境变量
本地变量
只处在与当前shell进程,set
显示当前shell进程中定义的所有变量(local+env)和函数
环境变量在任何进程中可见
本地变量只在shell进程中可见
在shell中环境变量与本地变量定义及使用方法相似.
定义或赋值一个变量
VAR_NAME=value # 这里的=号两边是绝对不能有空格的
本地变量导出为环境变量
export VAR_NAME
定义一个本地变量同时导出
export PATH=/usr/local
删除变量(local+env)
unset VAR_NAME
取变量的值
${VAR_NAME} #推荐使用这种
$VAR_NAME
# shell中的变量无需指定类型 本质上都是字符串
# 变量无需先定义后使用 对未定义的变量取值返回空字符串
代换
Wildcard
文件名代换(Globbing)
Wildcard(通配符),和正则规则是一样的
* ? [ ]
$()
命令代换,先执行命令,再将结果赋值给变量,将输出结果代换到命令中
` 或$()
$ DATE=`date`
$ echo $DATE
$ DATE=$(date) # 推荐使用这种方式
$ echo $DATE
$[]
算术代换: $(()) 或 $[]
~$ echo $[8#10+1] # 10进制 8+1
9
~$ echo $[8#16+1] # 16进制8+1
15
单引号与双引号
- 单引号,保存引号中内容的字面值,包括
和
- 单引号内不得再出现单引号
- 单引号必须配对
- 双引号防止通配符扩展,允许变量扩展
- 双引号内的变量还是变量,而不是像单引号一样是字面值
~$ DT=$(date)
~$ echo "${DT}"
2019年 01月 25日 星期五 22:07:57 CST
~$ echo ‘${DT}‘
${DT}
所有的取值操作都应该放在双引号中,like "${VAR}"
语法
条件测试
test
[
测试结果为真 exit status=0 else 1
~$ var=2
~$ test ${var} -lt 1
~$ echo $?
1
~$ test ${var} -lt 8
~$ echo $?
0
~$ test ${var} -lte 2 # 用不了
bash: test: -lte: 需要二元表达式
~$ [ ${var} -lt 2 ] # [空格参数1空格参数2空格参数3空格参数4 参数4是]
~$ echo $?
1
常用测试命令:
[ -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(大于等于)之中的一个
测试条件之间的 或 与 非
带与、或、非的测试命令
[ ! EXPR ] EXPR可以是上表中的任意一种测试条件,!表示逻辑反
[ EXPR1 -a EXPR2 ] EXPR1和EXPR2可以是上表中的任意一种测试条件,-a表示逻辑与
[ EXPR1 -o EXPR2 ] EXPR1和EXPR2可以是上表中的任意一种测试条件,-o表示逻辑或
if/then/elif/else/fi
#! /bin/bash
echo "Please give me a number >"
read VR # 读取用户输入 赋值给变量VR
# 变量VR的值与100比较
if [ "${VR}" -lt 100 ]; then
echo "${VR} < 100"
else
echo "${VR} > 100"
fi
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 # : 是特殊命令 返回值为0 永远为真
echo "are you stupid?"
read Y_N
if [ "${Y_N}" = "yes" ]; then
echo "hi,stupid."
elif [ "${Y_N}" = "no" ]; then
echo "oh,my deer."
else
echo "stupid is as stupid does."
exit 1
fi
exit 0
shell中的&&和||和java中的相似
-a
shell中的and -o
shell中的or
使用起来和&&和||等价
但是
&&和||还有一种用法,就是由前面部分的测试结果决定后半部分的语句是否执行,相当于
&&
if ...then...
||
if not ...then...
test "$(whoami)" != "root" && echo "need root privliges"; exit 1
test "$(whoami)" == "root" || echo "need root privliges"; exit 1
case/esac
Shell的case可以匹配字符串和Wildcard
每个匹配分支可以有若干条命令,末尾必须以;;
结束
执行时找到第一个匹配的分支并执行相应的命令,然后直接跳到esac之后,不需要像C语言一样用break跳出
echo "something you like..."
read n
case "${n}" in
bee)
echo "busy as a bee";;
dog)
echo "日了狗了";;
cat)
echo "找只猫来";;
*)
echo "不懂你输入的是什么"
exit 1;;
esac
exit 0
shell中获取命令行参数${1}
取第一个参数,后面的用2,3,4,5取
case "${1}" in
start)
echo "服务器启动成功.";;
stop)
echo "成功杀掉服务器进程";;
restart)
echo "服务器重启好了.";;
*)
echo "无效的命令行参数."
exit 1
esac
exit 0
for/do/done
oh,这不是python的for in循环么?
for name in ruhua canglaoshi xiaozhe mihane toshiyuki; do
if [ "${name}" == "ruhua" ]; then
echo "I do not like ${name}."
else
echo "I like ${name}"
fi
done
#-------------------
$ ./xm.sh
I do not like ruhua.
I like canglaoshi
I like xiaozhe
I like mihane
I like toshiyuki
# 列出 /home/mark下的所以file
for fn in "$(ls /home/mark)"; do
echo "${fn}"
done
# 列出当前路径下所有以x开头的文件名
for fn in x*; do
echo "${fn}"
done
while/do/done
c=1
while [ "${c}" -lt 3 ]; do
echo "hi,golang!"
c=$[${c}+1]
done
函数
函数声明
Shell函数,没有返回值和参数列表
先定义后调用,调用方式是直接使用函数名
注意函数体的左花括号‘{‘和后面的命令之间必须有空格或换行,如果将最后一条命令和右花括号‘}‘写在同一行,命令末尾必须有;号。
# 定义函数
fun(){ echo "Function foo is called";} # {空格echo...;}
foo(){
echo "function in shell..."
echo "$(pwd)"
}
# 调用函数
fun
foo
函数参数
Shell函数没有参数列表并不表示不能传参数,事实上,函数就像是迷你脚本,调用函数时可以传任意个参数,在函数内同样是用$0、$1、$2
等变量来提取参数,函数中的位置参数相当于函数的局部变量,改变这些变量并不会影响函数外面的$0、$1、$2
等变量。函数中可以用return命令返回,如果return后面跟一个数字则表示函数的Exit Status。
fun1(){
echo "${0}" # 执行脚本的$0参数
echo "${1}" # x
echo "${2}" # y
echo "${@}" # x,y
return 0
}
fun1 x y
# -----------------
$ ./xm.sh
./xm.sh
x
y
x y
练习
# 判断是否是一个目录
is_directory(){
# 接收位置参数1
DIR_NAME="${1}"
# 是目录 exit status 为0 else 1
if [ -d "${DIR_NAME}" ]; then
return 0
else
return 1
fi
}
# 根据命令行参数创建目录
for DIR in "${@}"; do
if is_directory "${DIR}"; then : # 如果目录已存在 什么也不做 :
else
echo "${DIR} doest‘t exist. Creating it now."
mkdir "${DIR}" > /dev/null 2>&1 # 创建目录 标准输出 标准错误都重定向到/dev/null 不显示
if [ $? -ne 0 ]; then # 如果创建目录失败
echo "Cannot create directory ${DIR}"
exit 1
fi
fi
done
# for dir in abc def;do
# rm -rf ${dir}
# done
位置参数和特殊变量
$0 相当于C语言main函数的argv[0]
$1、$2... 这些称为位置参数(Positional Parameter),相当于C语言main函数的argv[1]、argv[2]...
$# 相当于C语言main函数的argc - 1,注意这里的#后面不表示注释
[email protected] 表示参数列表"$1" "$2" ...,例如可以用在for循环中的in后面。
$* 表示参数列表"$1" "$2" ...,同上
$? 上一条命令的Exit Status
$$ 当前进程号
位置参数可以用shift命令左移。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1、$2、$3丢弃,$0不移动。不带参数的shift命令相当于shift 1。例如:
#! /bin/sh
echo "The program $0 is now running"
echo "The first parameter is $1"
echo "The second parameter is $2"
echo "The parameter list is [email protected]"
shift
echo "The first parameter is $1"
echo "The second parameter is $2"
echo "The parameter list is [email protected]"
其他
输入输出
read V
echo [option] string
-e 解析转义字符
-n 不回车换行。默认情况echo回显的内容后面跟一个回车换行。
文件重定向
cmd > file 把标准输出重定向到新文件中
cmd >> file 追加
cmd > file 2>&1 标准出错也重定向到1所指向的file里
cmd >> file 2>&1
cmd < file1 > file2 输入输出都定向到文件里
cmd < &fd 把文件描述符fd作为标准输入
cmd > &fd 把文件描述符fd作为标准输出
cmd < &- 关闭标准输入
tee
将命令的结果输入到标准输出的同时保存到文件
tee -a x.txt
-a参数表示追加
cd /home/mark
ls -alh | tee a.txt
find
grep
awk 处理行和列
sed 处理行
以上是关于Shell编程的主要内容,如果未能解决你的问题,请参考以下文章