码迷,mamicode.com
首页 > 系统相关 > 详细

鸟哥的Linux私房菜_基础版_学习笔记7:第十一章 认识与学习 BASH

时间:2015-05-25 18:46:30      阅读:204      评论:0      收藏:0      [点我收藏+]

标签:linux   鸟哥私房菜   

11.1 认识 BASH 这个 Shell
技术分享
图 1.1.1、硬件、核心与用户的相关性图示

11.1.4 Bash shell 的功能

既然 /bin/bash 是 Linux 默认的 shell ,bash 是 GNU 计划中重要的工具软件之一,目前也是 Linux distributions 的标准 shell 。 bash 主要兼容于 sh ,并且依据一些使用者需求,而加强的 shell 版本。不论你使用的是那个 distribution ,你都难逃需要学习 bash 的宿命啦!那么这个 shell 有什么好处,干嘛 Linux 要使用他作为默认的 shell 呢? bash 主要的优点有底下几个:

  • 命令记忆能力 (history):
  • 命令与文件补全功能: ([tab] 按键的好处)
  • 命令别名配置功能: (alias)

例如鸟哥最喜欢直接以 lm 这个自定义的命令来取代上面的命令,也就是说, lm 会等于 ls -al 这样的一个功能,嘿!那么要如何作呢?就使用 alias 即可!可以直接下达命令来配置别名呦:

alias lm=‘ls -al‘
  • 作业控制、前台、后台控制: (job control, foreground, background)
  • 程序化脚本: (shell scripts)
  • 通配符: (Wildcard)
11.1.5 Bash shell 的内建命令: type
怎么知道这个命令是来自于外部命令(指的是其他非 bash 所提供的命令) 或是内建在 bash 当中的呢? 嘿嘿!利用 type 这个命令来观察即可!
[root@www ~]# type [-tpa] name选项与参数:
    :不加任何选项与参数时,type 会显示出 name 是外部命令还是 bash 内建命令
-t  :当加入 -t 参数时,type 会将 name 以底下这些字眼显示出他的意义:
      file    :表示为外部命令;
      alias   :表示该命令为命令别名所配置的名称;
      builtin :表示该命令为 bash 内建的命令功能;
-p  :如果后面接的 name 为外部命令时,才会显示完整文件名;
-a  :会由 PATH 变量定义的路径中,将所有含 name 的命令都列出来,包含 alias范例一:查询一下 ls 这个命令是否为 bash 内建?
[root@www ~]# type ls
ls is aliased to `ls --color=tty‘ <==未加任何参数,列出 ls 的最主要使用情况
[root@www ~]# type -t ls
alias                             <==仅列出 ls 运行时的依据
[root@www ~]# type -a ls
ls is aliased to `ls --color=tty‘ <==最先使用 aliase
ls is /bin/ls                     <==还有找到外部命令在 /bin/ls范例二:那么 cd 呢?
[root@www ~]# type cd
cd is a shell builtin             <==看到了吗? cd 是 shell 内建命令
所以,这个 type 也可以用来作为类似 which 命令的用途啦!找命令用的!

11.1.6 命令的执行
范例:如果命令串太长的话,如何使用两行来输出?
[vbird@www ~]# cp /var/spool/mail/root /etc/crontab \
> /etc/fstab /root
 需要特别留意, [Enter] 按键是紧接着反斜杠 (\) 的,两者中间没有其他字符。 因为 \ 仅跳脱『紧接着的下一个字符』而已!所以,万一我写成: 『  [Enter] 』,亦即 [Enter] 与反斜杠中间有一个空格时,则 \ 跳脱的是『空格键』而不是 [Enter] 按键!这个地方请再仔细的看一遍!很重要!

11.2 Shell 的变量功能
变量是 bash 环境中非常重要的一个玩意儿,我们知道 Linux 是多人多任务的环境,每个人登陆系统都能取得一个 bash , 每个人都能够使用 bash 下达 mail 这个命令来收受『自己』的邮件,问题是, bash 是如何得知你的邮件信箱是哪个文件? 这就需要『变量』的帮助啦!
11.2.1 什么是变量?
 那就是:『 y = ax + b 』这东西,在等号左边的(y)就是变量,在等号右边的(ax+b)就是变量内容。 要注意的是,左边是未知数,右边是已知数喔! 
  • 变量的可变性与方便性
  • 影响 bash 环境操作的变量
由于系统需要一些变量来提供他数据的存取 (或者是一些环境的配置参数值, 例如是否要显示彩色等等的) ,所以就有一些所谓的『环境变量』 需要来读入系统中了!这些环境变量例如 PATH、HOME、MAIL、SHELL 等等,都是很重要的, 为了区别与自定义变量的不同,环境变量通常以大写字符来表示呢!
  • 脚本程序设计 (shell script) 的好帮手
技术分享
11.2.2 变量的取用与配置:echo, 变量配置守则, unset
你可以利用 echo 这个命令来取用变量, 但是,变量在被取用时,前面必须要加上钱字号『 $ 』才行
  • 变量的取用: echo
[root@www ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
[root@www ~]# echo ${PATH}
需要在变量名称前面加上 $ , 或者是以 ${变量} 的方式来取用都可以!
[root@www ~]# echo $myname<==这里并没有任何数据~因为这个变量尚未被配置!是空的!
[root@www ~]# myname=VBird
[root@www ~]# echo $myname
VBird  <==出现了!因为这个变量已经被配置了!

瞧!如此一来,这个变量名称 myname 的内容就带有 VBird 这个数据啰~

  • 变量的配置守则
  • 1、变量与变量内容以一个等号『=』来连结,如下所示: 
    『myname=VBird』

  • 2、等号两边不能直接接空格符,如下所示为错误: 
    『myname = VBird』或『myname=VBird Tsai』

  • 3、变量名称只能是英文字母与数字,但是开头字符不能是数字,如下为错误: 
    『2myname=VBird』

  • 4、变量内容若有空格符可使用双引号『"』或单引号『‘』将变量内容结合起来,但
    • 双引号内的特殊字符如 $ 等,可以保有原本的特性,如下所示:
      『var="lang is $LANG"』则『echo $var』可得『lang is en_US』
    • 单引号内的特殊字符则仅为一般字符 (纯文本),如下所示:
      『var=‘lang is $LANG‘』则『echo $var』可得『lang is $LANG』

  • 5、可用跳脱字符『 \ 』将特殊符号(如 [Enter], $, \, 空格符, ‘等)变成一般字符;

  • 6、在一串命令中,还需要藉由其他的命令提供的信息,可以使用反单引号『`命令`』或 『$(命令)』。特别注意,那个 ` 是键盘上方的数字键 1 左边那个按键,而不是单引号! 例如想要取得核心版本的配置:
    『version=$(uname -r)』再『echo $version』可得『2.6.18-128.el5』

  • 7、若该变量为扩增变量内容时,则可用 "$变量名称" 或 ${变量} 累加内容,如下所示:
    『PATH="$PATH":/home/bin』

  • 8、若该变量需要在其他子程序运行,则需要以 export 来使变量变成环境变量
    『export PATH』

  • 9、通常大写字符为系统默认变量,自行配置变量可以使用小写字符,方便判断 (纯粹依照使用者兴趣与嗜好) ;

  • 10、取消变量的方法为使用 unset :『unset 变量名称』例如取消 myname 的配置:
    『unset myname』
  • 范例三:我要在 PATH 这个变量当中『累加』:/home/dmtsai/bin 这个目录
    [root@www ~]# PATH=$PATH:/home/dmtsai/bin
    [root@www ~]# PATH="$PATH":/home/dmtsai/bin
    [root@www ~]# PATH=${PATH}:/home/dmtsai/bin# 上面这三种格式在 PATH 里头的配置都是 OK 的!但是底下的例子就不见得啰!
    [root@www ~]# name=${name}yes<==以此例较佳!
    范例七:取消刚刚配置的 name 这个变量内容
    [root@www ~]# unset name
    11.2.3 环境变量的功能
    查询环境变量,我们可以利用两个命令来查阅,分别是 env 与 export 呢!
    • 用 env 观察环境变量与常见环境变量说明
    范例一:列出目前的 shell 环境下的所有环境变量与其内容。
    [root@www ~]# env
    • HOME
      代表用户的家目录。还记得我们可以使用 cd ~ 去到自己的家目录吗?或者利用 cd 就可以直接回到用户家目录了。那就是取用这个变量啦~ 有很多程序都可能会取用到这个变量的值!

    • SHELL
      告知我们,目前这个环境使用的 SHELL 是哪支程序? Linux 默认使用 /bin/bash 的啦!

    • HISTSIZE
      这个与『历史命令』有关,亦即是, 我们曾经下达过的命令可以被系统记录下来,而记录的『笔数』则是由这个值来配置的。

    • MAIL
      当我们使用 mail 这个命令在收信时,系统会去读取的邮件信箱文件 (mailbox)。

    • PATH
      就是运行文件搜寻的路径啦~目录与目录中间以冒号(:)分隔, 由于文件的搜寻是依序由 PATH 的变量内的目录来查询,所以,目录的顺序也是重要的喔。

    • LANG
      这个重要!就是语系数据啰~很多信息都会用到他, 举例来说,当我们在启动某些 perl 的程序语言文件时,他会主动的去分析语系数据文件, 如果发现有他无法解析的编码语系,可能会产生错误喔!一般来说,我们中文编码通常是 zh_TW.Big5 或者是 zh_TW.UTF-8,这两个编码偏偏不容易被解译出来,所以,有的时候,可能需要修订一下语系数据。 这部分我们会在下个小节做介绍的!

    • RANDOM
      这个玩意儿就是『随机随机数』的变量啦!目前大多数的 distributions 都会有随机数生成器,那就是 /dev/random 这个文件。 我们可以透过这个随机数文件相关的变量 ($RANDOM) 来随机取得随机数值喔。在 BASH 的环境下,这个 RANDOM 变量的内容,介于 0~32767 之间,所以,你只要 echo $RANDOM 时,系统就会主动的随机取出一个介于 0~32767 的数值。万一我想要使用 0~9 之间的数值呢?呵呵~利用 declare 宣告数值类型, 然后这样做就可以了:
      [root@www ~]# declare -i number=$RANDOM*10/32768 ; echo $number
      8   <== 此时会随机取出 0~9 之间的数值喔!

    大致上是有这些环境变量啦~里面有些比较重要的参数,在底下我们都会另外进行一些说明的~


    • 用 set 观察所有变量 (含环境变量与自定义变量)

    set 除了环境变量之外, 还会将其他在 bash 内的变量通通显示出来哩!信息很多,底下鸟哥仅列出几个重要的内容:

    [root@www ~]# set
    ——PS1:(提示字符的配置)
    这是 PS1 (数字的 1 不是英文字母),这个东西就是我们的『命令提示字符』喔! 当我们每次按下 [Enter] 按键去运行某个命令后,最后要再次出现提示字符时, 就会主动去读取这个变量值了。上头 PS1 内显示的是一些特殊符号,这些特殊符号可以显示不同的信息, 每个 distributions 的 bash 默认的 PS1 变量内容可能有些许的差异,不要紧,『习惯你自己的习惯』就好了。 你可以用 man bash (注3)去查询一下 PS1 的相关说明,以理解底下的一些符号意义。

    • \d :可显示出『星期 月 日』的日期格式,如:"Mon Feb 2"
    • \H :完整的主机名。举例来说,鸟哥的练习机为『www.vbird.tsai』
    • \h :仅取主机名在第一个小数点之前的名字,如鸟哥主机则为『www』后面省略
    • \t :显示时间,为 24 小时格式的『HH:MM:SS』
    • \T :显示时间,为 12 小时格式的『HH:MM:SS』
    • \A :显示时间,为 24 小时格式的『HH:MM』
    • \@ :显示时间,为 12 小时格式的『am/pm』样式
    • \u :目前使用者的账号名称,如『root』;
    • \v :BASH 的版本信息,如鸟哥的测试主板本为 3.2.25(1),仅取『3.2』显示
    • \w :完整的工作目录名称,由根目录写起的目录名称。但家目录会以 ~ 取代;
    • \W :利用 basename 函数取得工作目录名称,所以仅会列出最后一个目录名。
    • \# :下达的第几个命令。
    • \$ :提示字符,如果是 root 时,提示字符为 # ,否则就是 $ 啰~
    技术分享
    技术分享

    技术分享技术分享

    ——$:(关于本 shell 的 PID)
    『 echo $$ 』即可!出现的数字就是你的 PID 号码。
    ——?:(关于上个运行命令的回传值)
    上一个运行的命令所回传的值』, 上面这句话的重点是『上一个命令』与『回传值』两个地方。当我们运行某些命令时, 这些命令都会回传一个运行后的代码。一般来说,如果成功的运行该命令, 则会回传一个 0 值,如果运行过程发生错误,就会回传『错误代码』才对!一般就是以非为 0 的数值来取代。
    [root@www ~]# echo $SHELL
    /bin/bash                                  <==可顺利显示!没有错误!
    [root@www ~]# echo $?
    0                                          <==因为没问题,所以回传值为 0
    [root@www ~]# 12name=VBird
    -bash: 12name=VBird: command not found     <==发生错误了!bash回报有问题
    [root@www ~]# echo $?
    127                                        <==因为有问题,回传错误代码(非为0)# 错误代码回传值依据软件而有不同,我们可以利用这个代码来搜寻错误的原因喔!
    • export: 自定义变量转成环境变量
    env 与 set 现在知道有所谓的环境变量与自定义变量,那么这两者之间有啥差异呢?其实这两者的差异在于『 该变量是否会被子程序所继续引用』啦!

    如你想要让该变量内容继续的在子程序中使用,那么就请运行:

    [root@www ~]# export 变量名称

    如果仅下达 export 而没有接变量时,那么此时将会把所有的『环境变量』秀出来喔!例如:

    [root@www ~]# export
    11.2.4 影响显示结果的语系变量 (locale)
    [root@www ~]# locale -a....(前面省略)....
    zh_TW
    zh_TW.big5     <==大五码的中文编码
    zh_TW.euctw
    zh_TW.utf8     <==万国码的中文编码
    [root@www ~]# locale<==后面不加任何选项与参数即可!
    LANG=en_US                   <==主语言的环境

    11.2.5 变量的有效范围
    基本上你可以这样看待:
    环境变量=全局变量
    自定义变量=局部变量
    11.2.6 变量键盘读取、数组与宣告: read, array, declare
    • read
    [root@www ~]# read [-pt] variable选项与参数:
    -p  :后面可以接提示字符!
    -t  :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦!范例一:让用户由键盘输入一内容,将该内容变成名为 atest 的变量
    [root@www ~]# read atestThis is a test<==此时光标会等待你输入!请输入左侧文字看看
    [root@www ~]# echo $atest
    This is a test          <==你刚刚输入的数据已经变成一个变量内容!范例二:提示使用者 30 秒内输入自己的大名,将该输入字符串作为名为 named 的变量内容
    [root@www ~]# read -p "Please keyin your name: " -t 30 named
    Please keyin your name: VBird Tsai<==注意看,会有提示字符喔!
    [root@www ~]# echo $named
    VBird Tsai        <==输入的数据又变成一个变量的内容了!
    • declare / typeset
    范例二:将 sum 变成环境变量
    [root@www ~]# declare -x sum
    [root@www ~]# export | grep sum
    declare -ix sum="450"  <==果然出现了!包括有 i 与 x 的宣告!范例三:让 sum 变成只读属性,不可更动!
    [root@www ~]# declare -r sum
    [root@www ~]# sum=tesgting
    -bash: sum: readonly variable  <==老天爷~不能改这个变量了!范例四:让 sum 变成非环境变量的自定义变量吧!
    [root@www ~]# declare +x sum<== 将 - 变成 + 可以进行『取消』动作
    [root@www ~]# declare -p sum<== -p 可以单独列出变量的类型
    declare -ir sum="450" <== 看吧!只剩下 i, r 的类型,不具有 x 啰!
    • 数组 (array) 变量类型

    在 bash 里头,数组的配置方式是:var[index]=content

    范例:配置上面提到的 var[1] ~ var[3] 的变量。
    [root@www ~]# var[1]="small min"
    [root@www ~]# var[2]="big min"
    [root@www ~]# var[3]="nice min"
    [root@www ~]# echo "${var[1]}, ${var[2]}, ${var[3]}"
    small min, big min, nice min
    11.2.7 与文件系统及程序的限制关系: ulimit
    『限制用户的某些系统资源』
    范例一:列出你目前身份(假设为root)的所有限制数据数值
    [root@www ~]# ulimit -a
    范例二:限制用户仅能创建 10MBytes 以下的容量的文件
    [root@www ~]# ulimit -f 10240
    [root@www ~]# ulimit -a
    file size               (blocks, -f) 10240 <==最大量为10240Kbyes,相当10Mbytes
    [root@www ~]# dd if=/dev/zero of=123 bs=1M count=20
    File size limit exceeded <==尝试创建 20MB 的文件,结果失败了!
    11.2.8 变量内容的删除、取代与替换
    • 变量内容的删除与取代
    范例一:先让小写的 path 自定义变量配置的与 PATH 内容相同
    [root@www ~]# path=${PATH}
    [root@www ~]# echo $path
    /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
    /usr/sbin:/usr/bin:/root/bin  <==这两行其实是同一行啦!范例二:假设我不喜欢 kerberos,所以要将前两个目录删除掉,如何显示?
    [root@www ~]# echo ${path#/*kerberos/bin:}
    /usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
    

    上面这个范例很有趣的!他的重点可以用底下这张表格来说明:

    ${variable#/*kerberos/bin:}   上面的特殊字体部分是关键词!用在这种删除模式所必须存在的
    
    ${variable#/*kerberos/bin:}
       这就是原本的变量名称,以上面范例二来说,这里就填写 path 这个『变量名称』啦!
    
    ${variable#/*kerberos/bin:}
       这是重点!代表『从变量内容的最前面开始向右删除』,且仅删除最短的那个
    
    ${variable#/*kerberos/bin:}
       代表要被删除的部分,由于 # 代表由前面开始删除,所以这里便由开始的 / 写起。
       需要注意的是,我们还可以透过通配符 * 来取代 0 到无穷多个任意字符   以上面范例二的结果来看, path 这个变量被删除的内容如下所示:
    /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
    /usr/sbin:/usr/bin:/root/bin  <==这两行其实是同一行啦!

    很有趣吧!这样了解了 # 的功能了吗?接下来让我们来看看底下的范例三!

    范例三:我想要删除前面所有的目录,仅保留最后一个目录
    [root@www ~]# echo ${path#/*:}
    /usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:
    /root/bin     <==这两行其实是同一行啦!# 由于一个 # 仅删除掉最短的那个,因此他删除的情况可以用底下的删除线来看:
    # /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
    # /usr/sbin:/usr/bin:/root/bin  <==这两行其实是同一行啦!
    
    [root@www ~]# echo ${path##/*:}
    /root/bin
    # 嘿!多加了一个 # 变成 ## 之后,他变成『删除掉最长的那个数据』!亦即是:
    # /usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:
    # /usr/sbin:/usr/bin:/root/bin  <==这两行其实是同一行啦!

    非常有趣!不是吗?因为在 PATH 这个变量的内容中,每个目录都是以冒号『:』隔开的, 所以要从头删除掉目录就是介于斜线 (/) 到冒号 (:) 之间的数据!但是 PATH 中不止一个冒号 (:) 啊! 所以 # 与 ## 就分别代表:

    • # :符合取代文字的『最短的』那一个;
    • ##:符合取代文字的『最长的』那一个
    变量配置方式 说明
    ${变量#关键词}
    ${变量##关键词}
    若变量内容从头开始的数据符合『关键词』,则将符合的最短数据删除
    若变量内容从头开始的数据符合『关键词』,则将符合的最长数据删除
    ${变量%关键词}
    ${变量%%关键词}
    若变量内容从尾向前的数据符合『关键词』,则将符合的最短数据删除
    若变量内容从尾向前的数据符合『关键词』,则将符合的最长数据删除
    ${变量/旧字符串/新字符串}
    ${变量//旧字符串/新字符串}
    若变量内容符合『旧字符串』则『第一个旧字符串会被新字符串取代』
    若变量内容符合『旧字符串』则『全部的旧字符串会被新字符串取代』

    • 变量的测试与内容替换
    • 范例一:测试一下是否存在 username 这个变量,若不存在则给予 username 内容为 root
      [root@www ~]# echo $username<==由于出现空白,所以 username 可能不存在,也可能是空字符串
      [root@www ~]# username=${username-root}
      [root@www ~]# echo $username
      root       <==因为 username 没有配置,所以主动给予名为 root 的内容。
      [root@www ~]# username="vbird tsai"<==主动配置 username 的内容
      [root@www ~]# username=${username-root}
      [root@www ~]# echo $username
      vbird tsai <==因为 username 已经配置了,所以使用旧有的配置而不以 root 取代

      在上面的范例中,重点在于减号『 - 』后面接的关键词!基本上你可以这样理解:

      new_var=${old_var-content}
         新的变量,主要用来取代旧变量。新旧变量名称其实常常是一样的
      
      new_var=${old_var-content}这是本范例中的关键词部分!必须要存在的哩!
      
      new_var=${old_var-content}
         旧的变量,被测试的项目!
      
      new_var=${old_var-content}
         变量的『内容』,在本范例中,这个部分是在『给予未配置变量的内容』

      不过这还是有点问题!因为 username 可能已经被配置为『空字符串』了!果真如此的话,那你还可以使用底下的范例来给予 username 的内容成为 root 喔!

      范例二:若 username 未配置或为空字符串,则将 username 内容配置为 root
      [root@www ~]# username=""
      [root@www ~]# username=${username-root}
      [root@www ~]# echo $username<==因为 username 被配置为空字符串了!所以当然还是保留为空字符串!
      [root@www ~]# username=${username:-root}
      [root@www ~]# echo $username
      root  <==加上『 : 』后若变量内容为空或者是未配置,都能够以后面的内容替换!
    变量配置方式 str 没有配置 str 为空字符串 str 已配置非为空字符串
    var=${str-expr} var=expr var= var=$str
    var=${str:-expr} var=expr var=expr var=$str
    var=${str+expr} var= var=expr var=expr
    var=${str:+expr} var= var= var=expr
    var=${str=expr} str=expr
    var=expr
    str 不变
    var=
    str 不变
    var=$str
    var=${str:=expr} str=expr
    var=expr
    str=expr
    var=expr
    str 不变
    var=$str
    var=${str?expr} expr 输出至 stderr var= var=$str
    var=${str:?expr} expr 输出至 stderr expr 输出至 stderr var=$str
    11.3 命令别名与历史命令:
    11.3.1 命令别名配置: alias, unalias

    那么需要下达『 ls -al | more 』这个命令,我是觉得很烦啦! 要输入好几个单字!那可不可以使用 lm 来简化呢?当然可以,你可以在命令行下面下达:

    [root@www ~]# alias lm=‘ls -al | more‘

     rm 提供了一个选项来让我们确认是否要移除该文件,那就是 -i 这个选项!所以,你可以这样做:

    [root@www ~]# alias rm=‘rm -i‘

    至于如果要取消命令别名的话,那么就使用 unalias 吧!例如要将刚刚的 lm 命令别名拿掉,就使用:

    [root@www ~]# unalias lm
    11.3.2 历史命令:history
    [root@www ~]# history [n]
    [root@www ~]# history [-c]
    [root@www ~]# history [-raw] histfiles选项与参数:
    n   :数字,意思是『要列出最近的 n 笔命令行表』的意思!
    -c  :将目前的 shell 中的所有 history 内容全部消除
    -a  :将目前新增的 history 命令新增入 histfiles 中,若没有加 histfiles ,
          则默认写入 ~/.bash_history
    -r  :将 histfiles 的内容读到目前这个 shell 的 history 记忆中;
    -w  :将目前的 history 记忆内容写入 histfiles 中!范例一:列出目前内存内的所有 history 记忆
    [root@www ~]# history# 前面省略
     1017  man bash
     1018  ll
     1019  history 
     1020  history
    # 列出的信息当中,共分两栏,第一栏为该命令在这个 shell 当中的代码,
    # 另一个则是命令本身的内容喔!至于会秀出几笔命令记录,则与 HISTSIZE 有关!范例二:列出目前最近的 3 笔数据
    [root@www ~]# history 3
     1019  history 
     1020  history
     1021  history 3
    
    范例三:立刻将目前的数据写入 histfile 当中
    [root@www ~]# history -w# 在默认的情况下,会将历史纪录写入 ~/.bash_history 当中!
    [root@www ~]# echo $HISTSIZE
    1000

    [root@www ~]# !number
    [root@www ~]# !command
    [root@www ~]# !!选项与参数:
    number  :运行第几笔命令的意思;
    command :由最近的命令向前搜寻『命令串开头为 command』的那个命令,并运行;
    !!      :就是运行上一个命令(相当于按↑按键后,按 Enter)
    11.4 Bash Shell 的操作环境:
    11.4.1 路径与命令搜寻顺序
  • 以相对/绝对路径运行命令,例如『 /bin/ls 』或『 ./ls 』;
  • 由 alias 找到该命令来运行;
  • 由 bash 内建的 (builtin) 命令来运行;
  • 透过 $PATH 这个变量的顺序搜寻到的第一个命令来运行。11.4.2 bash 的进站与欢迎信息: /etc/issue, /etc/motd
  • [root@www ~]# cat /etc/issue
    CentOS release 5.3 (Final)
    Kernel \r on an \m
    
    

    鸟哥是以完全未升级过的 CentOS 5.3 作为范例,里面默认有三行,较有趣的地方在于 \r 与 \m。 就如同 $PS1 这变量一样,issue 这个文件的内容也是可以使用反斜杠作为变量取用喔!

    11.4.3 bash 的环境配置文件

    • source :读入环境配置文件的命令

    由于 /etc/profile 与 ~/.bash_profile 都是在取得 login shell 的时候才会读取的配置文件,所以, 如果你将自己的偏好配置写入上述的文件后,通常都是得注销再登陆后,该配置才会生效。那么,能不能直接读取配置文件而不注销登陆呢? 可以的!那就得要利用 source 这个命令了!

    [root@www ~]# source 配置文件档名范例:将家目录的 ~/.bashrc 的配置读入目前的 bash 环境中
    [root@www ~]# source ~/.bashrc<==底下这两个命令是一样的!
    [root@www ~]#  .  ~/.bashrc

    利用 source 或小数点 (.) 都可以将配置文件的内容读进来目前的 shell 环境中! 

    • ~/.bashrc (non-login shell 会读)
    [root@www ~]# cat ~/.bashrc
    # .bashrc
    
    # User specific aliases and functions
    alias rm=‘rm -i‘             <==使用者的个人配置
    alias cp=‘cp -i‘
    alias mv=‘mv -i‘
    
    # Source global definitions
    if [ -f /etc/bashrc ]; then  <==整体的环境配置
            . /etc/bashrc
    fi

    特别注意一下,由于 root 的身份与一般使用者不同,鸟哥是以 root 的身份取得上述的数据, 如果是一般使用者的 ~/.bashrc 会有些许不同。看一下,你会发现在 root 的 ~/.bashrc 中其实已经规范了较为保险的命令别名了。 此外,咱们的 CentOS 5.x 还会主动的呼叫 /etc/bashrc 这个文件喔!为什么需要呼叫 /etc/bashrc 呢? 因为 /etc/bashrc 帮我们的 bash 定义出底下的数据:

    • 依据不同的 UID 规范出 umask 的值;
    • 依据不同的 UID 规范出提示字符 (就是 PS1 变量);
    • 呼叫 /etc/profile.d/*.sh 的配置

    11.4.4 终端机的环境配置: stty, set

    可以利用 stty (setting tty 终端机的意思) 呢! stty 也可以帮助配置终端机的输入按键代表意义喔!

    [root@www ~]# stty [-a]选项与参数:
    -a  :将目前所有的 stty 参数列出来;

     此外,如果出现 ^ 表示 [Ctrl] 那个按键的意思。举例来说, intr = ^C 表示利用 [ctrl] + c 来达成的。几个重要的代表意义是:

    • eof   : End of file 的意思,代表『结束输入』。
    • erase : 向后删除字符,
    • intr  : 送出一个 interrupt (中断) 的讯号给目前正在 run 的程序;
    • kill  : 删除在目前命令列上的所有文字;
    • quit  : 送出一个 quit 的讯号给目前正在 run 的程序;
    • start : 在某个程序停止后,重新启动他的 output
    • stop  : 停止目前屏幕的输出;
    • susp  : 送出一个 terminal stop 的讯号给正在 run 的程序。

    组合按键 运行结果
    Ctrl + C 终止目前的命令
    Ctrl + D 输入结束 (EOF),例如邮件结束的时候;
    Ctrl + M 就是 Enter 啦!
    Ctrl + S 暂停屏幕的输出
    Ctrl + Q 恢复屏幕的输出
    Ctrl + U 在提示字符下,将整列命令删除
    Ctrl + Z 『暂停』目前的命令
    11.4.5 通配符与特殊符号
    符号 意义
    * 代表『 0 个到无穷多个』任意字符
    ? 代表『一定有一个』任意字符
    [ ] 同样代表『一定有一个在括号内』的字符(非任意字符)。例如 [abcd] 代表『一定有一个字符, 可能是 a, b, c, d 这四个任何一个』
    [ - ] 若有减号在中括号内时,代表『在编码顺序内的所有字符』。例如 [0-9] 代表 0 到 9 之间的所有数字,因为数字的语系编码是连续的!
    [^ ] 若中括号内的第一个字符为指数符号 (^) ,那表示『反向选择』,例如 [^abc] 代表 一定有一个字符,只要是非 a, b, c 的其他字符就接受的意思。
    范例一:找出 /etc/ 底下以 cron 为开头的档名
    [root@www ~]# ll -d /etc/cron*  <==加上 -d 是为了仅显示目录而已范例二:找出 /etc/ 底下文件名『刚好是五个字母』的文件名
    [root@www ~]# ll -d /etc/?????  <==由于 ? 一定有一个,所以五个 ? 就对了范例三:找出 /etc/ 底下文件名含有数字的文件名
    [root@www ~]# ll -d /etc/*[0-9]*<==记得中括号左右两边均需 *范例四:找出 /etc/ 底下,档名开头非为小写字母的文件名:
    [root@www ~]# ll -d /etc/[^a-z]*<==注意中括号左边没有 *范例五:将范例四找到的文件复制到 /tmp 中
    [root@www ~]# cp -a /etc/[^a-z]* /tmp

    除了通配符之外,bash 环境中的特殊符号有哪些呢?底下我们先汇整一下:

    符号 内容
    # 批注符号:这个最常被使用在 script 当中,视为说明!在后的数据均不运行
    \ 跳脱符号:将『特殊字符或通配符』还原成一般字符
    | 管线 (pipe):分隔两个管线命令的界定(后两节介绍);
    ; 连续命令下达分隔符:连续性命令的界定 (注意!与管线命令并不相同)
    ~ 用户的家目录
    $ 取用变量前导符:亦即是变量之前需要加的变量取代值
    & 工作控制 (job control):将命令变成背景下工作
    ! 逻辑运算意义上的『非』 not 的意思!
    / 目录符号:路径分隔的符号
    >, >> 数据流重导向:输出导向,分别是『取代』与『累加』
    <, << 数据流重导向:输入导向 (这两个留待下节介绍)
    ‘ ‘ 单引号,不具有变量置换的功能
    " " 具有变量置换的功能!
    ` ` 两个『 ` 』中间为可以先运行的命令,亦可使用 $( )
    ( ) 在中间为子 shell 的起始与结束
    { } 在中间为命令区块的组合!
    11.5 数据流重导向
    数据流重导向就是将某个命令运行后应该要出现在屏幕上的数据, 给他传输到其他的地方
    11.5.1 什么是数据流重导向

    运行一个命令,通常他会是这样的:

    技术分享
    图 5.1.1、命令运行过程的数据传输情况

    我们运行一个命令的时候,这个命令可能会由文件读入数据,经过处理之后,再将数据输出到屏幕上。在上图当中, standard output 与 standard error output 分别代表『标准输出』与『标准错误输出』

    • standard output 与 standard error output
    标准输出指的是『命令运行所回传的正确的信息』,而标准错误输出可理解为『 命令运行失败后,所回传的错误信息』。

    数据流重导向可以将 standard output (简称 stdout) 与 standard error output (简称 stderr) 分别传送到其他的文件或装置去,而分别传送所用的特殊字符则如下所示:

    1. 标准输入  (stdin) :代码为 0 ,使用 < 或 << ;
    2. 标准输出  (stdout):代码为 1 ,使用 > 或 >> ;
    3. 标准错误输出(stderr):代码为 2 ,使用 2> 或 2>> ;

    为了理解 stdout 与 stderr ,我们先来进行一个范例的练习:

    范例一:观察你的系统根目录 (/) 下各目录的文件名、权限与属性,并记录下来
    [root@www ~]# ll /<==此时屏幕会显示出文件名信息
    
    [root@www ~]# ll / > ~/rootfile<==屏幕并无任何信息
    [root@www ~]# ll  ~/rootfile<==有个新档被创建了!
    -rw-r--r-- 1 root root 1089 Feb  6 17:00 /root/rootfile
    

    怪了!屏幕怎么会完全没有数据呢?这是因为原本『 ll / 』所显示的数据已经被重新导向到 ~/rootfile 文件中了!

    咦!原本的『 ll / 』数据就不见了吗?是的!因为该文件的创建方式是:

    1. 该文件 (本例中是 ~/rootfile) 若不存在,系统会自动的将他创建起来,但是
    2. 当这个文件存在的时候,那么系统就会先将这个文件内容清空,然后再将数据写入!
    3. 也就是若以 > 输出到一个已存在的文件中,那个文件就会被覆盖掉啰!
    那如果我想要将数据累加而不想要将旧的数据删除,那该如何是好?利用两个大于的符号 (>>) 就好啦!以上面的范例来说,你应该要改成『 ll / >> ~/rootfile 』即可。

    也就是说:

    • 1> :以覆盖的方法将『正确的数据』输出到指定的文件或装置上;
    • 1>>:以累加的方法将『正确的数据』输出到指定的文件或装置上;
    • 2> :以覆盖的方法将『错误的数据』输出到指定的文件或装置上;
    • 2>>:以累加的方法将『错误的数据』输出到指定的文件或装置上;
    范例三:承范例二,将 stdout 与 stderr 分存到不同的文件去
    [dmtsai@www ~]$ find /home -name .bashrc > list_right 2> list_error

    注意喔,此时『屏幕上不会出现任何信息』!因为刚刚运行的结果中,有 Permission 的那几行错误信息都会跑到 list_error 这个文件中,至于正确的输出数据则会存到 list_right 这个文件中啰!

    • /dev/null 垃圾桶黑洞装置与特殊写法

    如果我知道错误信息会发生,所以要将错误信息忽略掉而不显示或储存呢? 这个时候黑洞装置 /dev/null 就很重要了!这个 /dev/null 可以吃掉任何导向这个装置的信息喔!将上述的范例修订一下:

    范例四:承范例三,将错误的数据丢弃,屏幕上显示正确的数据
    [dmtsai@www ~]$ find /home -name .bashrc 2> /dev/null
    /home/dmtsai/.bashrc  <==只有 stdout 会显示到屏幕上, stderr 被丢弃了
    范例五:将命令的数据全部写入名为 list 的文件中
    [dmtsai@www ~]$ find /home -name .bashrc > list 2> list<==错误
    [dmtsai@www ~]$ find /home -name .bashrc > list 2>&1   <==正确
    [dmtsai@www ~]$ find /home -name .bashrc &> list       <==正确

    上述表格第一行错误的原因是,由于两股数据同时写入一个文件,又没有使用特殊的语法, 此时两股数据可能会交叉写入该文件内,造成次序的错乱

    • standard input : < 与 <<
    范例六:利用 cat 命令来创建一个文件的简单流程
    [root@www ~]# cat > catfiletesting
    cat file test<==这里按下 [ctrl]+d 来离开
    
    [root@www ~]# cat catfile
    testing
    cat file test
    [root@www ~]# cat > catfile << "eof"
    > This is a test.
    > OK now stop
    > eof<==输入这关键词,立刻就结束而不需要输入 [ctrl]+d
    
    [root@www ~]# cat catfile
    This is a test.
    OK now stop     <==只有这两行,不会存在关键词那一行!
    11.5.2 命令运行的判断依据: ; , &&, ||
    • cmd ; cmd (不考虑命令相关性的连续命令下达)

    在某些时候,我们希望可以一次运行多个命令,例如在关机的时候我希望可以先运行两次 sync 同步化写入磁盘后才 shutdown 计算机,那么可以怎么作呢?这样做呀:

    [root@www ~]# sync; sync; shutdown -h now
    • $? (命令回传值) 与 && 或 ||
    命令下达情况 说明
    cmd1 && cmd2 1. 若 cmd1 运行完毕且正确运行($?=0),则开始运行 cmd2。
    2. 若 cmd1 运行完毕且为错误 ($?≠0),则 cmd2 不运行。
    cmd1 || cmd2 1. 若 cmd1 运行完毕且正确运行($?=0),则 cmd2 不运行。
    2. 若 cmd1 运行完毕且为错误 ($?≠0),则开始运行 cmd2。
    范例一:使用 ls 查阅目录 /tmp/abc 是否存在,若存在则用 touch 创建 /tmp/abc/hehe 
    [root@www ~]# ls /tmp/abc && touch /tmp/abc/hehe
    ls: /tmp/abc: No such file or directory 
    # ls 很干脆的说明找不到该目录,但并没有 touch 的错误,表示 touch 并没有运行
    
    [root@www ~]# mkdir /tmp/abc
    [root@www ~]# ls /tmp/abc && touch /tmp/abc/hehe
    [root@www ~]# ll /tmp/abc
    -rw-r--r-- 1 root root 0 Feb  7 12:43 hehe
    范例三:我不清楚 /tmp/abc 是否存在,但就是要创建 /tmp/abc/hehe 文件
    [root@www ~]# ls /tmp/abc || mkdir /tmp/abc && touch /tmp/abc/hehe

    由于Linux 底下的命令都是由左往右运行的,所以范例三有几种结果我们来分析一下:

    • (1)若 /tmp/abc 不存在故回传 $?≠0,则 (2)因为 || 遇到非为 0 的 $? 故开始 mkdir /tmp/abc,由于 mkdir /tmp/abc 会成功进行,所以回传 $?=0 (3)因为 && 遇到 $?=0 故会运行 touch /tmp/abc/hehe,最终 hehe 就被创建了;

    • (1)若 /tmp/abc 存在故回传 $?=0,则 (2)因为 || 遇到 0 的 $? 不会进行,此时 $?=0 继续向后传,故 (3)因为 && 遇到 $?=0 就开始创建 /tmp/abc/hehe 了!最终 /tmp/abc/hehe 被创建起来。

    整个流程图示如下:

    技术分享
    图 5.2.1、 命令依序运行的关系示意图
    11.6 管线命令 (pipe)

    假设我们想要知道 /etc/ 底下有多少文件,那么可以利用 ls /etc 来查阅,不过, 因为 /etc 底下的文件太多,导致一口气就将屏幕塞满了~不知道前面输出的内容是啥?此时,我们可以透过 less 命令的协助,利用:

    [root@www ~]# ls -al /etc | less
     其实这个管线命令『 | 』仅能处理经由前面一个命令传来的正确信息,也就是 standard output 的信息,对于 stdandard error 并没有直接处理的能力
    • 管线命令仅会处理 standard output,对于 standard error output 会予以忽略
    • 管线命令必须要能够接受来自前一个命令的数据成为 standard input 继续处理才行。
    11.6.1 选取命令: cut, grep
     不过,要注意的是,一般来说,撷取信息通常是针对『一行一行』来分析的, 并不是整篇信息分析的喔~底下我们介绍两个很常用的信息撷取命令:
    • cut
    [root@www ~]# cut -d‘分隔字符‘ -f fields<==用于有特定分隔字符
    [root@www ~]# cut -c 字符区间           <==用于排列整齐的信息选项与参数:
    -d  :后面接分隔字符。与 -f 一起使用;
    -f  :依据 -d 的分隔字符将一段信息分割成为数段,用 -f 取出第几段的意思;
    -c  :以字符 (characters) 的单位取出固定字符区间;
    [root@www ~]# export | cut -c 12-
    HISTSIZE="1000"
    INPUTRC="/etc/inputrc"
    KDEDIR="/usr"
    LANG="zh_TW.big5"
    .....(其他省略).....
    # 知道怎么回事了吧?用 -c 可以处理比较具有格式的输出数据!
    # 我们还可以指定某个范围的值,例如第 12-20 的字符,就是 cut -c 12-20 等等!

    • grep

    刚刚的 cut 是将一行信息当中,取出某部分我们想要的,而 grep 则是分析一行信息, 若当中有我们所需要的信息,就将该行拿出来~简单的语法是这样的:

    [root@www ~]# grep [-acinv] [--color=auto] ‘搜寻字符串‘ filename选项与参数:
    -a :将 binary 文件以 text 文件的方式搜寻数据
    -c :计算找到 ‘搜寻字符串‘ 的次数
    -i :忽略大小写的不同,所以大小写视为相同
    -n :顺便输出行号
    -v :反向选择,亦即显示出没有 ‘搜寻字符串‘ 内容的那一行!
    --color=auto :可以将找到的关键词部分加上颜色的显示喔!范例一:将 last 当中,有出现 root 的那一行就取出来;
    [root@www ~]# last | grep ‘root‘范例二:与范例一相反,只要没有 root 的就取出!
    [root@www ~]# last | grep -v ‘root‘范例三:在 last 的输出信息中,只要有 root 就取出,并且仅取第一栏
    [root@www ~]# last | grep ‘root‘ |cut -d ‘ ‘ -f1# 在取出 root 之后,利用上个命令 cut 的处理,就能够仅取得第一栏啰!范例四:取出 /etc/man.config 内含 MANPATH 的那几行
    [root@www ~]# grep --color=auto ‘MANPATH‘ /etc/man.config....(前面省略)....MANPATH_MAP     /usr/X11R6/bin          /usr/X11R6/man
    MANPATH_MAP     /usr/bin/X11            /usr/X11R6/man
    MANPATH_MAP     /usr/bin/mh             /usr/share/man
    # 神奇的是,如果加上 --color=auto 的选项,找到的关键词部分会用特殊颜色显示喔!

    11.6.2 排序命令: sort, wc, uniq

    • sort
    [root@www ~]# sort [-fbMnrtuk] [file or stdin]选项与参数:
    -f  :忽略大小写的差异,例如 A 与 a 视为编码相同;
    -b  :忽略最前面的空格符部分;
    -M  :以月份的名字来排序,例如 JAN, DEC 等等的排序方法;
    -n  :使用『纯数字』进行排序(默认是以文字型态来排序的);
    -r  :反向排序;
    -u  :就是 uniq ,相同的数据中,仅出现一行代表;
    -t  :分隔符,默认是用 [tab] 键来分隔;
    -k  :以那个区间 (field) 来进行排序的意思范例一:个人账号都记录在 /etc/passwd 下,请将账号进行排序。
    [root@www ~]# cat /etc/passwd | sort
    • uniq

    如果我排序完成了,想要将重复的数据仅列出一个显示,可以怎么做呢?

    [root@www ~]# uniq [-ic]选项与参数:
    -i  :忽略大小写字符的不同;
    -c  :进行计数范例一:使用 last 将账号列出,仅取出账号栏,进行排序后仅取出一位;
    [root@www ~]# last | cut -d ‘ ‘ -f1 | sort | uniq
    • wc
    如果我想要知道 /etc/man.config 这个文件里面有多少字?多少行?多少字符的话, 可以怎么做呢?其实可以利用 wc 这个命令来达成喔!
    [root@www ~]# wc [-lwm]选项与参数:
    -l  :仅列出行;
    -w  :仅列出多少字(英文单字);
    -m  :多少字符;范例一:那个 /etc/man.config 里面到底有多少相关字、行、字符数?
    [root@www ~]# cat /etc/man.config | wc 
        141     722    4617
    # 输出的三个数字中,分别代表: 『行、字数、字符数』
    11.6.3 双向重导向: tee
    技术分享
    图 6.3.1、tee 的工作流程示意图
    tee 可以让 standard output 转存一份到文件内并将同样的数据继续送到屏幕去处理! 这样除了可以让我们同时分析一份数据并记录下来之外,还可以作为处理一份数据的中间缓存盘记录之用! tee 这家伙在很多选择/填充的认证考试中很容易考呢!
    [root@www ~]# tee [-a] file选项与参数:
    -a  :以累加 (append) 的方式,将数据加入 file 当中!
    
    [root@www ~]# last | tee last.list
    11.6.4 字符转换命令: tr, col, join, paste, expand
    • tr

    tr 可以用来删除一段信息当中的文字,或者是进行文字信息的替换!

    • col
    [root@www ~]# col [-xb]选项与参数:
    -x  :将 tab 键转换成对等的空格键
    范例二:将 col 的 man page 转存成为 /root/col.man 的纯文本档
    [root@www ~]# man col > /root/col.man
    [root@www ~]# vi /root/col.man
    COL(1)          BSD General Commands Manual               COL(1)
    
    N^HNA^HAM^HME^HE
         c^Hco^Hol^Hl - filter reverse line feeds from input
    
    S^HSY^HYN^HNO^HOP^HPS^HSI^HIS^HS
         c^Hco^Hol^Hl [-^H-b^Hbf^Hfp^Hpx^Hx] [-^H-l^Hl _^Hn_^Hu_^Hm]
    # 你没看错!由于 man page 内有些特殊按钮会用来作为类似特殊按键与颜色显示,
    # 所以这个文件内就会出现如上所示的一堆怪异字符(有 ^ 的)
    此外, col 经常被利用于将 man page 转存为纯文本文件以方便查阅的功能!如上述的范例二!

    • join

    join 看字面上的意义 (加入/参加) 就可以知道,他是在处理两个文件之间的数据, 而且,主要是在处理『两个文件当中,有"相同数据" 的那一行,才将他加在一起』的意思。我们利用底下的简单例子来说明:

    [root@www ~]# join [-ti12] file1 file2选项与参数:
    -t  :join 默认以空格符分隔数据,并且比对『第一个字段』的数据,
          如果两个文件相同,则将两笔数据联成一行,且第一个字段放在第一个!
    -i  :忽略大小写的差异;
    -1  :这个是数字的 1 ,代表『第一个文件要用那个字段来分析』的意思;
    -2  :代表『第二个文件要用那个字段来分析』的意思。
    需要特别注意的是,在使用 join 之前,你所需要处理的文件应该要事先经过排序 (sort) 处理! 否则有些比对的项目会被略过呢!特别注意了!

    • paste

    这个 paste 就要比 join 简单多了!相对于 join 必须要比对两个文件的数据相关性, paste 就直接『将两行贴在一起,且中间以 [tab] 键隔开』而已!简单的使用方法:

    • expand

    他会自动将 [tab] 转成空格键

    11.6.5 分割命令: split
    可以帮你将一个大文件,依据文件大小或行数来分割,就可以将大文件分割成为小文件了! 
    [root@www ~]# split [-bl] file PREFIX选项与参数:
    -b  :后面可接欲分割成的文件大小,可加单位,例如 b, k, m 等;
    -l  :以行数来进行分割。
    PREFIX :代表前导符的意思,可作为分割文件的前导文字。范例一:我的 /etc/termcap 有七百多K,若想要分成 300K 一个文件时?
    [root@www ~]# cd /tmp; split -b 300k /etc/termcap termcap
    [root@www tmp]# ll -k termcap*
    -rw-r--r-- 1 root root 300 Feb  7 16:39 termcapaa
    -rw-r--r-- 1 root root 300 Feb  7 16:39 termcapab
    -rw-r--r-- 1 root root 189 Feb  7 16:39 termcapac# 那个档名可以随意取的啦!我们只要写上前导文字,小文件就会以
    # xxxaa, xxxab, xxxac 等方式来创建小文件的!
    11.6.6 参数代换: xargs

    11.6.7 关于减号 - 的用途

    某些命令需要用到文件名 (例如 tar) 来进行处理时,该 stdin 与 stdout 可以利用减号 "-" 来替代, 举例来说:

    [root@www ~]# tar -cvf - /home | tar -xvf -

    上面这个例子是说:『我将 /home 里面的文件给他打包,但打包的数据不是纪录到文件,而是传送到 stdout; 经过管线后,将 tar -cvf - /home 传送给后面的 tar -xvf - 』。后面的这个 - 则是取用前一个命令的 stdout, 因此,我们就不需要使用 file 了!这是很常见的例子喔!注意注意!




    鸟哥的Linux私房菜_基础版_学习笔记7:第十一章 认识与学习 BASH

    标签:linux   鸟哥私房菜   

    原文地址:http://blog.csdn.net/keyyuanxin/article/details/45971857

    (0)
    (0)
       
    举报
    评论 一句话评论(0
    登录后才能评论!
    © 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
    迷上了代码!