8月22日shell脚本编程之循环和函数

shell脚本编程

本章内容

编程基础
脚本基本格式
变量
运算
条件测试
流程控制
函数
数组
高级字符串操作
高级变量
配置用户环境

编程基础

程序:指令+数据
编程程序风格:
  过程式:以指令为中心,数据服务于指令
  对象式:以数据为中心,指令服务于数据
shell程序:提供了编程能力,解释执行

程序的执行方式

     计算机:运行二进制指令;
     编程语言:
    低级:汇编
    高级:
    编译:高级语言-->编译器-->目标代码
    java,C#
    解释:高级语言-->解释器-->机器代码
    shell, perl, python

编程基本概念

 编程逻辑处理方式:
顺序执行
循环执行
选择执行
 shell编程:过程式、解释执行
编程语言的基本结构:
数据存储:变量、数组
表达式: a + b
语句:if

shell脚本基础

 shell脚本是包含一些命令或声明,并符合一定格式的文
本文件
 格式要求:首行shebang机制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
 shell脚本的用途有:
 自动化常用命令
 执行系统管理和故障排除
 创建简单的应用程序
 处理文本或文件

创建shell脚本

 第一步:使用文本编辑器来创建文本文件
 第一行必须包括shell声明序列: #!
#!/bin/bash
 添加注释
注释以#开头
 第二步:运行脚本
 给予执行权限,在命令行上指定脚本的绝对或相对路径
 直接运行解释器,将脚本作为解释器程序的参数运行

脚本调试

bash -n /path/to/some_script
  检测脚本中的语法错误

bash -x /path/to/some_script
  调试执行

变量

 变量:命名的内存空间
数据存储方式:
字符:
数值:整型,浮点型
 变量:变量类型
作用:
1、数据存储格式
2、参与的运算
3、表示的数据范围
类型:
字符
数值:整型、浮点型

编程程序语言分类

 强类型:定义变量时必须指定类型、参与运算必须符合类型
要求;调用未声明变量会产生错误
如 java,python
 弱类型:无须指定类型,默认均为字符型;参与运算会自动
进行隐式类型转换;变量无须事先定义可直接调用
如: bash 不支持浮点数
 变量命名法则:
1、不能使程序中的保留字:例如if, for;
2、只能使用数字、字母及下划线,且不能以数字开头
3、见名知义
4、统一命名规则:驼峰命名法

bash中变量的种类

根据变量的生效范围等标准:
本地变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子进程均无效
环境变量:生效范围为当前shell进程及其子进程
局部变量:生效范围为当前shell进程中某代码片段(通常指函数)
位置变量:$1,$2..来表示,用于让脚本在脚本代码中调试通过命令行传递给它的参数
特殊变量:$?,$0,$*,$@,$#

本地变量

 变量赋值: name=‘value’,
 可以使用引用value:
(1) 可以是直接字串; name=“root"
(2) 变量引用: name="$USER"
(3) 命令引用: name=`COMMAND`, name=$(COMMAND)
 变量引用: ${name}, $name
"":弱引用,其中的变量引用会被替换为变量值
'':强引用,其中的变量引用不会被替换为变量值,而保
持原字符串
 显示已定义的所有变量: set
 删除变量: unset name

环境变量

 变量声明、赋值:
export name=VALUE
declare -x name=VALUE
 变量引用: $name, ${name}
 显示所有环境变量:
export
env
printenv
 删除: unset name
 bash有许多内建的环境变量: PATH, SHELL, USRE,UID,
HISTSIZE, HOME, PWD, OLDPWD, HISTFILE, PS1

只读和位置变量

只读变量:只能声时,但不能修改和删除
   readonly name
   declare -r name
位置变量:在脚本代码中调用通过命令行传递给脚本的参数
$1,$2..:对应第一、第二等参数,shift[n]换位置
$0:命令本身
$*:传递给脚本的所有参数,全部参数合为一个字符串
$@:传递给脚本的所有参数,每个参数为独立字符串
$#:传递给脚本的参数的个数
      $@,$*只在被双引号包起来的时候才会有差异
示例:判断给出的文件的行数
linecount="$(wc -l $1| cut -d' ' -f1)"
ho "$1 has $linecount lines."

算术运算

bash中的算术运算:help let
    +,-,*,/,%(取模),**(乘方)
实现算术运算:
(1)let var=算术表达式
(2)var=$[算术表达式]
(3)var=$((算术表达式))
(4)var=$(expr arg1 arg2 arg3..)
(5)declare -i var =数值
(6)echo '算术表达式'|bc
乘法符号有些场景中需要转义,如*
bash有内建的随机数生成器: $RANDOM( 1-32767)
  echo $[$RANDOM%50] : 0-49之间随机数

赋值

增强型赋值:
+=,-=,*=,/=,%=
let varOPERvalue
    例如:let count+=3
          自加3后自赋值
自增,自减:
let var+=1
let var++
let var-=1
let var--

逻辑运算

true,false
  1     0
与:
1 与 1 = 1
1 与 0 = 0
0 与 1 = 0
0 与 0 = 0
或:
1 或 1 = 1
1 或 0 = 1
0 或 1 = 1
0 或 0 = 0
非:!
! 1 = 0
! 0 = 1
短路运算:
短路与:
第一个为0,结果必定为0;
第一个为1,第二个必须要参与运算;
短路或:
第一个为1,结果必定为1;
第一个为0,第二个必须要参与运算;
异或: ^
异或的两个值,相同为假,不同为真

聚集命令

有两种聚集命令的方法:
复合式:date;who |wc -l
命令会一个接一个地运行
子shell:(date;who |wc -l) >> /tmp/trace
所有的输出都被发送给单个STDOUT和STDERR

退出状态

进程使用退出状态来报告成功或失败
 0 代表成功,1-255代表失败
 $?变量保存最近的命令退出状态
例如:
$ ping -c1 -W1 hostname &> /dev/null
$ echo $?

退出状态码

bash自定义退出状态码
exit [n]:自定义退出状态码;
注意:脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
注意:如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

条件测试

 判断某需求是否满足,需要由测试机制来实现;
专用的测试表达式需要由测试命令辅助完成测试过程;
 评估布尔声明,以便用在条件性执行中
• 若真,则返回0
• 若假,则返回1
 测试命令:
• test EXPRESSION
• [ EXPRESSION ]
• [[ EXPRESSION ]]
注意: EXPRESSION前后必须有空白字符

条件性的执行操作符

 根据退出状态而定,命令可以有条件地运行
• && 代表条件性的AND THEN
• || 代表条件性的OR ELSE
 例如:
$ grep -q no_such_user /etc/passwd \
|| echo 'No such user'
No such user
$ ping -c1 -W2 station1 &> /dev/null \
> && echo "station1 is up" \
> || (echo 'station1 is unreachable'; exit 1)
station1 is up

test命令

 长格式的例子:
$ test "$A" == "$B" && echo "Strings are equal"
$ test “$A” -eq “$B” \
&& echo "Integers are equal"
 简写格式的例子:
$ [ "$A" == "$B" ] && echo "Strings are equal"
$ [ "$A" -eq "$B" ] && echo "Integers are equal"

bash的测试类型

数值测试:
-eq:是否等于
-ne:是不等于否
-gt:是否大于
-ge:是否大于等于
-lt:是否小于
-le:是否小于等于

bash的测试类型

 字符串测试:
==:是否等于;
>: ascii码是否大于ascii码
<: 是否小于
!=: 是否不等于
=~:左侧字符串是否能被右侧的PATHERN所匹配
注意:此表达式一般用于[[  ]]中;
-z "STRING":字符串是否为空,空为真,不空为假
-n "STRING":字符串是否不空,不空为真,空为假
注意:用于字符串比较时用到的操作数都应该使用引号

文件测试

 存在性测试
-a FILE:同-e
-e FILE: 文件存在性测试,存在为真,否则为假;
 存在性及类别测试
-b FILE:是否存在且为块设备文件;
-c FILE:是否存在且为字符设备文件;
-d FILE:是否存在且为目录文件;
-f FILE:是否存在且为普通文件;
-h FILE 或 -L FILE:存在且为符号链接文件;
-p FILE:是否存在且为命名管道文件;
-S FILE:是否存在且为套接字文件;
 文件权限测试:
-r FILE:是否存在且可读
-w FILE: 是否存在且可写
-x FILE: 是否存在且可执行
 文件特殊权限测试:
-g FILE:是否存在且拥有sgid权限;
-u FILE:是否存在且拥有suid权限;
-k FILE:是否存在且拥有sticky权限;
 文件大小测试:
-s FILE: 是否存在且非空;
 文件是否打开:
-t fd: fd表示文件描述符是否已经打开且与某终端相关
-N FILE:文件自动上一次被读取之后是否被修改过
-O FILE:当前有效用户是否为文件属主
-G FILE:当前有效用户是否为文件属组
 双目测试:
FILE1 -ef FILE2: FILE1与FILE2是否指向同一个设
备上的相同inode
FILE1 -nt FILE2: FILE1是否新于FILE2;
FILE1 -ot FILE2: FILE1是否旧于FILE2;

组合测试条件

 第一种方式:
COMMAND1 && COMMAND2 并且
COMMAND1 || COMMAND2 或者
! COMMAND 非
如: [ -e FILE ] && [ -r FILE ]
 第二种方式:
EXPRESSION1 -a EXPRESSION2 并且
EXPRESSION1 -o EXPRESSION2 或者
! EXPRESSION

使用read命令来接收输入

使用read来把输入值分配给一个或多个shell变量:
-p 指定要显示的提示
-t TIMEOUT  多久以后没输入命令自动退出
read 从标准输入中读取值,给每个单词分配一个变量,所有剩余的单词都被分配给最后一个变量
read -p “Enter a filename: “ FILE

流程控制

         过程式编程语言:
        顺序执行
        选择执行
        循环执行
    ###条件选择if语句
     选择执行:
     注意: if语句可嵌套
     单分支
    if 判断条件: then
         条件为真的分支代码
    fi
     双分支
    if 判断条件; then
        条件为真的分支代码
    else
        条件为假的分支代码
    fi
     多分支
    if CONDITION1; then
         if-true
    elif CONDITION2; then
         if-ture
    elif CONDITION3; then
         if-ture
    ...
    else
         all-false
    fi
     逐条件进行判断,第一次遇为“真”条件时,执行其分支,
    而后结束整个if语句
    条件判断:case语句
    case 变量引用 in
    PAT1)
         分支1
         ;;
    PAT2)
         分支2
         ;;
    ...
    *)
         默认分支
         ;;
    esac
    case支持glob风格的通配符:
    *: 任意长度任意字符
    ?: 任意单个字符
    []:指定范围内的任意单个字符
    a|b: a或b

循环

 循环执行
将某代码段重复运行多次
重复运行多少次:
循环次数事先已知
循环次数事先未知
有进入条件和退出条件
 for, while, until

for循环

 for 变量名 in 列表;do
      循环体
  done
 执行机制:
依次将列表中的元素赋值给“变量名” ; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
 列表生成方式:
(1) 直接给出列表
(2) 整数列表:
(a) {start..end}
(b) $(seq [start [step]] end)
(3) 返回列表的命令
$(COMMAND)
(4) 使用glob, 如: *.sh
(5) 变量引用;
$@, $*

while循环

 while CONDITION; do
       循环体
done
 CONDITION:循环控制条件;进入循环之前,先做一次判
断;每一次循环之后会再次做判断;条件为“ true”,则执行
一次循环;直到条件测试状态为“ false”终止循环
 因此: CONDTION一般应该有循环控制变量;而此变量的值
会在循环体不断地被修正
 进入条件: CONDITION为true;
 退出条件: CONDITION为false

until循环

 until CONDITION; do
        循环体
 done
 进入条件: CONDITION 为false
 退出条件: CONDITION 为true

循环控制语句continue

 用于循环体中
 continue [N]:提前结束第N层的本轮循环,而直接进入下一
轮判断;最内层为第1层
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
    continue
fi
CMDn
...
done

循环控制语句break

 用于循环体中
 break [N]:提前结束第N层循环, 最内层为第1层
while CONDTIITON1; do
       CMD1
...
if CONDITION2; then
       break
fi
      CMDn
...
done

创建无限循环

 while true; do
    循环体
 done
 until false; do
    循环体
 Done

特殊用法

 while循环的特殊用法(遍历文件的每一行):
while read line; do
      循环体
done < /PATH/FROM/SOMEFILE
 依次读取/PATH/FROM/SOMEFILE文件中的每一行,且将
行赋值给变量line
 双小括号方法,即((…))格式,也可以用于算术运算
 双小括号方法也可以使bash Shell实现C语言风格的变量操作
#I=10
#((I++))
 for循环的特殊格式:
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
    循环体
done
 控制变量初始化:仅在运行到循环代码段时执行一次
 控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断

select循环与菜单

select variable in list
do
循环体命令
done
select 循环主要用于创建菜单,按数字顺序排列的
菜单项将显示在标准错误上,并显示 PS3 提示符,等待用户输入
 用户输入菜单列表中的某个数字,执行相应的命令
 用户输入被保存在内置变量 REPLY 中。

select与case

select 是个无限循环,因此要记住用 break 命令退
出循环,或用 exit 命令终止脚本。也可以按 ctrl+c退出循环。
select 经常和 case 联合使用
与 for 循环类似,可以省略 in list , 此时使用位置
参量

函数介绍

1、函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程
2、它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell序的一部分。
3、函数与shell程序比较相似,区别在于:
 Shell程序在子Shell中运行
 而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改

定义函数

 函数由两部分组成:函数名和函数体。
 语法一:
function f_name {
     ...函数体...
}

函数使用

函数的定义和使用:
    可在交互式环境下定义函数
    可将函数放在脚本文件中作为它的一部分
    可放在只包含函数的单独文件中
调用:函数只有被调用才会执行;
    调用:给定函数名
    函数名出现的地方,会被自动替换为函数代码
函数的生命周期:被调用时创建,返回时终止

函数返回值

函数有两种返回值;
函数的执行结果返回值:
   (1)使用echo或printf命令进行输出
   (2)函数体中调用命令的输出结果
函数的退出状态码:
   (1)默认取决于函数中执行的最后一条命令的退出状态码
   (2)自定义退出状态码,其格式为:
  return  从函数中返回,用最后状态命令决定返回值
  return 0 无错误返回
  return 1-255 有错误返回

交互式环境下定义和使用函数

 示例:
$dir() {
> ls -l
> }
 定义该函数后,若在$后面键入dir,其显示结果同ls -l的
作用相同。
$dir
 该dir函数将一直保留到用户从系统退出,或执行了如下
所示的unset命令:
$ unset dir

在脚本中定义及使用函数

函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用
调用函数仅使用其函数名即可。
示例:
$cat func1
#!/bin/bash
# func1
hello()
{
echo "Hello there today's date is `date +%F`"
}
echo "now going to the function hello"
hello
echo "back from the function"

使用函数文件

可以经常使用的函数存入函数文件,然后将函数文件载入shell。
文件名可任意选取,但最好与相关任务有某种联系。例如:function.main
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入次文件。

创建函数文件

$cat func1
#!/bin/bash
# func1
hello()
{
echo "Hello there today's date is `date +%F`"
}
echo "now going to the function hello"
hello
echo "back from the function"

载入函数

 函数文件已创建好后,要将它载入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, ...调用这些参数;还可以使用$@, $*, $#等特殊变量

函数变量

 变量作用域:
环境变量:当前shell和子shell有效
本地变量:只在当前shell进程有效,为执行脚本会启动
专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数。
局部变量:函数的生命周期;函数结束时变量被自动销毁
 注意:如果函数中有局部变量,如果其名称同本地变量, 使用局部变量。
 在函数中定义局部变量的方法
local NAME=VALUE

函数递归示例

 函数递归:
函数直接或间接调用自身
注意递归层数
 递归实例:
阶乘是基斯顿·卡曼于 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)!

 示例: fact.sh
#!/bin/bash
#
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])]
fi
}
fact 5

作业

1、打印九九乘法表

8月22日shell脚本编程之循环和函数

8月22日shell脚本编程之循环和函数

2、利用变量RANDOM生成10个随机数字,输出这个10数字,并显示其中的最大者和最小者

3、打印国际象棋棋盘

8月22日shell脚本编程之循环和函数

4、写个脚本:

*




8月22日shell脚本编程之循环和函数

5、扫描/etc/passwd文件每一行,如发现GECOS字段为空,则填充用户名和单位电话为62985600,并提示该用户的GECOS信息修改成功

8月22日shell脚本编程之循环和函数

6函数

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/SCRIPTNAME文件存在,则显示“ SCRIPTNAME is running…”如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“ SCRIPTNAMEis stopped…”其中: SCRIPTNAME为当前脚本名

(7)在所有模式下禁止启动该服务,可用chkconfig 和 service命令管理

      1 #!/bin/bash
      2 #
      3 #
      4 cat << EOF
      5 start
      6 stop
      7 restart
      8 status
      9 ==================
     10 EOF
     11 
     12 read -p "please input options:" option
     13 
     14 while [ "$option" != "start" -a "$option" != "stop" -a "$option" != "restart" -a "$option" != "status" ];do
     15    read -p "please input options again:" option
     16 done
     17 
     18 file=/var/lock/subsys/SCRIPT_NAME
     19 
     20 start(){
     21     if [[ -f $file ]];then
     22          echo "this file has been exist"
     23     else
     24        touch $file
     25        echo "start success"
     26     fi
     27 }
     28 
     29 stop(){
     30    if [[ -f $file ]];then
     31        rm -f $file
     32        echo "stop success"
     33    else
     34       echo "this file has been disappion"
     35    fi
     36 }
     37 
     38 status(){
     39     if [ -z $file ];then
     40        echo "SCRIPT_NAME is running"
     41     else
     42        echo "SCRIPT_NAME is stopping"
     43     fi
     44 }
     45 
     46 case $option in
     47 start)
     48     start
     49     ;;
     50 stop)
     51     stop
     52     ;;
     53 restart)
     54     start
     55     stop
     56     ;;
     57 *)
     58     status
     59     ;;
     60 esac
     61 
     62

8月22日shell脚本编程之循环和函数

8月22日shell脚本编程之循环和函数

7、写一个函数实现两个数字做为参数,返回最大值

8月22日shell脚本编程之循环和函数

8月22日shell脚本编程之循环和函数

8、斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列: 0、 1、 1、 2、 3、 5、 8、 13、 21、 34、 ……,斐波纳契数列以如下被以递归的方法定义: F( 0) =0, F( 1) =1, F( n) =F(n-1)+F(n-2)( n≥2)写一个函数,求n阶斐波那契数列

8月22日shell脚本编程之循环和函数

8月22日shell脚本编程之循环和函数

原创文章,作者:15152188070,如若转载,请注明出处:http://www.178linux.com/38938

(0)
1515218807015152188070
上一篇 2016-08-24
下一篇 2016-08-24

相关推荐

  • FHS文件系统各目录功能

    FHS文件系统各目录功能 摘要: 本文主要介绍Linux的文件组织目录结构。 一、Linux 目录结构 在讲 Linux 目录结构之前,你首先要清楚一点东西,那就是 Linux 的目录与 Windows 的目录的区别,或许对于一般操作上的感受来说没有多大不同,但从它们的实现机制来说是完全不同的。 一种不同是体现在目录与存储介质(磁盘,内存,DVD 等)的关系…

    Linux干货 2016-10-18
  • raid介绍

    raid介绍 在生产环境中,服务器所需的存储容量比较大,一般都会选择磁盘阵列来走存储。 阵列的优点 多个磁盘合成一个“阵列”来提供更好的性能、冗余,或者两者都提供 首先在I/O能力上,多个磁盘并行读写,可以提高读写的速度。 阵列所提供的磁盘冗余,提升存储设配的耐用性。 RAID实现的 外接式磁盘阵列:通过扩展卡提供适配能力 内接式RAID:主板集成RAID控…

    2017-06-24
  • 操作系统图形界面发展史(1981-2009)

    注意,本文这罗列了从1981年以来有重大意义的操作系统的图形界面。 首先,先介绍两个网站: http://www.guidebookgallery.org/ 如果你比较关注图形化UI的设计, 可以上这个网站上看看。 http://toastytech.com/guis/index.html 这是一个操作系统图形界面收集的网站,上面几科包括…

    Linux干货 2016-05-17
  • Linux磁盘、文件系统管理

    对Linux而言,其哲学思想之一就是一切皆文件,因此Linux之上所有设备的操作都是通过文件接口来实现的,也就是说在访问设备的时候就如同访问一个文件一样,因为他们使用的都是文件系统接口。  设备也分种类,如:     1、块设备:block 其存取单位是“块”,如硬盘    &n…

    Linux干货 2016-08-29
  • Linux文件权限之facl

    1、什么是facl         facl(file access control list)文件访问控制列表,主要目的是在原有的u,g,o之外,另一层让普通用户能控制赋权给另外的用户或组的机制;       &nbs…

    Linux干货 2016-09-19
  • 轻松实现源码打包安装[原创]

    通常我们在Linux/Unix下安装一平台时往往需要十几甚至更多安装包,这些源码包来源于网络、本地硬盘、移动设备。有时碰到网络不畅通或下载地址失效会带来很多麻烦,一个好方法便是将常用的软件包下载到本地硬盘存放。问题是久而久之连自己都不晓得哪些包才是适用的。现用makeself来实现自解压倒安装倒是一个很好的解决方案,下面以制作Func客户端安装包为例。 一、…

    Linux干货 2015-03-27