概述:本章主要介绍bash编程中的函数和数组以及字符串处理和程序包管理工具等,来帮助小伙伴们更进一步的了解bash编程的内容和软件的安装、更新、卸载、查询等操作。
一、函数介绍
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程。
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分。
函数和shell程序比较相似,区别在于:
Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改
函数:function
过程式编程:代码重用
模块化编程
结构化编程
把一段独立功能的代码当作一个整体,并为之一个名字;命名的代码段,此即为函数;
注意:定义函数的代码段不会自动执行,在调用时执行;所谓调用函数,在代码中给定函数名即可;函数名出现的任何位置,在代码执行时,都会被自动替换为函数代码;
语法一:
function f_name {
...函数体...
}
语法二:
function f_name () {
...函数体...
}
语法三:
f_name() {
...函数体...
}
推荐使用第三种语法,简洁
函数使用
函数的定义和使用:
可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
调用:函数只有被调用才会执行;
调用:给定函数名
函数名出现的地方,会被自动替换为函数代码
函数的生命周期:每次被调用时创建,返回时终止
函数返回值
函数有两种返回值:
函数的执行结果返回值:
(1) 使用echo或printf命令进行输出
(2) 函数体中调用命令的输出结果
函数的退出状态码:
(1) 默认取决于函数中执行的最后一条命令的退出状态码
(2) 自定义退出状态码, 其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回。
return 1-255 有错误返回
遇到return函数生命周期就结束了
示例:给定一个用户名,去的用户名的第号和默认shell;
#!/bin/bash #userinfo(){ if id "$username" &> /dev/null;then grep "^$username\>" /etc/passwd |cut -d: -f3,7 else echo "No such user." fi } username=$1 userinfo username=$2 userinfo
#!/bin/bash # #chkconfig: - 50 50 #description:test service script prog=$(bashname $0) lockfile=/var/lock/subsys/$prog start() {} if [ -f $lockfile ];then echo "$prog is running yet." else touch $lockfile [ $? -eq 0 ] && echo "start $prog finshed." fi } stop(){ if [ -f $lockfile ];then rm -f $lockfile [ $? -eq 0 ] && echo "stop $prog finished." else echo "$prog is not running" fi } restart() { if [ -f $lockfile ];then rm -f $lockfile touch $lockfile echo "restart $prog finshed." else touch -f $lockfile echo "start $prog finshed." fi } status() { if [ -f $lockfile ];then echo "$prog is running." else echo "$prog is stoped." fi } usage() { echo "USage: $prog {start|stop|restart|status}" } case $1 in start) start ;; stop) stop ;; restatrt) stop start;; status) status;; *) usage exit 1 ;; esac
注意:
使函数文件可以将经常使用的函数存入函数文件,然后将函数文件载入shell。
文件名可任意选取,但最好与相关任务有某种联 系。例如: functions.main
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件。
创建函数文件
函数文件示例:
cat functions.main #!/bin/bash #functions.main findit() { if [ $# -lt 1 ] ; then echo "Usage:findit file" return 1 fi find / -name $1 –print }
载入函数
函数文件已创建好后,要将它载入shell
定位函数文件并载入shell的格式:
. filename 或 source filename
注意:此即<点> <空格> <文件名>
这里的文件名要带正确路径
示例:上例中的函数,可使用如下命令:
. functions.main
检查载入函数
使用set命令检查函数是否已载入。 set命令将在shell中显示
所有的载入函数。
示例:
#set findit=( ) { if [ $# -lt 1 ]; then echo "usage :findit file"; return 1 fi find / -name $1 -print }
执行shell函数
要执行函数,简单地键入函数名即可:
示例:
findit groups /usr/bin/groups /usr/local/backups/groups.bak
删除shell函数
现在对函数做一些改动。首先删除函数,使其对shell不可用
。使用unset命令完成此功能.
命令格式为:
unset function_name
实例:
$unset findit
再键入set命令,函数将不再显示
函数参数
函数可以接受参数:
传递参数给函数:调用函数时,在函数名后面以空白分隔
给定参数列表即可;例如“ testfunc arg1 arg2 ...”
在函数体中当中,可使用$1, $2, ...调用这些参数;还
可以使用$@, $*, $#等特殊变量
示例:添加10个用户,添加用户的功能使用函数实现,用户名作为参数传递给函数。
#!/bin/bash # addusers() { if id $1 &>/dev/null;then return 5 else useradd $1 retval=$? return=$retval fi } for i in {1..10};do addusers ${1}${i} retval=$? if [ $retval -eq 0 ];then echo "Add user ${1}${i} finished." elif [ $retval -eq 5 ];then echo "User ${1}${i} exists." else echo "Unkown Error." fi done
函数变量
变量作用域:
环境变量:当前shell和子shell有效
本地变量:只在当前shell进程有效,为执行脚本会启动
专用子shell进程;因此,本地变量的作用范围是当前shell脚本
程序文件,包括脚本中的函数。
局部变量:函数的生命周期;函数结束时变量被自动销毁
注意:如果函数中有局部变量,如果其名称同本地变量,使
用局部变量。
在函数中定义局部变量的方法
local NAME=VALUE
示例:
#!/bin/bash # name=tom setname() { local name=jerry echo "Function:$name" } setname echo "Shell:$name"
函数递归实例
函数递归:
函数直接或间接调用自身
注意递归层数
递归实例:
阶乘是基斯顿·卡曼于1808年发明的运算符号,是数学术语
一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且有0的阶乘为1。自然数n的阶乘写作n!。
n!=1×2×3×...×n。
阶乘亦可以递归方式定义: 0!=1, n!=(n-1)!×n。
n!=n(n-1)(n-2)...1
n(n-1)! = n(n-1)(n-2)!
示例1:求n的阶乘
#!/bin/bash # fact() { if [ $1 -eq 0 -o $1 -eq 1 ];then echo 1 else echo $[$1*$(face$[$1-1])] fi } fact $1
示例2:求斐波那契数列各代的值
#!/bin/bash # fab() { if [ $1 -eq 1 ];then echo -n "1 " elif [ $1 -eq 2 ];then echo -n "1" else echo "$[$(fab$[$1-1])+$(fab$[$1-2])] " fi } for i in $(seq 1 $1);do fab $i done echo
数组
变量:存储单个元素的内存空间
数组:存储多个元素的连续的内存空间,相当于多个变量的集合。
数组名和索引
数组名:整个数组只有一个名字;
索引:编号从0开始,属于数值索引
数组名[索引]
${ARRAY_NAME[INDEX]}
注意:bash4.0版本之后开始支持索引可支持使用自定义的格式(也可以是字符),而不仅是数值格式,即为关联索引,
bash的数组支持稀疏格式(索引不连续)
定义数组
声明数组:
declare -a ARRAY_NAME:声明索引数组
declare -A ARRAY_NAME:声明关联数组
关联数组(先声明再使用)
数组元素的赋值:
(1) 一次只赋值一个元素;
ARRAY_NAME[INDEX]=VALUE
例如:
weekdays[0]="Sunday" weekdays[4]="Thursday"
示例:
menu[0]=hsr
menu[1]=mckr
menu[2]=bjky
echo ${menu[*]}
(2) 一次赋值全部元素:
ARRAY_NAME=("VAL1" "VAL2" "VAL3" ...)
[root@localhost bin]# number=(1 2 5 6 7 9 10) [root@localhost bin]# echo ${number[*]} [root@localhost bin]# number2=({1..9}) [root@localhost bin]# echo ${number2[*]} 1 2 3 4 5 6 7 8 9
[root@localhost bin]# files=(/tmp/*) root@localhost bin]# echo ${files[*]} /tmp/1223 /tmp/123 /tmp/a /tmp/b /tmp/bb /tmp/file1 /tmp/hogsuspend /tmp/per5.sh /tmp/ssh-0gAHISs2wV2R /tmp/systemd-private-b1280e3fddc544dc9e3b2e371fce366f-colord.service-NshluI /tmp/systemd-private-b1280e3fddc544dc9e3b2e371fce366f-cups.service-fzhP58 /tmp/systemd-private-b1280e3fddc544dc9e3b2e371fce366f-rtkit-daemon.service-h7vLWl /tmp/systemd-private-b1280e3fddc544dc9e3b2e371fce366f-vmtoolsd.service-h4soY3 /tmp/tracker-extract-files.0 /tmp/VMwareDnD /tmp/vmware-root
[root@localhost bin]# files=({1,2}.) [root@localhost bin]# echo ${files[*]} 1. 2.
(3) 只赋值特定元素:
ARRAY_NAME=([0]="VAL1" [3]="VAL2" ...)
num=([0]=100 [2]=20 [10]=1000)
(4) 交互式数组值对赋值
read -a ARRAY
[root@localhost bin]# read -a num 1 3 5 7 78 96 1 3 4 8 [root@localhost bin]# echo ${num[*]} 1 3 5 7 78 96 1 3 4 8
引用数组
引用数组元素: ${ARRAY_NAME[INDEX]}
注意:省略[INDEX]表示引用下标为0的元素
数组的长度(数组中元素的个数):(变量也可以应用)
${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
示例:生成10个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bash declare -a rand declare -i max=0 for i in {0..9}; do rand[$i]=$RANDOM echo ${rand[$i]} [ ${rand[$i]} -gt $max ] && max=${rand[$i]} done echo "Max: $max"
练习
编写脚本,定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件;要统计其下标为偶数的文件中的行数之和
#!/bin/bash # declare -a files files=(/var/log/*.log) 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[*]}]
删除数组中的某元素:导致稀疏格式
unset ARRAY[INDEX]
关联数组:
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2‘...)
字符串处理
bash的字符串处理工具:
字符串切片:
${#var}:返回字符串变量var的长度
${var:offset}:返回字符串变量var中从第offset个字符后(不包括第offset个字符)的字符开始,到最后的部分,offset的取值在0 到 ${#var}-1 之间(bash4.2后,充许为负值)
${var:offset:number}:返回字符串变量var中从第offset个字符后(不包括第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
${var:=value}:如果var为空或未设置,那么返回value,并将value赋值给var;否则,则返回var的值
${var:?error_info}:如果var为空或未设置,那么返回error_info;否则,则返回var的值为脚本程序使用配置文件,实现变量赋值
(1) 定义文本文件,每行定义“ name=value”
(2) 在脚本中source此文件即可
高级变量用法-有类型变量
Shell变量一般是无类型的,但是bash Shell提供了declare和
typeset两个命令用于指定变量的类型,两个命令是完全等价
的
declare [选项] 变量名
-r 将变量设置为只读属性
-i 将变量定义为整型数
-a 将变量定义为数组
-f 显示此脚本前定义过的所有函数名及其内容
-F 仅显示此脚本前定义过的所有函数名
-x 将变量声明为环境变量
-l 将变量值转为小写字母
-u 将变量值转为大写字母
间接变量引用
如果第一个变量的值是第二个变量的名字,从第一个变量引
用第二个变量的值就称为间接变量引用
variable1=variable2
variable2=value
variable1的值是variable2,而variable2又是变量名,
variable2的值为value,间接变量引用是指通过variable1获
得变量值value的行为
间接变量引用
bash Shell提供了两种格式实现间接变量引用
eval tempvar=\$$variable1
tempvar=${!variable1}
示例:
[root@server ~]# N=NAME [root@server ~]# NAME=wangxiaochun [root@server ~]# N1=${!N} [root@server ~]# echo $N1 wangxiaochun [root@server ~]# eval N2=\$$N [root@server ~]# echo $N2 wangxiaochun
eval命令
eval命令将会首先扫描命令行进行所有的置换,然后再执
行该命令。该命令适用于那些一次扫描无法实现其功能的
变量。该命令对变量进行两次扫描。
示例:
[root@server ~]# V1=pwd [root@server ~]# echo $V1 pwd [root@server ~]# eval $V1 /root
创建临时文件
mktemp命令:创建的临时文件可避免冲突
mktemp [OPTION]... [TEMPLATE]
TEMPLATE: filename.XXX
X至少要出现三个
OPTION:
-d: 创建临时目录
-p DIR或--tmpdir=/DIR:指明临时文件所存放的目录位置
实例:
#mktemp /tmp/test.XXX #tmpdir=`mktemp –d /tmp/testdir.XXX` #mktemp --tmpdir=/testdir test.XXXXXX
安装复制文件
install命令:默认会给文件执行权限;用于源码安装
install [OPTION]... [-T] SOURCE DEST 单文件
install [OPTION]... SOURCE... DIRECTORY
install [OPTION]... -t DIRECTORY SOURCE...
install [OPTION]... -d DIRECTORY...创建空目录
选项:
-m MODE,默认755
-o OWNER
-g GROUP
-d 创建目录
示例:
install -m 700 -o wang -g admins file1 file2
install –m –d /testdir/installdir
bash如何展开命令行()流程
把命令行分成单个命令词
展开别名
展开大括号种的声明( {})
展开波浪符声明( ~)
命令替换$() 和 ``)
再次把命令行分成命令词
展开文件通配( *、 ?、 [abc]等等)
准备I/0重导向( <、 >)
运行命令
防止扩展
反斜线( \)会使随后的字符按原意解释
$ echo Your cost: \$5.00
Your cost: $5.00
加引号来防止扩展
单引号( ’)防止所有扩展
双引号( ”)也防止所有扩展,但是以下情况例外:
$(美元符号) - 变量扩展
`(反引号) - 命令替换
\(反斜线) - 禁止单个字符扩展
!(叹号) - 历史命令替换
bash的配置文件
按生效范围划分,存在两类:
全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置:
~/.bash_profile
~/.bashrc
注意:以上文件只针对登录用户有效
shell登录两种方式
交互式登录:
(1)直接通过终端输入账号密码登录;
(2)使用“ su - UserName” 切换的用户
执行顺序:
/etc/profile -->/etc/profile.d/*.sh-->~/.bash_profile -->~/.bashrc -->/etc/bashrc
非交互式登录:
(1)su UserName
(2)图形界面下打开的终端
(3)执行脚本
执行顺序:
~/.bashrc --> /etc/bashrc --> /etc/profile.d/*.sh
Profile类
按功能划分,存在两类:
profiile类和bashrc类
profile类:为交互式登录的shell提供配置
全局: /etc/profile, /etc/profile.d/*.sh
个人: ~/.bash_profile
功用:
(1) 用于定义环境变量
(2) 运行命令或脚本
Bashrc类
bashrc类:为非交互式和交互式登录的shell提供配置
全局: /etc/bashrc
个人: ~/.bashrc
功用:
(1) 定义命令别名和函数
(2) 定义本地变量
编辑配置文件生效
修改profile和bashrc文件后需生效
两种方法:
1重新启动shell进程
2 . 或source
例:
. ~/.bashrc
Bash 退出任务
保存在~/.bash_logout文件中(用户)
在退出登录shell时运行
用于
创建自动备份
清除临时文件
二、压缩和解压缩工具和程序包管理
压缩比
目的:时间 换 空间
CPU的时间--> 磁盘空间
服务器CPU时间一般很珍贵,所以不合适
桌面环境则CPU时间大量空闲,所以一般比较合适
compress/uncompreess
后缀名:.Z(早期使用,现在已经淘汰,了解即可)
compress [-dfvcVr] [-b maxbits] [file ...]
-d: 解压缩,相当于
-c: 结果输出至标准输出,不删除原文件
-v: 显示详情
uncompress 解压缩
zcat file.Z >file
压缩解压缩工具种类:
gzip/gunzip/zcat,后缀名.gz(使用最普遍)
bzip2/bunzip2,后缀名.bz2
xz/unxz,后缀名.xz(压缩比最大)
lzma/unlzma/lzcat
zip/unzip(既能归档又能压缩)
tar,cpio(归档打包)
1、gzip/gunzip/zcat:(需要识别后缀名)
标准格式:
gzip [ -acdfhlLnNrtvV19 ] [-S suffix] [ name ... ]
gunzip [ -acfhlLnNrtvV ] [-S suffix] [ name ... ]
zcat [ -fhLV ] [ name ... ] 查看创建临时压缩文件并查看内容
常用使用格式
gzip [OPTION]... FILE...
-d:解压缩,相当于gunzip;
-#:指定压缩比,默认是6,数字越大,压缩比越大(1-9)
-c:将压缩结果输出至标准输出;
可以使用:gzip -c FILE > /PATH/TO/SOMEFILE.gz既保留原文件又压缩
实例:
gzip -c messages >messages.gz
gzip -c -d messages.gz > messages
zcat messages.gz > messages
注意:zcat仅适合查看小文件
2、bzip2/bunzip2/bzcat
标准格式:
bzip2 [ -cdfkqstvzVL123456789 ] [ filenames ... ]
bunzip2 [ -fkvsVL ] [ filenames ... ]
bzcat [ -s ] [ filenames ... ] 查看创建临时压缩文件并查看内容
bzip2recover filename 修复数据的,但是修复能力有限
常用格式:
bzip2 [OPTION]... FILE...
-d:解压缩,相当于bunzip2;
-#:指定压缩比,默认是6,数字越大,压缩比越大(1-9)
-k:keep,保留原文件
3、xz/unxz/xzcat
lzma/unlzma/lzcat
xz [option]... [file]...
unxz is equivalent to xz --decompress.
xzcat is equivalent to xz --decompress --stdout. 查看创建临时压缩文件并查看内容
lzma is equivalent to xz --format=lzma.
unlzma is equivalent to xz --format=lzma --decompress.
lzcat is equivalent to xz --format=lzma --decompress --stdout.
xz [option]... [file]...
-d:解压缩,相当于unxz;
-#:指定压缩比,默认是6,数字越大,压缩比越大(1-9)
-k:keep,保留原文件
注意:以上压缩工具默认只能压缩文件,不能压缩目录
压缩能力:xz > bzip2 > gzip 但是bzip2算法极占资源
归档工具:tar,cpio
tar命令:
tar [OPTION...] [FILE]...
tar选项可以不带-
(1)创建归档
-c -f /PATH/TO/SOMEFILE.tar FILE...
-cf /PATH/TO/SOMEFILE.tar FILE...
归档路径 目标文件
-f -c 不可以,参数就变成-c的了
(2)展开归档
-xf /PATH/TO/SOMEFILE.tar 展开至当前目录
-xf /PATH/TO/SOMEFILE.tar -C /PATH/TO/SOMEDIR 展开至指定目录
(3)查看归档文件的文件列表
-tf /PATH/TO/SOMEFILE.tar
-tvf /PATH/TO/SOMEFILE.tar
归档完成后通常需要压缩,结合此前的压缩工具,就能实现压缩多个文件和目录
-j: bzip2, -z: gzip, -J: xz
(4)归档并压缩
-z:gzip2
创建归档并压缩
-zcf /PATH/TO/SOMEFILE.tar.gz FILE...
解压缩并展开归档
-zxf /PATH/TO/SOMEFILE.tar.gz
-xf /PATH/TO/SOMEFILE.tar.gz
-j:bzip2
创建归档并压缩
-jcf /PATH/TO/SOMEFILE.tar.gz FILE...
解压缩并展开归档
-jxf /PATH/TO/SOMEFILE.tar.bz2
-xf /PATH/TO/SOMEFILE.tar.bz2
-J:xz
创建归档并压缩
-Jcf /PATH/TO/SOMEFILE.tar.xz FILE...
解压缩并展开归档
-Jxf /PATH/TO/SOMEFILE.tar.xz
-xf /PATH/TO/SOMEFILE.tar.xz
注意:解压缩并展开归档时,可以省略z,j,J,直接使用xf,解压时可以自动识别文件压缩格式
cpio工具:复制 从或到文件
cpio命令是通过重定向的方式将文件进行打包备份,还原恢复的工具,它可以解压以“ .cpio”或者“ .tar”结尾的文件。
cpio [选项] > 文件名或者设备名
cpio [选项] < 文件名或者设备名
选项
-o 将文件拷贝打包成文件或者将文件输出到设备上
-i 解包,将打包文件解压或将设备上的备份还原到系统
-t 预览,查看文件内容或者输出到设备上的文件内容
-v 显示打包过程中的文件名称。
-d 解包生成目录,在cpio还原时,自动的建立目录
-c 一种较新的存储方式
zip:既能归档又能压缩
zip/unzip
后缀名;.zip
打包压缩
zip –r sysconfig.zip sysconfig/
解包解压缩
unzip sysconfig.zip
cat /var/log/messages | zip message -
unzip -p message > message
练习:下载redis-3.0.2.tar.gz ,展开至/tmp目录;而后得新创建压缩为xz格式;
tar zxfredis-3.0.2.tar.gz -C /tmp xz -k redis-3.0.2.tar
API:Application Progam Interface
API:Application Binary Interface
Unix-like,
ELF
windows
exe,msi
库级别的虚拟化:
linux:运行windows需要WinE
Windows:运行linux需要Cywin
系统级开发:
C/C++:http,sftpd,nginx
go
应用级开发:
java:hadoop,hbase(jvm)
Python:openstack,(pvm)
perl:(perl解释器)
ruby:(ruby)
php:(php)
C/C++程序格式:
源代码:文本格式的程序代码;
编译开发环境:编译器、头文件、开发库
二进制格式:文本格式的程序代码-->编译器-->二进制格式(二进制程序、库文件、配置文件、帮助文件)
java/python程序格式:
源代码:编译成能够在其虚拟机(jvm/pvm)运行的格式;
开发环境:编译器、开发库
二进制
项目构建工具:
C/C++:make
java:maven
程序包管理器:
源代码-->目标二进制格式-->组织成为一个或有限几个“包”文件;
安装、升级、卸载、查询、校验
程序包管理器:
debian:dpt,dpkg,".deb"
redhat:redhat package manager,rpm,".rpm" rpm is package manager
S.uS.E:rpm(但是和redhat不兼容(路径和组织格式不兼容)),".rpm"
Gentoo:ports
ArchLinux:(新兴轻量级)
源代码:name-version.tar.gz
VERSION:major.minor.release
主版本号(重大版本分支).次版本号(有一半以上修改).发型号(bug或代码改进)
rpm包命名格式:
name-VERSION-release.arch.rpm
VERSION:major.minor.release
release.arch:rpm包的发型号
release.os:2.el7.i386.rpm(第几次制作.应用平台.应用架构)
archetecture:i386,x64(mad64),ppc,noarch
常见的arch:
x86: i386, i486, i586, i686
x86_64: x64, x86_64, amd64
powerpc: ppc
跟平台无关: noarch
包:分类和拆包
Application-VERSION-ARCH.rpm: 主包
Application-devel-VERSION-ARCH.rpm 开发子包
Application-utils-VERSION-ARHC.rpm 其它子包
Application-libs-VERSION-ARHC.rpm 其它子包
包之间:可能存在依赖关系,甚至循环依赖
例如
x,y,z
x-->y,z
y-->A,B,C
C-->Y
解决包之间依赖关系所带来的诸多不便,而诞生了相应的前端工具
前端工具:自动解决依赖关系;
yum:rhel系列系统上rpm包管理器的前端工具;
apt-get(apt-cache):deb包管理器的前端工具;
zypper:suse的rpm管理器前端工具;
dnf:Fedor22+系统上rpm包管理器的前端工具;yum之后的新型前端工具
程序包管理器:
功能:将编译好的应用程序的各组成文件打包成一个或几个程序包文件,从而更方便地实现程序包的安装、升级、卸载和查询等管理操作;
1、程序包的组成清单(每个程序包的单独实现);
文件清单
安装和卸载时运行的脚本
2、数据库(公共)
程序包的名称和版本
依赖关系
功能说明
安装生成的个文件的文件路径及校验码信息;
查看二进制程序所依赖的库文件:
ldd /PATH/TO/BINARY_FILE
管理及查看本机装载的库文件:
ldconfig
/sbin/ldconfig -p: 显示本机已经缓存的所有可用库文件
名及文件路径映射关系
配置文件: /etc/ld.so.conf, /etc/ld.so.conf.d/*.conf
缓存文件: /etc/ld.so.cache
centos7 rpm数据库位置:/var/lib/rpm/
获取程序包的途径:
(1)系统发行版的光盘或官方的文件服务器(或镜像站点);
http://mirrors.aliyun.com;
http://mirrors.sohu.com;
http://mirrors.163.com;
(2) 项目的官方站点
www.Zabbix.com
httpd.apache.org
(3)第三方组织:
(a)EPEL
(b)Rpmforge:RHEL推荐,包很全
(c)搜索引擎
http://pkgs.org
http://rpmfind.net
http://rpm.pbone.net
(4) 自动动手
建议:检查其合法性
来源合法性;
程序包的完整性;
CentOS系统上rpm命令管理程序包:
安装、升级、卸载、查询和校验、数据库维护
rpm命令:rpm [OPTIONS] [PACKAGE_FILE]
安装:-i,--install
升级:-U,--update,-F,-freshen
卸载:-e,erase
查询:-q,--query
校验:-V,--verify
安装:
rpm {-i|--install} [install-options] PACKAGE_FILE ...
rpm -ivh PACKAGE_FILE ...
GENERAL OPTIONS:通用选项
-v:verbose,详细信息
-vv:更详细的输出
[install-options]
-h:hash marks输出进度条;每个#表示2%的进度;
-test:测试安装,但不真正执行安装; dry run模式;检查并报告依赖关系及冲突信息等
--nodeps:忽略依赖关系;不建议使用
--replacepkgs| replacefiles:重新安装(不需要卸载,不能替换配置文件)
--nosignature: 不检查来源合法性
--nodigest:不检查包完整性
注意:rpm可以自带脚本
四类: --noscripts:不执行程序包脚本
preinstall:安装过程开始之前运行的脚本;%pre,--nopre
postinstall:: 安装过程完成之后运行的脚本; %post,--nopost
preuninstall:: 卸载过程真正开始执行之前运行的脚本; %preun,--nopreun
postuninstall:: 卸载过程完成之后运行的脚本; %postun,--nopostun
升级:
rpm {-U|--upgrade} [install-options] PACKAGE_FILE...
rpm {-F|--freshen} [install-options] PACKAGE_FILE...
upgrade:安装有旧版程序包,则“升级”
如果不存在旧版程序包,则“安装”
freshen:安装有旧版程序包,则“升级”
如果不存在旧版程序包,则不执行升级操作
rpm -Uvh PACKAGE_FILE ...
rpm -Fvh PACKAGE_FILE ...
--oldpackage:降级
--force: 强行升级
注意:
(1) 不要对内核做升级操作; Linux支持多内核版本并存,因此,对直接安装新版本内核
(2) 如果原程序包的配置文件安装后曾被修改,升级时,新版本的提供的同一个配置文件并不会直接覆盖老版本的配置文件,而把新版本的文件重命名(FILENAME.rpmnew)后保留
卸载:
rpm {-e|--erase} [--allmatches] [--nodeps] [--noscripts] [--notriggers] [--test] PACKAGE_NAME ...
--allmatches:卸载所有匹配指定名称的程序包的各版本;
--nodeps:忽略依赖关系
--test:测试卸载,dry run模式
查询
rpm {-q|--query} [select-options] [query-options]
[select-options]
-a: 查询所有已经安装过的包
-f: 查看指定的文件由哪个程序包安装生成
-g:查询指定包组都有哪些包
-p rpmfile:针对尚未安装的程序包文件做查询操作;
--whatprovides CAPABILITY:查询指定的CAPABILITY由哪个包所提供
--whatrequires CAPABILITY:查询指定的CAPABILITY被哪个包所依赖
rpm2cpio 包文件|cpio –itv 预览包内文件
rpm2cpio 包文件|cpio –id “ *.conf” 释放包内文件
[query-options]
--changelog:查询rpm包的changelog
-c: 查询程序的配置文件
-d,--docfiles: 查询指定程序包的文档
-i: information:查询程序包相关的信息、版本号、大小、所属的包组等
-l,list: 查看指定的程序包安装后生成的所有文件;
--scripts:查询程序包自带的脚本片段
-R,--requires: 查询指定的程序包所依赖的CAPABILITY;
--provides: 列出指定程序包所提供的CAPABILITY;
PACKAGE_NAME:查询指定的程序包是否已经安装,及其版本;
查询用法:
-qi PACKAGE
-qf FILE
-qc PACKAGE
-ql PACKAGE
-qd PACKAGE
-qpi PACKAGE_FILE
-qpl PACKAGE_FILE
-qpc PACKAGE_FILE, ...
-qa
校验
rpm {-V|--verify} [select-options] [verify-options]
S file Size differsda 文件大小
M Mode differs (includes permissions and file type) 权限
5 digest (formerly MD5 sum) differs MD5码
D Device major/minor number mismatch 主次设备号
L readLink(2) path mismatch readLink路径
U User ownership differs 属主所有权
G Group ownership differs 属组所有权
T mTime differs 更新时间
P capabilities differ. 能力
包来源合法性验正及完整性验正:
完整性验正: SHA256
来源合法性验正: RSA
公钥加密:
对称加密:加密、解密使用同一密钥;
非对称加密:密钥是成对儿的
public key: 公钥,公开所有人
secret key: 私钥, 不能公开
过程
1、单向加密程序包获得特征码
2、用自己的私钥加密特征码
3、附加到程序后面
4、用公钥解密获得特征码
5、获取程序特征码与解密后的特征码比较
导入所需要公钥:
rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7 导入公钥
CentOS 7发行版光盘提供: RPM-GPG-KEY-CentOS-7
rpm -qa gpg-pubkey*
验证:
(1)安装此组织签名的程序时,会自动执行验证
(2)手动验证:rpm -K|checksig rpmfile 检查包的完整性和签名
rpm数据库
数据库重建:
/var/lib/rpm
rpm {--initdb|--rebuilddb} [-v] [--dbpath DIRECTORY] [--root DIRECTORY]
initdb: 初始化
如果事先不存在数据库,则新建之
否则,不执行任何操作
rebuilddb: 重建已安装的包头的数据库索引目录
获取帮助:
centos 6:man rpm
centos 7:man rpmdb
eg:
rpm --initdb --dbpath=/tmp/rpm
rpm --rebuilddb --dbpath=/tmp/rpm
yum
CentOS: yum, dnf
YUM: Yellowdog Update Modifier, rpm的前端程序,用来
解决软件包相关依赖性,可以在多个库之间定位软件包,
up2date的替代工具
yum repository: yum repo,存储了众多rpm包,以及
包的相关的元数据文件(放置于特定目录repodata下)
文件服务器:
ftp:// ftp服务器
http:// web服务器
file:/// 本地服务器
yum配置文件
yum客户端配置文件:
/etc/yum.conf:为所有仓库提供公共配置
/etc/yum.repos.d/*.repo:为仓库的指向提供配置
仓库指向的定义:
[repositoryID]
name=Some name for this repository
baseurl=url://path/to/repository/
enabled={1|0}
gpgcheck={1|0}
gpgkey=URL
enablegroups={1|0}
failovermethod={roundrobin|priority}
默认为: roundrobin,意为随机挑选;
cost= 默认为1000
yum命令的用法:
yum [options] [command] [package ...]
yum-config-manager
yum-config-manager --disable “仓库名" 禁用仓库
yum-config-manager --enable “仓库名” 启用仓库
显示仓库列表:
repolist [all|enabled|disabled]
显示程序包:
list
# yum list [all | glob_exp1] [glob_exp2] [...]
# yum list {available|installed|updates} [glob_exp1]
[...]
安装程序包:
install package1 [package2] [...]
reinstall package1 [package2] [...] (重新安装)
yum
升级程序包:
update [package1] [package2] [...]
downgrade package1 [package2] [...] (降级)
检查可用升级:
check-update
卸载程序包:
remove | erase package1 [package2] [...]
查看程序包information:
info [...]
查看指定的特性(可以是某文件)是由哪个程序包所提供:
provides | whatprovides feature1 [feature2] [...]
清理本地缓存:
clean [ packages | metadata | expire-cache |
rpmdb | plugins | all ]
构建缓存:
makecache
搜索: search string1 [string2] [...]
以指定的关键字搜索程序包名及summary信息
查看指定包所依赖的capabilities:
deplist package1 [package2] [...]
查看yum事务历史:
history [info|list|packages-list|packages-info|
summary|addon-info|redo|undo|
rollback|new|sync|stats]
yum history
yum history info 6
yum history undo 6
日志 : /var/log/yum.log
安装及升级本地程序包:
localinstall rpmfile1 [rpmfile2] [...]
(用install替代)
localupdate rpmfile1 [rpmfile2] [...]
(用update替代)
包组管理的相关命令:
groupinstall group1 [group2] [...]
groupupdate group1 [group2] [...]
grouplist [hidden] [groupwildcard] [...]
groupremove group1 [group2] [...]
groupinfo group1 [...]
如何使用光盘当作本地yum仓库:
(1) 挂载光盘至某目录,例如/media/cdrom
# mount /dev/cdrom /media/cdrom
(2) 创建配置文件
[CentOS7]
name=
baseurl=
gpgcheck=
enabled=
yum的命令行选项:
--nogpgcheck:禁止进行gpg check
-y: 自动回答为“ yes”
-q:静默模式
--disablerepo=repoidglob:临时禁用此处指定的repo
--enablerepo=repoidglob:临时启用此处指定的repo
--noplugins:禁用所有插件
yum仓库
yum的repo配置文件中可用的变量:
$releasever: 当前OS的发行版的主版本号
$arch: 平台, i386,i486,i586,x86_64等
$basearch:基础平台; i386
$YUM0-$YUM9:自定义变量
创建yum仓库:
createrepo [options]
程序包编译安装:
Application-VERSION-release.src.rpm --> 安装后,使
用rpmbuild命令制作成二进制格式的rpm包,而后再安装
源代码-->预处理-->编译(gcc)-->汇编-->链接-->执行
源代码组织格式:
多文件:文件中的代码之间,很可能存在跨文件依赖关系
C、 C++: make (项目管理器, configure -->
Makefile.in --> makefile)
java: maven
编译安装
C代码编译安装三步骤:
1、 ./configure:
(1) 通过选项传递参数,指定启用特性、安装路径等;执
行时会参考用户的指定以及makefile.in文件生成makefile
(2) 检查依赖到的外部环境
2、 make:根据makefile文件,构建应用程序
3、 make install:复制文件到相应路径
开发工具:
autoconf: 生成configure脚本
automake:生成Makefile.in
注意:安装前查看INSTALL, README
select
select 循环与菜单
select variable in list
do
循环体命令
done
select 循环主要用于创建菜单,按数字顺序排列的
菜单项将显示在标准错误上,并显示 PS3 提示符,
等待用户输入
用户输入菜单列表中的某个数字,执行相应的命令
用户输入被保存在内置变量 REPLY 中。
select 与 case
select 是个无限循环,因此要记住用 break 命令退
出循环,或用 exit 命令终止脚本。也可以按 ctrl+c
退出循环。
select 经常和 case 联合使用
与 for 循环类似,可以省略 in list , 此时使用位置参量
作业:
1、1、编写服务脚本/root/bin/testsrv.sh,完成如下要求
(1) 脚本可接受参数:start, stop, restart, status
(2) 如果参数非此四者之一,提示使用格式后报错退出
(3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功”
考虑:如果事先已经启动过一次,该如何处理?
(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成”
考虑:如果事先已然停止过了,该如何处理?
(5) 如是restart,则先stop, 再start
考虑:如果本来没有start,如何处理?
(6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示“SCRIPT_NAMEis running...”
如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...”
其中:SCRIPT_NAME为当前脚本名
#bin/bash # 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 "Staring $prog finished." fi } stop() { if [ -e $lockfile ];then rm -f $loackfile &&echo "Stop $prog ok." else echo "$prog is stopped yet." } status() { if [ -f $lockfile ];then echo "$prog is running." else echo "$prog is stoped." fi } usage() { echo "USage: $prog {start|stop|restart|status}" } case $1 in start) start ;; stop) stop ;; restatrt) stop start;; status) status;; *) usage exit 1 ;; esac
2、编写脚本/root/bin/copycmd.sh
(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
(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出
#!/bin/bash ch_root="/mnt/sysroot" [ ! -d $ch_root ] && mkdir $ch_root bincopy() { if which $1 &>/dev/null;then local cmd_path=`which --skip-alias $1` local bin_dir=`dirname $cmd_path` [ -d ${ch_root}${bin_dir} ] ||mkdir -p ${ch_root}${bin_dir} [ -f ${ch_root}${cmd_dir} ] ||cp $cmd_path ${ch_root}${bin_dir} return 0 else echo "Command not found." return 1 fi } libcopy() { local lib_list=$(ldd `which --skip-alias $1` |grep -Eo '/[^[:space:]]+') for loop in $lib_list;do local lib_dir=`dirname $loop` [ -d ${ch_root}${lib_dir} ] ||mkdir -p ${ch_root}${lib_dir} [ -f ${ch_root}${loop} ] || cp $loop ${ch_root}${lib_dir} done } read -p "Please input a command:" command while [ "$command" !="quit" ];do if bincopy $command;then libcopy $command fi read -p "Please input a command or quit:" command done
3、汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
利用函数,实现N片盘的汉诺塔的移动步骤
#!/bin/bash # step=0 move() { let step++ echo "$step: move disk $1 $2-------->$3" } hanoi() { if [ $1 -eq 1 ];then move $1 $2 $4 else hanoi "$[$1-1]" $2 $4 $3 move $1 $2 $4 hanoi "$[$1-1]" $3 $2 $4 fi } read -p "please input the number of plates:" number hanoi $number A B C
或者
#!/bin/bash # step=0 hanoi() { [[ ! $1 =~ ^[1-9][]0-9]*$ ]]&&echo "Error! please input a positive interger" && exit if [ $1 -eq 1 ];then let step++ echo "$step: move disk $1 $2-------->$3" else hanoi "$[$1-1]" $2 $4 $3 let step++ echo "$step: move disk $1 $2-------->$3" hanoi "$[$1-1]" $3 $2 $4 fi } read -p "please input the number of plates:" number hanoi $number A B C