函数(函数返回值,函数参数),systemd,bash数组,bash字符串处理 第16天

回顾:

sed命令、bash脚本编程

sed命令:
sed的格式如下:
sed [options] 'SCRIPT' FILE...
sed的编辑命令有:d, p, w, r, a, i, c, s, =,
sed的高级编辑命令有  n, N, h, H, g, G, p, P, x, D

bash脚本编程:
while, for, case, until

case语句格式如下:
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
分支n
;;
esac

case支持glob风格的通配符:
*: 任意长度任意字符;
?: 任意单个字符;
[]:指定范围内的任意单个字符;
a|b: a或b


bash中的function:函数
过程式编程:代码重用
                    模块化编程
                    结构化编程

语法一:
function f_name
{
...函数体...
}

语法二:
f_name()
{
...函数体...
}

函数只有被调用才会执行;
调用:给定函数名,在函数名出现的地方,会被自动替换为函数代码;

函数的生命周期:被调用时创建,返回时终止;
return命令返回自定义状态结果,return 0表示成功,return 1-255表示失败

例子:添加用户
#!/bin/bash
#
function adduser
{
       if id $username &> /dev/null; then
           echo "$username exists."
           return 1
       else
           useradd $username
           [ $? -eq 0 ] && echo "Add $username finished." && return 0
       fi
}

for i in {1..10}; do
    username=myuser$i
    adduser   #这里是函数调用
done


示例:服务脚本
#!/bin/bash
#
# chkconfig: - 88 12
# description: test service script
#

prog=$(basename $0)
lockfile=/var/lock/subsys/$prog

start()
{
    if [ -e $lockfile ]; then
        echo "$prog is aleady running."
        return 0
    else
        touch $lockfile
        [ $? -eq 0 ] && echo "Starting $prog finished."
    fi
}

stop()
{
       if [ -e $lockfile ]; then
              rm -f $lockfile && echo "Stop $prog ok."
       else
              echo "$prog is stopped yet."
       fi
}

status()
{
       if [ -e $lockfile ]; then
              echo "$prog is running."
       else
              echo "$prog is stopped."
       fi
}

usage()
{
      echo "Usage: $prog {start|stop|restart|status}"
}


if [ $# -lt 1 ]; then
      usage
      exit 1
fi

case $1 in
start)
              start
              ;;
stop)
              stop
              ;;
restart)
              stop
              start
              ;;
status)
              status
              ;;
*)
              usage
esac

练习:打印九九乘法表,利用函数实现;

函数返回值:
函数的执行结果返回值:
    (1) 使用echo或print命令进行输出;
    (2) 函数体中调用命令的执行结果;
函数的退出状态码:
     (1) 默认取决于函数体中执行的最后一条命令的退出状态码,$?
     (2) 自定义退出状态码:return

函数可以接受参数:
传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“testfunc arg1 arg2 ...”

在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*(参数列表), $#(参数个数)等特殊变量;

示例:添加10个用户
#!/bin/bash
#
function adduser
{
     if [ $# -lt 1 ]; then
          return 2
          # 2: no arguments
     fi

     if id $1 &> /dev/null; then
          echo "$1 exists."
          return 1
     else
          useradd $1
          [ $? -eq 0 ] && echo "Add $1 finished." && return 0
     fi
}

for i in {1..10}; do
     adduser myuser$i
done


变量作用域:
本地变量:当前shell进程;为了执行脚本会启动专用的shell进程;因此,本地变量的作用范围是当前shell脚本程序文件;

局部变量:函数的生命周期;函数结束时变量被自动销毁;
如果函数中有局部变量,其名称同本地变量;
在函数中定义局部变量的方法:
local NAME=VALUE

例如:
#!/bin/bash
declare -i global_i=5    #本地变量,定义在函数之外,作用域是整个脚本
func()
{
    local local_i=90          #局部变量,定义在函数之内,作用域是整个func函数
    echo "func $global_i"
    echo "func $local_i"
}
echo "Main $global_i"


函数递归:
函数直接或间接调用自身;
N!=N(n-1)(n-2)...1
n(n-1)! = n(n-1)(n-2)!

例子:阶乘的例子
#!/bin/bash
#
fact() {
    if [ $1 -eq 0 -o $1 -eq 1 ]; then
         echo 1
    else
         echo $[ $1 * `fact $[$1-1]` ]
    fi
}
fact 5

练习:求n阶斐波那契数列;
#!/bin/bash
#
fab()
{
    if [ $1 -eq 1 ]; then
        echo 1
    elif [ $1 -eq 2 ]; then
        echo 1
    else
        echo $[ $(fab $[$1-1]) + $(fab $[$1-2]) ]
    fi
}
fab 7

Systemd:
程序的启动流程如下:
POST --> Boot Sequence --> Bootloader --> kernel + initramfs(initrd) --> rootfs --> /sbin/init

init程序在CentOS各个版本中是不同的:
CentOS 5中是SysV风格的 init
CentOS 6:  Upstart
CentOS 7: Systemd

Systemd新特性如下:
系统引导时实现服务并行启动;
按需激活进程;
系统状态快照;
基于依赖关系定义服务控制逻辑;

systemd的核心概念:unit(单元)
配置文件进行标识和配置;文件中主要包含了系统服务、监听socket、保存的系统快照以及其它与init相关的信息;保存至如下的目录中:
/usr/lib/systemd/system/
/run/systemd/system/
/etc/systemd/system/

unit的类型:
Service unit: 文件扩展名为.service, 用于定义系统服务;
     ls /usr/lib/systemd/system/*.service
Target unit: 文件扩展名为.target,用于模拟实现“运行级别”;
    ls /usr/lib/systemd/system/*.target
Device unit: 文件扩展名为.device, 用于定义内核识别的设备;
Mount unit: 文件扩展名为.mount, 定义文件系统挂载点;
Socket unit: 文件扩展名为.socket, 用于标识进程间通信用的socket文件;
Snapshot unit: 文件扩展名为.snapshot, 管理系统快照;
Swap unit: 文件扩展名为.swap, 用于标识swap设备;
Automount unit: 文件扩展名为.automount,文件系统的自动挂载点;
Path unit: .path,文件扩展名为.path,用于定义文件系统中的一个文件或目录;

关键特性:
基于socket的激活机制:socket与服务程序分离;
基于bus的激活机制:
基于device的激活机制:比如,某个设备挂载进系统时,激活相关服务
基于path的激活机制:目录或文件发生变化,激活相关服务
系统快照:保存各unit的当前状态信息于持久存储设备中;
向后兼容SysV init脚本;

不兼容:
systemctl命令固定不变
非由systemd启动的服务,systemctl无法与之通信

管理系统服务:service
CentOS 7: service类型的 unit
     注意:CentOS 7能兼容早期的服务脚本

命令:systemctl COMMAND name.service
例如  systemctl start httpd.service

启动:service name start ==> systemctl start name.service
停止:service name stop ==> systemctl stop name.service
重启:service name restart ==> systemctl restart name.service
状态:service name status ==> systemctl status name.service
条件式重启:service name condrestart ==> systemctl try-restart name.service
重载或重启服务:systemctl reload-or-restart name.service
      重载是不关闭应用的前提下重新读取配置文件
重载或条件式重启服务:systemctl reload-or-try-restart name.service
禁止设定为开机自启:systemctl mask name.service
取消禁止设定为开机自启:systemctl unmask name.service


查看某服务当前激活与否的状态:systemctl is-active name.service
查看所有已经激活的服务:systemctl list-units --type service
查看所有服务:systemctl list-units --type service --all

设定某服务开机自启:chkconfig name on ==> systemctl enable name.service
          开机禁止自启:chkconfig name off ==> systemctl disable name.service
查看所有服务的开机自启状态:chkconfig --list ==> systemctl list-unit-files --type service
查看服务是否开机自启:systemctl is-enabled name.service

其它命令:
查看服务的依赖关系:systemctl list-dependencies name.service

管理target类型的 units:
unit配置文件:.target
运行级别:
0  ==> runlevel0.target链接文件链接至poweroff.target
1  ==> runlevel1.target链接文件链接至rescue.target
2  ==> runlevel2.target链接文件链接至multi-user.target
3  ==> runlevel3.target链接文件链接至multi-user.target
4  ==> runlevel4.target链接文件链接至multi-user.target
5  ==> runlevel5.target链接文件链接至graphical.target
6  ==> runlevel6.target链接文件链接至reboot.target

级别切换:
init N ==> systemctl isolate name.target

查看级别:
runlevel ==> systemctl list-units --type target

获取默认运行级别:
/etc/inittab ==> systemctl get-default

修改默认级别:
/etc/inittab ==> systemctl set-default name.target

切换至紧急救援模式:
systemctl rescue

切换至emergency模式:
systemctl emergency

其它常用命令:
关机:systemctl halt、systemctl poweroff
重启:systemctl reboot
挂起:systemctl suspend
快照:systemctl hibernate
快照并挂起:systemctl hybrid-sleep

----------------------------------分割线----------------------------------------------------

回顾: bash脚本编程、systemd

函数:模块化编程,函数定义如下:
function f_name {
...函数体...
}

f_name() {
...函数体...
}

函数的返回:return命令


函数的参数:
函数体中调用参数:$1, $2, ...
$*, $@, $#

向函数传递参数:
函数名 参数列表



bash脚本编程:数组

变量:存储单个元素的内存空间;
数组:存储多个元素的连续的内存空间;
索引:编号从0开始,属于数值索引;
注意:索引也可支持使用自定义的格式,而不仅仅是数值格式;
bash的数组支持稀疏格式;
引用数组中的元素:${ARRAY_NAME[INDEX]}  花括号一般不可省略

声明数组:
declare -a ARRAY_NAME
declare -A ARRAY_NAME: 关联数组;key-value


数组元素的赋值:
(1) 一次只赋值一个元素;
ARRAY_NAME[INDEX]=VALUE
weekdays[0]="Sunday"
weekdays[4]="Thursday"
(2) 一次赋值全部元素:
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
(3) 只赋值特定元素:
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
(4) read -a ARRAY

引用数组元素:${ARRAY_NAME[INDEX]}
注意:省略[INDEX]表示引用下标为0的元素;

数组的长度(数组中元素的个数):${#ARRAY_NAME[*]}或者${#ARRAY_NAME[@]}


示例:生成10个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bash
#
declare -a rand
declare -i max=$RANDOM
declare -i min=$max
rand[0]=$max
echo "0:${rand[0]}"

for i in {1..9}; do
      rand[$i]=$RANDOM
      echo "$i:${rand[$i]}"
      [ ${rand[$i]} -gt $max ] && max=${rand[$i]}
      [ ${rand[$i]} -lt $min ] && min=${rand[$i]}
done

echo "Max: $max"
echo "Min:  $min"

练习:写一个脚本
定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件;要统计其下标为偶数的文件中的行数之和;
#!/bin/bash
#
declare -a files
files=(/var/log/*.log)   #初始化数组files
declare -i lines=0

for i in $(seq 0 $[${#files[*]}-1]); do
      if [ $[$i%2] -eq 0 ];then
            let lines+=$(wc -l ${files[$i]} | cut -d' ' -f1)
      fi
done

echo "Lines: $lines."

引用数组中的所有元素:${ARRAY[@]}, ${ARRAY[*]}

数组切片:${ARRAY[@]:offset:number}
offset: 要跳过的元素个数
number: 要取出的元素个数
取偏移量之后的所有元素:${ARRAY[@]:offset};

向数组中追加元素:ARRAY[${#ARRAY[*]}]=value ,追加到数组尾部

删除数组中的某元素:unset ARRAY[INDEX]

关联数组:key-value
declare -A ARRAY_NAME
ARRAY_NAME=([index_name1]='val1' [index_name2]='val2' ...)

练习:生成10个随机数,升序或降序排序;


bash的字符串处理工具:

字符串切片:
${var:offset:number}
取字符串的最右侧几个字符:${var: -lengh}
注意:冒号后必须有一空白字符;

基于模式取子串:
${var#*word}:其中word可以是指定的任意字符;功能:自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符之间的所有字符;

${var##*word}:同上,不过,删除的是字符串开头至最后一次由word指定的字符之间的所有内容;
file="/var/log/messages"
${file##*/}的结果是 messages

${var%word*}:其中word可以是指定的任意字符;功能:自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符之间的所有字符;
file="/var/log/messages"
${file%/*}的结果是/var/log

${var%%word*}:同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符;

示例:url=http://www.magedu.com:80
${url##*:}的结果是80
${url%%:*} 的结果是http

字符串的查找替换:
${var/pattern/substi}:查找var所表示的字符串中,第一次被pattern所匹配到的字符串,以substi替换之;
${var//pattern/substi}: 查找var所表示的字符串中,所有能被pattern所匹配到的字符串,以substi替换之;全局替换

${var/#pattern/substi}:查找var所表示的字符串中,行首被pattern所匹配到的字符串,以substi替换之;
${var/%pattern/substi}:查找var所表示的字符串中,行尾被pattern所匹配到的字符串,以substi替换之;

查找并删除:
${var/pattern}:查找var所表示的字符串中,删除第一次被pattern所匹配到的字符串
${var//pattern}:查找var所表示的字符串中,删除所有被pattern所匹配到的字符串
${var/#pattern}:查找var所表示的字符串中,删除行首被pattern所匹配到的字符串,
${var/%pattern}:查找var所表示的字符串中,删除行尾被pattern所匹配到的字符串,

字符大小写转换:
${var^^}:把var中的所有小写字母转换为大写;
${var,,}:把var中的所有大写字母转换为小写;

变量赋值:
${var:-value}:如果var为空或未设置,那么返回value;否则,则返回var的值;
${var:=value}:如果var为空或未设置,那么返回value,并将value赋值给var;否则,则返回var的值;

${var:+value}:如果var不空,则返回value;
${var:?error_info}:如果var为空或未设置,那么返回error_info;否则,则返回var的值;

为脚本程序使用配置文件:步骤如下
(1) 定义文本文件,每行定义“name=value”
(2) 在脚本中source此文件即可

命令:
mktemp命令:创建临时文件或临时目录
mktemp [OPTION]... [TEMPLATE]
    TEMPLATE使用filename.XXX,其中XXX至少要出现三个;
    -d: 创建临时目录;
    --tmpdir=/PATH/TO/SOMEDIR:指明临时文件或目录位置;
例如   mktemp  /tmp/somefile.XXXX

install命令:
install [OPTION]... [-T] SOURCE DEST        单个复制文件
install [OPTION]... SOURCE... DIRECTORY  复制多个文件到目录中
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...   创建空目录
选项:
-m MODE
-o OWNER
-g GROUP
-D
例如  install -m 700  -d  workspace  创建空目录workspace,并且指明权限为700
         install -D /usr/sbin/ss  /mnt/sysroot/usr/sbin/ss

练习:写一个脚本
(1) 提示用户输入一个可执行命令名称;
(2) 获取此命令所依赖到的所有库文件列表;
(3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下;
/bin/bash ==> /mnt/sysroot/bin/bash
/usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd
(4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下:
/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2

进一步地:每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出;

#!/bin/bash
#
if [ $# -ne 2 ] ; then
    echo "usage: `basename $0`  COMMAND  DIRECTORY"  # $1是命令,$2是一個目錄
    exit 1
fi

if [ ! -e $1 ];then
    echo "$1 is not a exists file"
    exit 2
fi

if [ ! -d $2 ];then
    echo "$2 is not a exists directory"
    exit 3
fi

declare -a dep_files  #dep_files 表示数组
declare -a dep_files_need
declare -i i=0
dep_files=(`ldd $1`)
#echo ${dep_files[*]}

for item in ${dep_files[*]};do
   if echo $item | grep -E "^/.*so\.[0-9]$" &> /dev/null ;then
      dep_files_need[i]=$item
      let i+=1
   fi
    # echo $item
done

echo ${dep_files_need[*]}

if [ ! -e $2$1 ];then
    install -D $1 $2$1
fi

for item in ${dep_files_need[*]};do
   if [ ! -e $2$item ];then
       install -D $item $2$item
   fi
done











评论

此博客中的热门博文

OAuth 2教程

网格策略

apt-get详细使用