Bash 之 Shell编程的重要性
Posted 阿文Linux
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bash 之 Shell编程的重要性相关的知识,希望对你有一定的参考价值。
温馨提示:本文大量表格,代码不适合在移动端阅读
Shell作为连接用户与硬件和kernel交互的一种方式,从unix操作系统设计之初就存在,所以,它是unix系统中除了C外最核心的程序,同时也是最古老的脚本语言。
不过,这里要澄清一个概念,shell和bash是两个不同的东西,shell是一些常见工具的组合,如ls,ps,wc,tr,cat....而Bash是单独的一个独立程序。
Bsh,Unix shell的一种,在1987年由布莱恩·福克斯为了GNU计划而编写。原先是计划用在GNU操作系统上,但能运行于大多数类Unix系统的操作系统之上,包括Linux与Mac OS X v10.4都将它作为默认shell。bash是 Bourne shell的后继兼容版本与开放源代码版本,它的名称来自Bourne shell(sh)的一个双关语:Bourne-Again SHell。
Bash的优势在于方便管理员操作,更快更好的保持系统的稳定和高效。它的优点包括:
封装了很多与OS,Kernel交互的接口,方便用户使用和管理类UNIX操作系统,包括如Linux和Mac基于文本的命令解释器
人机交互非常友好易懂,不仅能够快速执行用户输入的命令,也支持从脚本文件中读取完整的程序代码
它支持文件名替换(通配符匹配)、管道、命令替换、变量,以及条件判断和循环遍历的结构控制语句,后台运行,进程信号等程序功能,支持扩展
作为接近底层的胶水语言,短小精悍却可以内嵌任何其它程序,或者管道输出增强威力(pipe是个伟大的发明,详见《UNIX传奇》)
高效简单,安全可靠,历经30+年的考验,无内存泄露的风险
当然,Bash也有一些不足的地方,需要大家注意区分应用场合:
因为缺少严谨的数据类型和数据结构,不适合高精度运算的场合,如科研,火星探索...
基于文本的解释型语言,不适合于对性能有极高要求的场合,如多进程,线程,程...设计用于机器系统管理的脚本语言,虽然有Curl
这样的客户端神器,但仍然不适合于复杂网络操作,如编写网络服务,支持大并发访问...
多字节编码支持不够,也不适用于数据库的操作,图形界面编写,- 基于互联网的web应用开发...
因此,请不要把它和C/Python/Java这样的大而全的程序做对比,那是两个不同领域的工具
UNIX的三大设计哲学Unix程序设计哲学-KISS
Write programs that do one thing and do it well.
轻巧极致就是最好,方便调试和替换
Write programs to work together.
编写简单的组件,用整洁的接口让它们互联。
Write programs to handle text streams, because that is a universal interface.
数据流尽可能是文本的,便于标准的工具阅读和过滤
Bash语法讲解变量操作
变量声明(非必须操作)
1. 首个字符必须为字母(a-z,A-Z)
2. 中间不能有空格,可以使用下划线_
3. 不能使用标点符号
4. 不能使用bash里的关键字(help查看保留关键字)
5. 在赋值等号 ”=” 的两边没有空格,任何空格将导致错误
6. 单双引号,注意具体场合
● declare -a 声明一个数组
● declare -i 声明一个整型
● declare -x 声明为环境变量
● declare -r 声明一个只读变量
可变变量,如:
a=1 b="hello"
只读变量
readonly a="nochange"
内置变量
变量名
|
含义
|
\$0
|
脚本名字
|
\$1~\$9
|
带入参数的位置
|
\${10}
|
第10个位置的参数
|
\$#
|
带入参数的个数
|
\$*
|
所有的位置参数(作为单个字符串) *
|
\$@
|
所有的位置参数(每个都作为独立的字符串)
|
\$?
|
返回值
|
\$\$
|
脚本的进程ID(PID)
|
\$_
|
之前命令的最后一个参数
|
\$!
|
运行在后台的最后一个作业的进程ID(PID)
|
变量初始化
表达式
|
含义
|
\${var}
|
变量var的值, 与\$var相同
|
\${var-DEFAULT}
|
如果var没有被声明, 那么就以\$DEFAULT作为其值 *
|
\${var:=DEFAULT}
|
如果var没有被声明, 或者其值为空, 那么就以\$DEFAULT作为其值 *
|
\${var?ERR_MSG}
|
如果var没被声明, 那么就打印\$ERR_MSG *
|
\${var:?ERR_MSG}
|
如果var没被设置, 那么就打印\$ERR_MSG *
|
\${!varprefix*}
|
匹配之前所有以varprefix开头进行声明的变量
|
字符串变量操作
表达式
|
含义
|
\${#string}
|
\$string的长度
|
\${string:position}
|
在\$string中, 从位置\$position开始提取子串
|
\${string:position:length}
|
在\$string中, 从位置\$position开始提取长度为\$length的子串
|
\${string#substring}
|
从变量\$string的开头, 删除最短匹配\$substring的子串
|
\${string##substring}
|
从变量\$string的开头, 删除最长匹配\$substring的子串
|
\${string%substring}
|
从变量\$string的结尾, 删除最短匹配\$substring的子串
|
\${string%%substring}
|
从变量\$string的结尾, 删除最长匹配\$substring的子串
|
\${string/substring/replacement}
|
使用\$replacement, 来代替第一个匹配的\$substring
|
\${string//substring/replacement}
|
使用\$replacement, 代替所有匹配的\$substring
|
\${string/#substring/replacement}
|
如果\$string的前缀匹配\$substring, 那么就用\$replacement来代替匹配到的\$substring
|
数值类
\${string/%substring/replacement}
|
如果\$string的后缀匹配\$substring, 那么就用\$replacement来代替匹配到的\$substring
|
条件判断文件类
标记
|
代表含义
|
-e filename
|
如果 filename 存在,则为真
|
-b filename
|
如果 filename 存在,并且是块文件,则为真
|
-c filename
|
如果 filename 存在,并且是字符文件,则为真
|
-d filename
|
如果 filename 存在,并且为目录,则为真
|
-f filename
|
如果 filename 存在,并且为常规文件,则为真
|
-h filename
|
如果 filename 存在,并且为符号连接,则为真
|
-s filename
|
如果 filename 存在,并且大小不为零,为真
|
-u filename
|
如果 filename 存在,并且为set-user-id,为真
|
-g filename
|
如果 filename 存在,并且为set-group-id,为真
|
-r filename
|
如果 filename 存在,并且可读,则为真
|
-w filename
|
如果 filename 存在,并且可写,则为真
|
-x filename
|
如果 filename 存在,并且可执行,则为真
|
-k filename
|
如果 filename 存在,并且设置了sticky位,为真
|
-p filename
|
如果 filename 存在,并且为有名管道(FIFO),真
|
-o optname
|
如果 shell 选项 optname 被开启,则为真
|
-t fd
|
如果文件描述符被打开并指向一个终端,则为真
|
-O filename
|
如果 filename 存在,并且被有效用户ID所拥有,则为真
|
-G filename
|
如果 filename 存在,并且被有效组ID所拥有,则为真
|
-S filename
|
如果 filename 存在,并且为一个socket,则为真
|
-N filename
|
如果 filename 存在,并且在上次读取后被修改过,则为真
|
file1 -nt file2
|
如果 file1 比 file2 新,或者 file1 存在 file2 不存在,则为真
|
file1 -ot file2
|
如果 file1 比 file2 旧,或者 file2 存在 file1 不存在,则为真
|
file1 -ef file2
|
如果 file1 和 file2 都指向同样的设备(device)和索引节点号(inode numbers),则为真
|
标记
|
代表含义
|
-eq 等于
|
[ \$num1 -eq \$num2 ]
|
-ne 不等于
|
[ 100 -ne \$num1 ]
|
-lt 小于
|
[ 100 -lt `expr \$num1 + \$num2` ]
|
-le 小于或等于
|
[ 100 -le `expr \$num1 \* \$num2` ]
|
-gt 大于
|
[ 100 -gt `expr \$num1 / \$num2` ]
|
-ge 大于或等于
|
[ 100 -ge `expr \$num1 % \$num2` ]
|
字符类
标记
|
代表含义
|
-z string
|
如果 string 空为真
|
-n string
|
如果 string 长度不为零为真
|
string1 != string2
|
如果 string1 与 string2 不同,则为真
|
string1 == string2
|
如果 string1 与 string2 相同,则为真
|
string1 \> string2
|
如果 string1 按字典顺序比较大于 string2,则为真
|
string1 \< string2
|
如果 string1 按字典顺序比较小于 string2,则为真
|
流程控制
如: if while for 分支
表达式分支判断
if...then...fi if...then...else..fi if...then...elif...then...else...fi case...in...esac score="90" if [ $score -ge 90 ];then echo "A" elif [ $score -ge 80 ];then echo "B" elif [ $score -ge 70 ];then echo "C" else echo "D" fi
字符匹配判断
case "$1" in start) echo "start server...";; stop) *) esac echo "stop server...";; echo "unknown action";;
循环
for ...in ...do...done while ...do...done while read...do...done
for循环版本
total=0 for((i=0;i<=100;i++));do for i in `seq 1 100`;do ((total+=i)) echo $total
while循环版本
total=0 i=0 while [ $i -le 100 ];do total=$[$total+$i] ((i++)) done echo $total
while循环读取参数
#!/bin/sh verbose='' while getopts 'af:v' flag; do case "${flag}" in a) echo "aaa";; f) files="${OPTARG}" ;; v) verbose='1.0.0' ;; *) echo "Unexpected option ${flag}" ;; esac done [ ! -z $files ] && echo $files [ ! -z $verbose ] && echo $verbose
注意:如果字符后跟冒号(例如f:),则该选项预期具有参数。用法示例:./script -v -a -b -f filename
while读取文件行
while读取文件行
#!/bin/sh while read -r LINE;do echo $LINE done < test.txt
菜单
#!/bin/sh while read -r LINE;do echo $LINE done < test.txt MENU="TV Movie Game Sport" echo "which do you like?" select list in $MENU Quit;do done [ $list = "Quit" ] && echo "ByeBye" && break; echo "Oh,you like $list !"
内置函数
命令
|
含义
|
. file or source file
|
dot命令从文件file中读取命令并执行
|
:
|
空操作,返回退出状态0
|
alias
|
显示和创建已有命令的别名
|
bg
|
把作业放到后台
|
bind
|
显示当前关键字与函数的绑定情况,或将关键字与readline函数或宏进行绑定
|
break
|
从最内层循环跳出
|
cd [arg]
|
改变目录,如果不带参数,则回到主目录,带参数则切换到参数所指的目录
|
declare [var]
|
显示所有变量,或用可选属性声明变量
|
dirs
|
显示当前记录的目录(pushd的结果)
|
echo [args]
|
通用打印
|
prinf
|
格式化输出
|
eval [args]
|
把args读入Shell,并执行产生的命令
|
exec command
|
运行命令,替换掉当前Shell |
exit [n] 以状态n退出Shell
|
export [var] 使变量可被子Shell识别
|
fc 历史的修改命令,用于编辑历史命令
|
fg 把后台作业放到前台
|
getopts 解析并处理命令行选项
|
history 显示带行号的命令历史列表
|
jobs 显示放到后台的作业
|
kill [-signal process] 向由PID号或作业号指定的进程发送信号。输入kill-l查看信号列表
|
let 用来计算算术表达式的值,并把算术运算的结果赋给变量
|
local 用在函数中,把变量的作用域限制在函数内部
|
logout 退出登录Shell
|
popd 从目录栈中删除项
|
pushd 向目录栈中增加项
|
pwd 打印出当前的工作目录
|
read [var] 从标准输入读取一行,保存到变量var中
|
readonly [var] 将变量var设为只读,不允许重置该变量
|
return [n] 从函数中退出,n是指定给return命令的退出状态值
|
set 设置选项和位置参量
|
shift [n] 将位置参量左移n次
|
stop pid 暂停第pid号进程的运行
|
suspend 终止当前Shell的运行(对登录Shell无效)
|
test 检查文件类型,并计算条件表达式
|
times 显示由当前Shell启动的进程运行所累计用户时间和系统时间
|
trap [arg] [n] 当Shell收到信号n(n为0、1、2或15)时,执行arg
|
ulimit 显示或设置进程可用资源的最大限额
|
umask [八进制数字] 用户文件关于属主、属组和其他用户的创建模式掩码
|
unalias 取消所有的命令别名设置
|
unset [name] 取消指定变量的值或函数的定义
|
wait [pid#n] 等待pid号为n的后台进程结束,并报告它的结束状态 |
read 命令
#!/bin/sh
read -t15 -n5 -p "请输入你的编号(<=5位数)" userid read -t5 -n5 -s -p "请输入你的密码(限时5秒)" userpass
printf "\n%10s : %10s\n" $userid $userpass
#!/bin/bash
read -n1 -p "Do you want to continue [Y/N]? " answer
case $answer in
Y|y)
echo "fine ,continue";;
N|n)
echo "ok,good bye";;
*)
echo "error choice";;
esac
exit 0
#!/bin/sh BONDING="bond0|eth0@eth1@eth2@eth3@eth4@eth5|0|118.123.118.134|255.255.255.22 4|118.123.118.129|1500" xx=$IFS;IFS="|";read -r nick devs mode ip mask gw mtu <<<"$BONDING";IFS=$xx
printf 命令
#!/bin/bash
printf "%-10s %-8s %-4s\n" 姓名 性别 体重kg printf "%-10s %-8s %-4.2f\n" 郭靖 男 66.1234printf "%-10s %-8s %-4.2f\n" 杨过 男 48.6543printf "%-10s %-8s %-4.2f\n" 郭芙 女 47.9876
Bash命令行小技巧
ctrl-a 跳转到行首
ctrl-e 跳转到行尾
ctrl-b 往后移动字符
ctrl-f 往前移动字符
ctrl-w 回退剪切一个单词
ctrl-u 剪切至行首
ctrl-k 剪切至行尾
ctrl-y 粘贴剪切板内容
ctrl-r 反向搜索历史命令
Bash的颜色方案
下表是linux终端所支持的基本颜色的代码:
前景
|
背景
|
颜色
|
30
|
40
|
黑色
|
31
|
41
|
红色
|
32
|
42
|
绿色
|
33
|
43
|
黄色
|
34
|
44
|
蓝色
|
35
|
45
|
紫红色
|
36
|
46
|
青蓝色
|
37
|
47
|
白色
|
除了基本的色彩代码,linux还支持一些额外的样式控制代码,如下表所示:
代码
|
含义
|
0
|
OFF
|
1
|
高亮显示
|
4
|
下划线
|
5
|
闪烁
|
7
|
反白显示
|
8
|
不可见 |
演示代码
#/bin/bash
for STYLE in 0 1 4 5 7 8; do
for FG in 30 31 32 33 34 35 36 37; do
for BG in 40 41 42 43 44 45 46 47; do
CTRL="3[${STYLE};${FG};${BG}m"
echo -en "${CTRL}"
echo -n "${STYLE};${FG};${BG}"
echo -en "3[0m"
done
echo done
echo done
# Reset
echo -e "3[0m"
以上是关于Bash 之 Shell编程的重要性的主要内容,如果未能解决你的问题,请参考以下文章