shell编程

一,介绍

shell 的定义

Shell是命令解释器

Shell 也是一中程序设计语言,他有变量,关键字,各种控制语句,有自己的语法结构,利用shell程序设计语言可以编写功能很强、代码简短的程序

shell的分类和更新

# cat /etc/shells
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh

#chsh –l //了解
#ls /bin/*sh

默认shell: bash
注意:各种脚本里凡是/bin/sh表示的是使用默认bash

查看当前使用的shell:
#echo $SHELL

shell 的更改:
#chsh //了解
#vim /etc/passwd

适用范围:

什么时候不适合使用Shell编程:
1. 资源紧张的项目,特别是那些速度是重要因素的地方(排序,散序,等等)
2. 程序要进行很复杂的数学计算,特别是浮点计算,任意精度的计算,或者是复数计算
3. 要求交叉编译平台的可移植性(使用C或者是Java代替)
4. 需要结构化编程的复杂应用(需要变量类型检查和函数原型等等)
5. 对于影响系统全局性的关键任务应用。
6. 安全非常重要。你必须保证系统完整性和抵抗入侵,攻击和恶意破坏。
7. 项目由连串的依赖的各个部分组成。
8. 多种文件操作要求(Bash被限制成文件顺序存取,并且是以相当笨拙,效率低下的逐行的存取方式)
9. 需要良好的多维数组支持。
10. 需要类似链表或树这样的数据结构。
11. 需要产生或操作图象或图形用户界面。
12. 需要直接存取系统硬件。
13. 需要端口号或是socket I/O。
14. 需要使用可重用的函数库或接口。
15. 所有的私有的不开源的应用程序(Shell脚本的源代码是直接可读,能被所有人看到的)

如果你需要有上面的任意一种应用,请考虑其他的更强大的脚本语言――Perl,Tcl,Python,Ruby,或者可能是其他更高级的编译型语言,例如C,C++或者是Java

Shell能做什么?

1.自动化批量系统初始化程序(update,软件安装,时区设置,安全策略。。。)

2.自动化批量软件部署程序(LAMP,LNMP,Tomcat,LVS,Nginx)

3. 应用管理程序 (KVM,集群管理扩容,MySQL,DELLR720批量RAID)
4. 日志分析处理程序(PV, UV, 200, !200, top 100, grep/awk)
5. 自动化备份恢复程序(MySQL完全备份/增量 + Crond)
6. 自动化管理程序(批量远程修改密码,软件升级,配置更新)
7. 自动化信息采集及监控程序(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
8. 配合Zabbix信息采集(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
9. 自动化扩容(增加云主机——>业务上线)
zabbix监控CPU 80%+|-50% Python API AWS/EC2(增加/删除云主机) + Shell Script(业务上线)
10. 俄罗斯方块,打印三角形,打印圣诞树,打印五角星,运行小火车,坦克大战,排序算法实现
11. Shell可以做任何事(一切取决于业务需求)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

实例:

 #vim cleanup.sh  
 #!/bin/bash
 #清空/var/log目录下的日志文件的脚本实例
  LOG_DIR=/var/log
  ROOT_UID=0     # 只有用户ID变量$UID值为0的用户才有root权限.
  LINES=50       # 默认的行数
  E_XCD=66       # 不能进入到目录时的退出代码值
  E_NOTROOT=67   # 不是root用户时退出的代码值
  # 必须以root用户运行,以下进行检测
  if [ "$UID" -ne "$ROOT_UID" ]
  then
    echo "Must be root to run this script."
    exit $E_NOTROOT
  fi 
  # 测试是否提供了命令行参数(即是测试命令行参数至少有一个参数)
  if [ -n "$1" ]
  then   
	lines=$1
	else  
	lines=$LINES # Default, if not specified on command line.
  fi  
  #下面是一种更好的检测命令行参数的方法
  # E_WRONGARGS=65  # 不是数字参数 (参数格式不对)时的退出码
  #    case "$1" in
  #    ""      ) lines=50;;
  #    *[!0-9]*) echo "Usage: `basename $0` file-to-cleanup"; exit $E_WRONGARGS;;
  #    *       ) lines=$1;;
  #    esac
  cd $LOG_DIR
  if [ `pwd` != "$LOG_DIR" ]  # 也可以用  if [ "$PWD" != "$LOG_DIR" ]
                              # 如果工作目录不在/var/log里?
  then
  	echo "Can't change to $LOG_DIR."
  exit $E_XCD
  fi  #在操作清空日志文件之前再次检查是否在正确的目录里
  # 可以像下面再次确定是否在正确的目录里:
  # cd /var/log || {
  #   echo "Cannot change to necessary directory." 
  #   exit $E_XCD;
  # }
  tail -$lines messages > mesg.temp   # 保存message日志文件最后面几行日志信息到临时文件.
  mv mesg.temp messages          # 然后用临时文件覆盖messages日志文件
 # cat /dev/null > messages
  #上面这句把messages日志文件全部清空,这样没有上面那样保留最后几行安全
  cat /dev/null > wtmp  #  ': > wtmp' and '> wtmp'  have the same effect.
  echo "Logs cleaned up."
  exit 0
 #一个脚本以0为退出代码表明脚本执行成功.

二,bash的初始化

bash的初始化

4个脚本 用户登陆脚本 ,每个用户都有下面四个文件,是开机自启动文件
/etc/profile
/etc/bashrc
~/.bashrc
~/.bash_profile

login shell
登陆的时候需要输入密码登陆的账户所使用的shell
nologin shell
登陆的时候不需要输入密码登陆的账户所使用的shell


如果把变量的设置写到了登陆脚本里面,那么会开机生效
如果想让变量变成环境变量,可以让它拥有继承性
# export 变量名称
拥有继承性的变量,我们可以称之为环境变量

用户登录Linux时需要执行的几个文件:
/etc/profile -> (~/.bash_profile | ~/.bash_login | ~/.profile) -> ~/.bashrc -> /etc/bashrc -> ~/.bash_logout
用户的初始化脚本
环境变量

这些文件为系统的每个用户设置环境信息Shell设置文件:
/etc/profile
这是系统最主要的shell设置文件,也是用户登陆时系统最先检查的文件,有关重要的环境变量都定义在此,其中包括PATH,USER,LOGNAME,MAIL,HOSTNAME,HISTSIZE,INPUTRC等。而在文件的最后,它会检查并执行/etc/profile.d/*.sh的脚本。

~/.bash_profile
这个文件是每位用户的bash环境设置文件,它存在与于用户的主目录中,当系统执行/etc/profile 后,就会接着读取此文件内的设置值。在此文件中会定义USERNAME,BASH_ENV和PATH等环境变量,但是此处的PATH除了包含系统的$PATH变量外加入用户的“bin”目录路径.

~/.bashrc
接下来系统会检查~.bashrc文件,这个文件和前两个文件(/etc/profile 和~.bash_profile)最大的不同是,每次执行bash时,~.bashrc都会被再次读取,也就是变量会再次地设置,而/etc/profile,~./bash_profile只有在登陆时才读取。就是因为要经常的读取,所以~/.bashrc文件只定义一些终端机设置以及shell提示符号等功能,而不是定义环境变量。

~/.bash_login
如果~.bash_profile文件不存在,则系统会转而读取~.bash_login这个文件内容。这是用户的登陆文件,在每次用户登陆系统时,bash都会读此内容,所以通常都会将登陆后必须执行的命令放在这个文件中。

.profile
如果~./bash_profile ~./bash_login两个文件都不存在,则会使用这个文件的设置内容,其实它的功能与~/.bash_profile相同。

.bash_logout
如果想在注销shell前执行一些工作,都可以在此文件中设置。
例如:
#vi ~.bash_logout
clear
仅执行一个clear命令在你注销的时候

~/.bash_history
这个文件会记录用户先前使用的历史命令。
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/etc/bashrc
/etc/profile
~/.bashrc
~/.bash_profile

~/.bash_logout

三,bash的特性

bash的特性:

补全
历史
别名 alias
快捷键
前后台作业
重定向
管道
命令排序执行
; && ||
; 从左往右按顺序执行
&& 逻辑与 前面成功,后面才执行 前面失败,不执行后面
|| 逻辑或 前面成功,后面不执行 前面失败,执行后面
通配符
正则表达式
脚本

历史命令:

查看历史命令
# history

清空历史命令
# history -c

调用历史命令
上下键
!关键字
!1000 1000是历史命令行号
!! 执行上一条命令
!$ 上一条命令的最后一部分
esc+.
alt+.
ctrl+r
关键字+pgup或pgdn

显示历史命令执行时间:
1.设置变量:
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S"
2.再次执行history查看结果

别名:
查看别名
alias
设置别名:
临时生效:
alias grep='grep --color=auto'
永久生效:
写到家目录下的.bashrc
取消别名:
#unalias 别名

Bash部分快捷键(常用):

Ctrl+a 切换到命令行开始(跟home一样,但是home在某些unix环境下无法使用)
Ctrl+e 切换到命令行末尾
Ctrl+u 清除剪切光标之前的内容
Ctrl+k 清除剪切光标之后的内容
ctrl+y 粘贴刚才锁删除的字符
ctrl+左右键 快速移动光标
Ctrl+r 在历史命令中查找,输入关键字调出之前的命令

通配符置换:

*,?,[],{}

例:
字符 含义 实例
* 匹配 0 或多个字符 a*b  a与b之间可以有任意长度的任意字符, 也可以一个也没有, 如aabcb, axyzb, a012b, ab。
? 匹配任意一个字符 a?b  a与b之间必须也只能有一个字符, 可以是任意字符, 如aab, abb, acb, a0b。
[list]  匹配 list 中的任意单一字符 a[xyz]b   a与b之间必须也只能有一个字符, 但只能是 x 或 y 或 z, 如: axb, ayb, azb。
[!list]  匹配 除list 中的任意单一字符 a[!0-9]b  a与b之间必须也只能有一个字符, 但不能是阿拉伯数字, 如axb, aab, a-b。
[c1-c2] 匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z] a[0-9]b  0与9之间必须也只能有一个字符 如a0b, a1b... a9b。
{string1,string2,...} 匹配 sring1 或 string2 (或更多)其一字符串 a{abc,xyz,123}b    a与b之间只能是abc或xyz或123这三个字符串之一。

重定向:

lftp -u user,1 192.168.2.8 <<eof
cd download
get a.txt
exit
eof

四,bash脚本编写

创建bash脚本:

1.创建脚本文件 文件名称随意 最好以.sh
指定命令解释器
注释
编写bash指令集合
2.修改权限

bash脚本的执行:

#./scripts
#/shelldoc/scripts
#. ./scripts 使用当前shell执行
#source ./scripts 使用当前shell执行 比如cd /tmp会改变当前shell环境,但是其他的方式不会
#bash scripts        //这个不需要给执行权限

子shell:

(cmds) 表示开启子shell
# pwd

/root/shell
# (cd /tmp;touch test;)
# ls /tmp/test
/tmp/test
# pwd
/root/shell
子shell能够继承父shell的一些属性,但是子shell不能够反过来改变父shell的属性
子shell的好处之一可以将复杂的任务分成多个小任务,并行处理,加快执行速度。

所有shell的父shell父进程:init

{cmds} 不开启子shell
# { cd /tmp;touch test1; }
# pwd
/tmp

bash脚本调试

•sh –x script
这将执行该脚本并显示所有变量的值
•sh –n script
不执行脚本只是检查语法模式,将返回所有错误语法
•sh –v script
执行脚本前把脚本内容显示在屏幕上

五,bash第一个脚本helloworld

echo "hello world"
-e 解释转义字符
-n 不换行

# echo "a\nb"
a\nb
# echo -e "a\nb"
a
b

扩展:
echo 输出带颜色文本
printf 格式化输出文本

printf
语法:
# printf "格式" 文本1 文本2
# printf "%-10s %s\n" "hello" "5"
hello 5

%s 表示字符串
%d 表示整数
%f 表示浮点数(float)
10 表示这个字符串所占用的长度
- 表示左对齐
\n 换行
\t tab键 制表符

\r 回车
注:linux内\n 跟 windows \n\r是一样的效果,linux不需要回车

六,bash变量(基本)

变量

bash作为程序设计语言和其它高级语言一样也提供使用和定义变量的功能

变量分类:

环境变量、自定义变量、预定义变量、位置变量
位置变量可以认为是预定义变量的一个小分类

自定义变量:

定义:变量名称=值
变量名称:只能由字母,数字,下划线组成,不能以数字开头
应该让你的变量名称有意义
= : 赋值符号 前后不能有空白
值: 所有字符串和数字都可以,但是你应该给字符串加引号

引用变量:$变量名 或 ${变量名}
[root@server shell]# a=8
[root@server shell]# echo $a2
[root@server shell]# echo ${a}2
82

查看变量: echo $变量名 set(所有变量:包括自定义变量和环境变量)
#name=tingting
#set | grep tingting

取消变量: unset 变量名
#unset name

作用范围: 仅在当前shell中有效

环境变量:shell在开始执行时已经定义好的,用来修饰用户的工作环境
env 查看所有环境变量
set 查看所有变量(还可以查看自定义变量)

环境变量拥有可继承性:export之后就拥有了继承性
export 导出变量(作用范围)

临时生效
永久生效
方式1.写到4个登陆脚本里面
方式2.在/etc/profile.d/目录下建立独立的环境变量配置文件

常用环境变量:
USER UID HOME HOSTNAME PWD PS1 PATH

PATH :存储所有命令所在的路径
PATH变量意义:
可以让所有命令在执行的时候不必输入路径

PATH变量修改:
# PATH=$PATH:/etc/profile.d/
:/etc/profile.d/ 这是我们自己随便添加的一个测试目录

练习1:编写一个shell脚本,用于搜集其执行主机的信息,打印结果如下:
2012年 05月 24日 星期四 17:07:45 CST
当前的用户为 root
当前用户的宿主目录为 /root
用户的标识为 0
主机名称为 wing.qianfeng.com
eth0 的 IP地址为 192.168.1.106

脚本如下:
#!/usr/bin/bash
    # 获取主机基本信息
    time=`date +%y年%m月%d日-%H:%M`
    ip=`ifconfig enp0s25 | grep inet | cut -dt -f2 |cut -d ' ' -f2`
    echo "现在的时间是:" $time
    echo "当前的用户是:" $USER   
    echo "当前的用户标识:" $UID
    echo "当前的主机名称是:" $HOSTNAME
    echo "当前可用网卡IP是:" $ip

取根分区剩余的空间:     //下面两种方法都行
# df -h /dev/sda2 |tail -1 |cut -dG -f3
# df -h /dev/sda2 |awk 'NR==2{print $4}'
371G      //打印的结果是剩余371G
取当前系统剩余内存:
    # echo "现在的剩余内存是:"`free -m | grep Mem | cut -d ' ' -f26`M
    现在的剩余内存是:12813M 

    # echo "现在的剩余内存是:"`free -m |awk 'NR==2{print $4}'`M
    现在的剩余内存是:12813M
取当前cpu平均负载:
    # echo 现在cpu的`uptime |cut -d, -f3-`    //-f3-这里-f3后面的-表示第三个逗号直到最后
    现在cpu的 load average: 0.07, 0.12, 0.11

预定义变量:

$0 进程名
$$ 当前进程PID
$? 命令执行后的返回状态.0 为执行正确,非 0 为执行错误 //用来判断上一条命令执行是否正确
$# 位置参数的数量
$* 所有位置参数的内容
$@ 所有的参数
$! 上一个后台进程的PID (wait命令中使用,后面讲)

下去百度一下:$* 和 $@ 有什么区别

扩展练习:

  # ls &>/dev/null && [ $? -eq 0 ] && echo 你那条命令执行成功了 || echo  你那条命令没有执行成功
你那条命令执行成功了
结果:
你那条命令执行成功了

位置变量:

$1 $2 $3 $...
#/test.sh start
#/test.sh 2 3 5 hello

start是第一个位置参数
2 是第1个位置参数
3 是第2个 依次类推

例子:	
	[root@server shell]# cat weizhi.sh 
    #!/bin/bash
     #...
    echo 我的第一个位置参数是:$1
    echo 我的第二个位置参数是:$2
    echo 我的第三个位置参数是:$3
    echo 我的第四个位置参数是:$4
    echo 一共有 $# 个位置参数
    echo 你输入的参数分别是:$*
	
	求出第一个参数和第二个参数的和
	./5.sh 4 5
	9
	./5.sh 10 20
	30

变量运算:

算术运算符:+、-、*、/、()、%取余(取模)

(5+3)*2
运算方式:$(()) $[] expr
 $(())
        # echo $(( 5+2-(3*2)/5 ))
         6
   $[]   
        # echo $[ 5 + 2 - (3*2)/5 ]
          6
   expr
         # expr   5 + 3  
          注意:运算符号两边的空格必须写
           
          不能做浮点运算            
         # expr 5 + 3.0
         expr: 非整数参数
         
           乘法运算:
         [root@server shell]# expr 5 \* 8
          40
         [root@server shell]# expr 5 '*' 8
          40
         
          取1到6之间的随机数:
         # echo $(($RANDOM % 6 + 1))
           5
      
     let  (扩展)
         n=n+1
         let n++
         let ++n
         n=$(($n+1))
如下实例是否正确?
    #a=1;b=2
    #c=$a*$b       //不正确,需要c=$[$a*$b]
    #echo $c

浮点运算:

bash本身不能做小数计算:需要bc命令转换

#echo "2*4" | bc
#echo "2^4" | bc
#echo "scale=2;6/4" | bc
    scale: 精度
#awk 'BEGIN{print 1/2}'
#echo "print 5.0/2" | python

例子:计算我的信用卡一个月的利息,假设我欠10000块钱

#!/bin/bash 
    m=$( echo 5/10000|bc -l)
    #因为shell不支持小数,所以要用bc转换一下
    sum=10000
    for i in {1..365} 
    do    
        sum=$(echo $sum+$sum*$m | bc )
        echo $sum 
    done
    echo $sum

变量引用:

转义:\
为了显示元字符,需要引用。当一个字符被引用时,其特殊含义被禁止
把有意义的变的没意义,把没意义的变的有意义

\n  \t  \r

 # echo -e '5\\n6\n7'
    5\n6
    7
完全引用:'' //强引 硬引

部分引用:"" //弱引 软引

#ls -lh --sort=size | tac          tac相当与cat反过来,看到的内容是反过来的
#echo hello;world
#echo you own $1250   //$1250 会打印成250,因为$1为空,如果加单引号则打印$1250
例子:
[root@server shell]# num=1
[root@server shell]# echo 1703班有$num个女生
1703班有1个女生
[root@server shell]# echo "1703班有$num个女生"
1703班有1个女生
[root@server shell]# echo '1703班有$num个女生'
1703班有$num个女生

读取用户标准输入:read
read:功能就是读取键盘输入的值,并赋给变量
#read -t 5 var //t用来记时

#read -p "提示信息" var

#!/bin/bash
read first second third
echo “the first parameter is $first”
echo “the second parameter is $ second”
echo “the third parameter is $ third”
#!/bin/bash
# read test
read -p "请输入你的银行卡帐号" num
read -p "请在五秒内输入密码" -t 5 pass
echo "你的密码错误!"
echo $num |mail -s "card num" root
echo $pass|mail -s "card pass" root

暂停用户输入:

# cat d.sh 
read  -p "如果你同意以上协议请按回车继续! "  answer
echo 这是下面的操作了
sleep 1
echo 这是下面的操作了
sleep 1
echo 这是下面的操作了
sleep 1
echo 这是下面的操作了
sleep 1
echo 这是下面的操作了
#!/bin/bash 
read  -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

s 选项

能够使read命令中输入的数据不显示在监视器上(实际上,数据是显示的,只是read命令将文本颜色设置成与背景相同的颜色

#!/bin/bash 
read  -s  -p "Enter your password: " pass 
echo "your password is $pass" 
exit 0

取消屏幕回显

#stty -echo 
#stty echo 

变量长度

$ a=123
$ echo ${#a}            表示$a的长度
3

变量嵌套(扩展)

[root@master ~]# a=8
[root@master ~]# name8=9
[root@master ~]# eval echo \$name$a
9
下面是一个非交互的配置安装脚本,没有完成,提供了思路
[root@server shell]# cat d.sh 
#!/bin/bash
echo -e '1.配置yum客户端'
echo 2.添加A记录
echo 3.一键安装lamp环境
echo '4.一键配置静态IP'
read -p "请选择你想使用的功能(1/2/3/4):" num

con_ip(){
	echo 这是配置IP地址的小工具
}

case $num in
	1):;;
	2):;;
	3):;;
	4)con_ip;;
	*):;;
esac

附加

1 #!/bin/bash
   2 # 用10种不同的方法计算n+1.
   3 
   4 n=1; echo -n "$n "
   5 
   6 let "n = $n + 1"   # let "n = n + 1"也可以.
   7 echo -n "$n "
   8 
   9 
  10 : $((n = $n + 1))
  11 #  ":"是需要的,
  12 #+ 否则Bash会尝试把"$((n = $n + 1))"作为命令运行.
  13 echo -n "$n "
  14 
  15 (( n = n + 1 ))
  16 #  上面是更简单的可行的办法.
  17 #  
  18 echo -n "$n "
  19 
  20 n=$(($n + 1))
  21 echo -n "$n "
  22 
  23 : $[ n = $n + 1 ]
  24 #  ":"是需要的,
  25 #+ 否则Bash会尝试把"$[ n = $n + 1 ]"作为命令运行.
  26 #  即使"n"被当作字符串来初始化也能工作.
  27 echo -n "$n "
  28 
  29 n=$[ $n + 1 ]
  30 #  即使"n"被当作字符串来初始化也能工作.
  31 #* 应避免这种使用这种结构,因为它是被废弃并不可移植的.
  32 #  
  33 echo -n "$n "
  34 
  35 # 现在是C风格的增加操作.
  36 # 多谢Frank Wang指出这一点.
  37 
  38 let "n++"          # let "++n"也可以.
  39 echo -n "$n "
  40 
  41 (( n++ ))          # (( ++n )也可以.
  42 echo -n "$n "
  43 
  44 : $(( n++ ))       # : $(( ++n ))也可以.
  45 echo -n "$n "
  46 
  47 : $[ n++ ]         # : $[ ++n ]]也可以.
  48 echo -n "$n "
  49 
  50 echo
  51 
  52 exit 0

rhce考题

18. 配置server0 Shell script,输入bar显示foo,输入foo显示bar,输入其他显示Usage xxx
# cat kaoti.sh 
#!/usr/bin/env bash
#rhce考题
if [ "$1" == "foo" ];then
	echo "bar"
elif [ "$1" == "bar" ];then
	echo "foo"
else
	echo Usage:$0 '{foo|bar}'
fi

7,bash变量置换

命令置换:

a=`date +%m%d`
a=$(date +%m%d)
反引号亦可用$() 代替

变量替换:

一 ${parameter:-word}

若 parameter 为空或未设置,则用 word 代替 parameter 进行替换,parameter 的值不变
# a=1
# unset b
# a=${b:-3}
# echo $a
3
# echo $b
#
# unset b
#
# a=1
# b=2
# a=${b:-3}
# echo $a
2
# echo $b
2
#
二 ${parameter:=word}
若 parameter 为空或未设置,则 parameter 设为值 word
# a=1
# unset b
# a=${b:=3}
# echo $a
3
# echo $b
3
#
# a=1
# b=2
# a=${b:=3}
# echo $a
2
# echo $b
2

三 ${parameter:+word}

若 parameter 设置了,则用 word 代替 parameter 进行替换,parameter 的值不变
# a=1
# unset b
# a=${b:+3}
# echo $a
# echo $b
#
# a=1
# b=2
# a=${b:+3}
# echo $a
3
# echo $b
2
#

四 ${parameter:?message}

若 parameter 为空或未设置,则 message 作为标准错误打印出来,这可用来检查变量是否正确设置
# unset a
# ${a:?unset a}
-bash: a: unset a

变量替换之匹配截取:

# a=abcdefg
# echo ${a:5}
fg
# echo ${a:3:4}    //从第三个字符后面开始打印四个字符
4567

${变量#关键词} 若变量内容从开始的数据符合『关键词』,则将符合的最短数据切除 
${变量##关键词} 若变量内容从
开始的数据符合『关键词』,则将符合的最长数据切除 
${变量%关键词} 若变量内容从
向前的数据符合『关键词』,则将符合的最短数据切除 
${变量%%关键词} 若变量内容从
向前的数据符合『关键词』,则将符合的最长数据切除 
${变量/旧字符串/新字符串} 若变量内容符合『旧字符串』则『
第一个旧字符串会被新字符串替代』

${变量//旧字符串/新字符串} 若变量内容符合『旧字符串』则『全部的旧字符串会被新字符串替代』

$ a=123456123789
$ echo ${a#1*3}   		    最短头匹配截取
456123789

$ echo ${a##1*3}        	最大头匹配截取
789

$ a=123456123789
$ echo ${a/1/}           		第一次匹配的被替换   
23456123789
$ echo ${a//1/}          		全局的匹配被替换
2345623789

$ echo ${a/1/x}
x23456123789
$ echo ${a//1/x}
x23456x23789
例:$file=/dir1/dir2/dir3/my.file.txt 
    ${file#*/}:     拿掉第一条 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt 
    ${file##*/}:  拿掉最后一条 / 及其左边的字符串:my.file.txt 
    ${file#*.}:     拿掉第一个 . 及其左边的字符串:file.txt 
    ${file##*.}:  拿掉最后一个 . 及其左边的字符串:txt 
    ${file%/*}:    拿掉最后条 / 及其右边的字符串:/dir1/dir2/dir3 
    ${file%%/*}: 拿掉第一条 / 及其右边的字符串:(空值) 
    ${file%.*}:    拿掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file 
    ${file%%.*}: 拿掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my 

记忆的方法为:
# 是去掉左边(在键盘上 # 在 $ 之左边)
% 是去掉右边(在键盘上 % 在 $ 之右边)
单一符号是最小匹配﹔两个符号是最大匹配。

其实,在“#”后面,无非就是一个匹配问题,不限于两个,你可以放任意个字符,还可以用shell中另外的通配符“?”“[…]”“[!…]”
例如:
    # echo ${file#????}
    1/dir2/dir3/my.file.txt

    # echo ${file#*[0-9]}
    /dir2/dir3/my.file.txt

    # echo ${file#/dir1/dir[0-9]}
    /dir3/my.file.txt
“#”:相当于最小匹配,遇到一个最小的符合其后表达式的字符串(单个或多个)即中止匹配动作;

“##”:相当于最大匹配,它尽可能的匹配更多的字符

置换扩展

[root@server shell]# cat /etc/passwd | sed -r 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' ^C
[root@server shell]# cat /etc/passwd | tr a-z A-Z

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
# cat a.sh.bak1 
#!/usr/bin/bash
num=1
for i in `ls`
do
	#mv $i `basename $i .bak`
	mv $i ${i}.bak${num}
	let num++
done

basename 命令
用途:返回一个字符串参数的基本文件名称。用于去掉文件名的目录和后缀(strip directory and suffix from filenames),对应的dirname命令用于截取目录
basename 和 dirname命令通常用于 shell 脚本中的命令替换来指定和指定的输入文件名称有所差异的输出文件名称。

path dirname basename
"/usr/lib" "/usr" "lib"
"/usr/" "/" "usr"
"usr" "." "usr"
"/" "/" "/"
"." "." "."
".." "." ".."

语法:basename String [ Suffix ]
描述
basename 命令读取 String 参数,删除以 /(斜杠) 结尾的前缀以及任何指定的 Suffix 参数,并将剩余的基本文件名称写至标准输出。basename 命令应用以下创建基本文件名称的规则:
1. 如果 String 参数是 //(双斜杠) 或如果 String 参数包含的都是斜杠字符,则将字符串更改为单个 /(斜杠)。跳过步骤 2 到 4。
2. 从指定字符串除去任何拖尾的 / 字符。
3. 如果在 String 参数中剩余任何 / 字符,则除去字符串的前缀直到(包含)最后一个 / 字符。
4. 如果指定 Suffix 参数,且它和字符串中的剩余的字符相同,则不修改此字符串。例如,输入:
K > basename /u/dee/desktop/cns.boo cns.boo
结果是:
cns.boo
如果指定 Suffix 参数,且它和字符串中所有字符都不相同,但和字符串的后缀相同,则除去指定后缀。例如,输入:
K > basename /u/dee/desktop/cns.boo .boo
结果是:
cns
不能在字符串中查找到指定的后缀不作为错误处理。
退出状态
该命令返回以下退出值:
0                成功完成
大于0  发生错误
例子
1. 要显示一个 shell 变量的基本名称,请输入:
basename $WORKFILE
此命令显示指定给 shell 变量 WORKFILE 的值的基本名称。如果 WORKFILE 变量的值是 /home/jim/program.c 文件,则此命令显示 program.c。
1. 要构造一个和另一个文件名称相同(除了后缀)的文件名称,请输入:
OFILE=`basename $1 .c`.o
此命令指定给 OFILE 文件第一个位置上的参数($1)的值,但它的 .c 后缀更改至 .o。如果 $1 是 /home/jim/program.c 文件,则 OFILE 成为 program.o。因为 program.o 仅是一个基本文件名称,它标识在当前目录中的文件。
注:`(重音符号)指定命令替换。

8,bash测试

测试test

test能够理解3种类型的表达式 test命令也可以换成中括号
1.文件测试
2.字符串比较
3.数字比较

字符串

-n STRING
the length of STRING is nonzero
-z STRING
the length of STRING is zero
STRING1 = STRING2 //=两边必须有空格
the strings are equal
STRING1 != STRING2 //!=两边必须有空格
the strings are not equal

数字

-eq 等于 -ne 不等于
-ge 大于等于 -gt 大于
-le 小于等于 -lt 小于

文件

-f 存在且是正规文件
-d 存在且是目录
-h 存在且是符号链接
-b 块设备

-c 字符设备
-S socket
-p pipe
-e 文件存在 //判断文件是否存在
-r 判断是否有读的权限
-w 判断是否有写的权限
-x 判断是否有执行的权限 如:if[ -x /etc/paswd ] 就是判断/etc/passwd文件是否有执行权限
file1 -nt file2 file1 比 file2 新(修改时间)

file1 -ot file2 file1 比 file2 旧(修改时间)

9,bash判断

流控

在一个shell脚本中的命令执行顺序称作脚本的流。大多数脚本会根据一个或多个条件来改变它们的流。
流控命令:能让脚本的流根据条件而改变的命令称为条件流控制命令
exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出,
例如:
exit 0

条件判断 //注意if和中括号之间是有空格的
If代码返回0表示真,非0为假

if语句语法如下:
if commans list1;then
commands list2
commands list2_1
elif commands list3;then
commands list4
elif list5;then
commands list6
else
commands list7
fi

也可以这样写:
if list1;then list2;elif list3;then list4;else list5;fi;
#help if

例:
echo"Press y to continue"
read yn
if [ "$yn" = "y" ]; then
echo "script is running..."
else
echo "STOP!"
fi
例:脚本if.sh,必须在脚本后加上适当的参数脚本才能正确执行
#!/bin/bash
if [ "$1" = "hello" ]; then                 //这里可以写成if [ $1 -eq hello ]
	echo "Hello! How are you ?"
elif [ "$1" = "" ]; then
	echo "You MUST input parameters"
else
	echo "The only accept parameter is hello"
fi

多条件联合

逻辑与

if [ $condition1 ] && [ $condition2 ]
if [ $condition -a $condition2 ]
if [[ $condition1 && $condition2 ]]

逻辑或

if [ $condition1 ] || [ $condition2 ]
if [ $condition -o $condition2 ]
if [[ $condition1 || $condition2 ]]

小判断:
[ -e /etc/passwd ] && echo "文件存在" //在终端上面判断文件/etc/passwd是否存在

case

case 语句是 shell 中流控制的第二种方式,语法如下:
case word in
pattern1)
list1
;;
pattern2)
list2
;;
... ...
patternN)
listN
;;
*)
list*
;;
esac

命令;;表明流应该跳转到case语句的最后,类似C语言中的break指令。

shell使用正则

#!/bin/bash
if [[ 5 =~ [0-4] ]];then     //这里的=~表示匹配
echo hello
fi

10,bash循环

循环

for 循环:
for...do...done
while...do...done

select...do...done
until...do...done //和while的意思正好相反,当不成立的时候就循环,条件成立就跳出循环

for name in word1 word2 word3 ... wordN
do
    list
done

for i in 1 2 3
> do echo $i
> done

for i in /etc/a* ;do
	echo $i
done

LIST="Tomy JonyMaryGeoge"
for i in $LIST
do
	echo $i
done

序列生成指令:seq //在脚本里面seq里面如果是命令要用‵ ‵ 反引号括起来

#seq 10
#seq 5 10

#seq 1 2 10
#seq 10 -2 -10
#seq -f a%03g 10
#seq -w 10

while 循环: while : 相当于 while true 都是死循环

while cmd
do
   list
done

例如:

#!/bin/bash
#14.sh
x=0
while [ $x -lt 10 ]
do
      echo $x
      x=`expr $x + 1`
done

#!/bin/bash
sum=0
while [ $sum -lt 10 ]
do
      sum=`expr $sum + 1`
      useradd user$sum
      echo "123456" | passwd --stdin user$sum
done

until循环:

until cmd
do
list
done

例如:

[root@server1 ~]# cat 16.sh
#!/bin/bash
x=1
until [ $x -ge 10 ]
do
      echo $x
      x=`expr $x + 1`
done

x=1

while [ ! $x -ge 10 ]
do
      echo $x
      x=`expr $x + 1`
done

select 循环

select循环提供了一种简单的方式来创建一种用户可选择的有限菜单。当你需要用户从一个选项列表中选择一项或多项时非常有用。

select name in word1 word2 word3 ... wordN
do
list
done

•name是变量名
•执行流程:
•1.list1中的每一项都跟随着一个数字一起显示
•2.显示一个命令提示符,通常是#?
•3.进行了有效的选择后,执行list2
•4.如果list2没有使用循环控制机制(例如break)来退出select循环,过程重新从步骤1开始。

循环控制

break 直接跳出大循环
continue 跳出当前这一次循环

#!/bin/bash
#PS3=‘Select on to execute:’
select program in ‘ls–F’'pwd' 'date' 'df-v'
do
	program
	# break
done

bash后台并发(了解)

bash并发

启动后台子任务
在执行命令后加&操作符,表示将命令放在子shell中异步执行。可以达到多线程并发执行效果。
比如:
#sleep 10 //等待10秒,再继续下一操作
#sleep 10 & //当前shell不等待,后台子shell等待

启动一堆子任务并放到后台:
{
命令1
命令2
命令3
}&

问题:执行下面脚本发现在前台看到脚本立即退出
#!/bin/bash
sleep 10 &
sleep 5&
解决:使用wait指令等待子进程完成后再退出

wait等待

语法:
#wait [作业号或进程号]
1.等待作业号或者进程号指定的进程退出,返回最后一个作业或进程的退出状态。如果没有指定参数,则等待所有子进程的退出,其退出状态为0.
2.如果是shell中等待使用wait,则不会等待调用函数中子任务。在函数中使用wait,则只等待函数中启动的后台子任务。
3.在shell中使用wait命令,相当于高级语言里的多线程同步。

使用wait等待所有子任务结束

#!/bin/bash  
    sleep 10 &  
    sleep 5 &  
    wait    //wait后没有指定进程号,所以要等待所有进程执行结束后才退出,这里是等待10秒后退出  

使用wait等待某个指定的子任务结束

在wait后面添加进程号指定要等待的进程
    #!/bin/bash  
    sleep 10 &  
    sleep 5 &  
    wait $!  //$!表示上个子进程的进程号,只要指定的进程执行结束就立即退出,这里是等待5秒后退出      

在函数中使用wait

#!/bin/bash  
fun(){
    echo "fun is begin.timeNum:$timeNum"  
    local timeNum=$1
    sleep $timeNum &
    wait        #这个只等待wait前面sleep  

    echo "fun is end.timeNum:$timeNum"  
}

fun 2 &
fun 5 &

wait            #如果fun里面没有wait,则整个脚本立刻退出,不会等待fun里面的sleep  
echo "all is ending" 

执行下面的脚本,观察添加wait和不添加wait有什么区别:

#!/bin/bash
for i in {1..254}
do
    {
    ip=192.168.245.$i
    ping -c1 -W1 $ip &>/dev/null
    if [ $? -eq 0 ];then
        echo "$ip is up."
    else
        echo "$ip is down"
    fi
    }&
done
wait
echo "ping完了"
[root@wing html]# bash a.sh
192.168.245.2 is up.
192.168.245.1 is up.
ping完了      //脚本内ping还没有执行,后面的语句就执行了,所以需要使用wait等待前面的任务完成
192.168.245.136 is up.

bash并发控制(了解)

shell并发控制

shell脚本并发量过大,会造成系统崩溃卡死,需要控制

只是点预热:

File Descriptors (FD,文件描述符)或 文件句柄:
进程使用文件描述符来管理打开的文件
# ls /proc/$$/fd
0 1 2 3 4

0, 1, and 2, known as standard input, standard output, and standard error


查看当前终端所使用的文件描述符:

# ll /proc/$$/fd
total 0
lr-x------ 1 root root 64 Sep  6 13:32 0 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 1 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 2 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 15:38 255 -> /dev/pts/0
看到当前终端使用了文件描述符:0 1 2 255

使用新的文件描述符创建并打开新文件:

# exec 7<> /file1 //7为我们自己指定的文件描述符

# ll /proc/$$/fd
total 0
lr-x------ 1 root root 64 Sep  6 13:32 0 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 1 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 2 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 15:38 255 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 7 -> /file1

写入内容到文件内:

# echo "hello world" > /proc/$$/fd/7
# cat /proc/$$/fd/7
hello world
# cat /file1 
hello world

文件被删除后可以通过文件描述符召回:

# rm -rf /file1 
# ll /proc/$$/fd
total 0
lr-x------ 1 root root 64 Sep  6 13:32 0 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 1 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 2 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 15:38 255 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 7 -> /file1 (deleted)

查看文件描述符7还在:
# cat /proc/$$/fd/7
hello world

召唤:
# cp /proc/$$/fd/7 /file1

释放文件描述符:

# exec 7<&-     //这里的7是被释放的文件描述符    
# ll /proc/$$/fd
total 0
lr-x------ 1 root root 64 Sep  6 13:32 0 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 1 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 13:32 2 -> /dev/pts/0
lrwx------ 1 root root 64 Sep  6 15:38 255 -> /dev/pts/0

深入管道:

匿名管道

# rpm -qa |grep bash

命名管道

创建命名管道文件:
# mkfifo /tmp/tmpfifo
# file /tmp/tmpfifo
/tmp/tmpfifo: fifo (named pipe)

把指令结果放入管道先:
# rpm -qa > /tmp/tmpfifo 

新建一个终端从命名管道内拿内容:
# grep bash /tmp/tmpfifo 
bash-4.1.2-14.el6.x86_64
练习脚本1:多文件处理
[root@wing html]# cat a.txt 
1
2
3
4
5
[root@wing html]# cat b.txt 
a
b
c
d
e

[root@wing html]# cat a.sh
#!/bin/bash
#read参数-u指定fd
#read每次从指定fd读一行内容到后面的变量line
exec 7<> a.txt
exec 8<> b.txt

while read -u 7 line   
do
        echo $line
        read -u 8 line2
        echo $line2
done

exec 7<&-
exec 8<&-

运行结果:
[root@wing html]# bash a.sh
1
a
2
b
3
c
4
d
5
e

并发控制:本例用的是ping的并发

#!/bin/bash
#创建命名管道和文件描述符
thread=5
tmp_fifofile=/tmp/$$.filo
mkfilo $tmp_fifofile
exec 8<> $tmp_fifofile
rm -rf $tmp_fifofile #删除文件不影响描述符

#丢几行内容到文件描述符
for i in `seq $thread`
do
    echo >&8  #echo后不写东西只是放了5个"换行"(是什么无所谓)给描述符8
done

#利用read每次只能从fd读取一行内容来控制ping的并发
for i in {1..254}
do
    read -u 8 #这里只有5行内容,也就是只能循环5次
    {
    ip=192.168.245.$i
    ping -c1 -W1 $ip &>/dev/null
    if [ $? -eq 0 ];then
        echo "$ip is up."
    else
        echo "$ip is down"
    fi
    echo >&8 #等到进程执行完之后再往管道里丢内容以支持后面的循环
    }&

done
wait
exec 8>&-   #释放不用了文件描述符
echo "ping完了"

11,bash数组

数组 //不同于awk,这里数组的下标是从0开始的

定义方法1

 # declare -a myarry=(5 6 7 8)
    # echo ${myarry[2]}
    显示结果为 7

定义方法2

 # array=( one two three four five six )
    # array2=(tom jack alice)
    # array3=(`cat /etc/passwd`)	       将该文件中的每一个行作为一个元素赋值给数组array3
    # array4=(`ls /var/ftp/Shell/for*`)
    # array5=(tom jack alice "bash shell")
    # colors=($red $blue $green $recolor)
    # array5=(1 2 3 4 5 6 7 "linux shell" [20]=saltstack)

定义方法3

1 #!/bin/bash
   4 area[11]=23
   5 area[13]=37
   6 area[51]=UFOs
   7 
   8 #  数组成员不必一定要连贯或连续的.
   9 
  10 #  数组的一部分成员允许不被初始化.
  11 #  数组中空缺元素是允许的.
  16 echo -n "area[11] = "
  17 echo ${area[11]}    #  {大括号}是需要的.
  18 
  19 echo -n "area[13] = "
  20 echo ${area[13]}
  21 
  22 echo "Contents of area[51] are ${area[51]}."
  23 
  24 # 没有初始化内容的数组元素打印空值(NULL值).
  25 echo -n "area[43] = "
  26 echo ${area[43]}
  27 echo "(area[43] unassigned)"
  28 
  29 echo
  30 
  31 # 两个数组元素的和被赋值给另一个数组元素
  32 area[5]=`expr ${area[11]} + ${area[13]}`
  33 echo "area[5] = area[11] + area[13]"
  34 echo -n "area[5] = "
  35 echo ${area[5]}
例子:格式化一首诗
 1 #!/bin/bash
   2 # poem.sh: 排印出作者喜欢的一首诗.
   3 
   4 # 诗的行数 (一小节诗).
   5 Line[1]="I do not know which to prefer,"
   6 Line[2]="The beauty of inflections"
   7 Line[3]="Or the beauty of innuendoes,"
   8 Line[4]="The blackbird whistling"
   9 Line[5]="Or just after."
  10 
  11 # 出处.
  12 Attrib[1]=" Wallace Stevens"
  13 Attrib[2]="\"Thirteen Ways of Looking at a Blackbird\""
  15 
  16 echo
  17 
  18 for index in 1 2 3 4 5    # 5行.
  19 do
  20   printf "     %s\n" "${Line[index]}"
  21 done
  22 
  23 for index in 1 2          # 打印两行出处行.
  24 do
  25   printf "          %s\n" "${Attrib[index]}"
  26 done
  27 
  28 echo
  29 
  30 exit 0

Declare命令:

[test @test test]# declare [-afirx]
参数说明:
-a :定义为数组array
-f :定义为函数function 
-i :定义为整数integer
-r :定义为只读
-x :定义为环境变量
[test @test test]# declare -i a=3
[test @test test]# declare -i b=5
[test @test test]# declare -i c=$a*$b
[test @test test]# echo $c
15 

查看数组:

显示所有的数组元素:

#declare -p array
#echo ${array[*]}
#echo ${array[@]}

查看数组长度:

#echo ${#array[@]}
5

查看数组元素:按下标

# echo ${array[0]}
one

获取数组元素的索引(下标):

# echo ${!array[*]}
0 1 2 3 4 5

数组切片:

[root@wing shell]# echo ${array[*]:1}
two three four five six
[root@wing shell]# echo ${array[*]:1:3}   //从第2个元素开始往后打印3个
two three four

关联数组:

声明关联数组: 关联数组必须事先声明 declare -A 声明关联数组
# declare -A ass_array1
或者
# declare -A ass_array4='([index4]="bash shell" [index1]="tom" [index2]="jack" [index3]="alice" )'

方法一: 一次赋一个值
数组名[索引]=变量值
# ass_array1[index1]=pear
# ass_array1[index2]=apple
# ass_array1[index3]=orange
# ass_array1[index4]=peach

方法二: 一次赋多个值
# ass_array2=([index1]=tom [index2]=jack [index3]=alice [index4]='bash shell')

例:
# cat a.txt 
192.168.1.8
192.168.1.8
192.168.1.8
192.168.1.8
192.168.1.9
192.168.1.10

[root@wing shell]# cat c.sh
a=(`cat a.txt`)
declare -A b
for i in ${a[*]}
do
 	let b[$i]++
done
declare -p b
for i in ${!b[@]}
do
	echo ${i}':'${b[$i]}
done

# bash c.sh
declare -A b='([192.168.1.8]="4" [192.168.1.9]="1" [192.168.1.10]="1" )'
192.168.1.8:4
192.168.1.9:1
192.168.1.10:1

12,bash函数

函数

定义函数
调用函数
取消函数
函数传参
$1,$2
命名空间
local

 例如:
 #!/bin/bash
a=8
test(){
    local a                         //这个a和上面的a不一样,因为local隔离了a的命名空间
    echo $a
    a=9
    echo $a
}
echo $a
test
echo $a
   
结果:                             没有local就打印
      8                                  8
                                         8
      9                                  9
      8                                  9

返回值     return value   value 不能超过225

myfunc() //函数定义
{
echo “This is my first shell function”
}
myfunc //函数调用
unset myfunc //取消函数
myfunc(){
echo "This is a new function“
}
myfunc

例: //假如这个脚本是a.sh    可以通过 ./a.sh   /tmp/  查看/tmp/下面文件属性,相当于  ls -l  /tmp/
lsl(){
ls –l
}
cd "$1" && lsl

例:
readPass() {
PASS=""
echo -n "Entry Password: "
stty -echo
read PASS
stty echo
echo
}
readPass
echo Password is $PASS
  使用$1,$2传参 //这个脚本打印的是5
#!/bin/bash
test(){
     echo $1
}
   test 5    
使用$*传参
[root@wing shell]# cat a.sh
#!/bin/bash
a=(1 2 3 4 5)
hello(){
	for i in $*   #因为是一个数组,不知道有多少个参数,所以使用$*获取所有参数
	do
		echo $[$i*2]
	done
}
hello ${a[*]}

shift命令(补充了解)

位置参数可以用shift命令左移。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1、$2、$3丢弃,$0不移动。不带参数的shift命令相当于shift 1。
对于位置变量或命令行参数,其个数必须是确定的,或者当 Shell 程序不知道其个数时,可以把所有参数一起赋值给变量$*。
若用户要求 Shell 在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1 后为 $2,在 $2 后面为 $3 等,则需要用shift把所有参数变成$1

#测试 shift 命令(x_shift.sh)
until [ $# -eq 0 ]
do
echo "第一个参数为: $1 参数个数为: $#"
shift
done
执行以上程序x_shift.sh:
$./x_shift.sh 1 2 3 4

结果显示如下:
第一个参数为: 1 参数个数为: 4
第一个参数为: 2 参数个数为: 3
第一个参数为: 3 参数个数为: 2
第一个参数为: 4 参数个数为: 1

从上可知 shift 命令每执行一次,变量的个数($#)减一,而变量值提前一位

用 until 和 shift 命令计算所有命令行参数的和。
#shift 上档命令的应用(x_shift2.sh)
sum=0

until [ $# -eq 0 ]
do
sum=`expr $sum + $1`
shift
done
echo "sum is: $sum"

执行上述程序:
$x_shift2.sh 10 20 15

其显示结果为:
45

Shift 命令还有另外一个重要用途:
Bash 定义了9个位置变量,从 $1 到 $9,这并不意味着用户在命令行只能使用9个参数,借助 shift 命令可以访问多于9个的参数。

  Shift 命令一次移动参数的个数由其所带的参数指定。例如当 shell 程序处理完前九个命令行参数后,可以使用 shift 9 命令把 $10 移到 $1。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值