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

相关推荐

  • 第四周小结

    这周我们主要学习了写脚本的简单语法,写了一些简单的脚本,下面就由我来简单介绍一下: 第一步使用文本编辑来创建脚本: 创建好后在里面写想要运行的脚本即可,然后按Esc—wq退出保存即可。也可以按q不保存退出;q!不保存强制退出;wq!保存强制退出。 第二步运行脚本,给予执行权限,在命令行上指定脚本的相对路径和绝对路径 对了,当在脚本里输入内容时,要Ese&#8…

    2017-08-06
  • bind简单配置之子域、主从、视图

    [bind简单配置之子域、主从、视图] 标签:bind简单配置、子域、主从、视图 [逻辑拓扑结构图] ns.mylinux.com主机上面有两种网卡用来模拟两个不通的网段。 [子域配置]         域名内部还可以继续划分子域,进行分片管理。下面是正向子域授权的配置: &nb…

    Linux干货 2015-10-22
  • SED命令

    sed是一种流编辑器,它一次处理一行内容。处理时,把当前处理的行存储在临时缓冲区中,称为“模式空间”(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。Sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,编写转…

    Linux干货 2017-05-01
  • rpm包及rmp命令详解

    一、软件运行环境           API: Application Programming Interface              国际标准&nbs…

    Linux干货 2016-08-22
  • linux 网络管理命令 SS的使用详则

    SS命令 ss命令用来显示处于活动状态的套接字信息,ss迷路可以用来获取socket统计信息,它可以显示和netstat类似的内容。但ss的优势在于它能够显示更多更详细的有关TCO和连接状态信息,而且比netstat更快速更高效。 当服务器的socket连接数量变得非常大时,无论是使用netest命令还是直接  cat/proc/net/tcp 。…

    2017-08-19
  • 软连接与硬连接

    硬链接定义 新建的文件是已经存在的文件的一个别名,当原文件删除时,新建的文件仍然可以使用. 硬链接(hard link) ln   UNIX文件系统提供了一种将不同文件链接至同一个文件的机制,称这种机制为链接。它可以使得单个程序对同一文件使用不同的名字。这样的好处是文件系统只存在一个文件的副本。系统简单地通过在目录中建立一个新的登记项来实现这种连接。该登记项…

    Linux干货 2017-07-22