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编程的重要性的主要内容,如果未能解决你的问题,请参考以下文章

Linux之shell编程的基本使用

Linux bash基础特性二

Linux Shell编程之基本概念

Linux Shell编程之谈谈bash

bash之set命令

Shell编程之函数