- 10.1 认识BASH 这个Shell
- 10.2 Shell 的变量功能
- 单引号与双引号必须要成对,在上面的设定中仅有一个单引号,因此当你按下enter后,
- 你还可以继续输入变数内容。这与我们所需要的功能不同,失败啦!
- 记得,失败后要复原请按下[ctrl]-c结束!
- 指令是由左边向右找→,先遇到的引号先有用,因此如上所示,单引号变成一般字元!
- 因为前两个单引号已成对,后面就多了一个不成对的单引号了!因此也就失败了!
- 利用反斜线(\)跳脱特殊字元,例如单引号与空白键,这也是OK的啦!
- 上面这三种格式在PATH里头的设定都是OK的!但是底下的例子就不见得啰!
- 知道了吧?如果没有双引号,那么变数成了啥?name的内容是$nameyes这个变数!
- 呵呵!我们可没有设定过nameyes这个变数呐!所以,应该是底下这样才对!
- 10.2.3 环境变量的功能
- 有许多可以使用的函式库功能被鸟哥取消啰!请自行查阅! |
- 看到了吗?提示字元变了!变的很有趣吧!其中,那个#85 比较有趣,
- 如果您再随便输入几次ls 后,该数字就会增加喔!为啥?上面有说明滴! |
- 错误代码回传值依据软体而有不同,我们可以利用这个代码来搜寻错误的原因喔!
- 咦!怎么又变成正确了?这是因为”?” 只与『上一个执行指令』有关,
- 所以,我们上一个指令是执行『 echo $? 』,当然没有错误,所以是0 没错! |
- 后面的鸟哥就都直接省略了!不然….浪费版面~ ^_^ |
- 由于一个# 仅删除掉最短的那个,因此他删除的情况可以用底下的删除线来看:
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin- 嘿!多加了一个# 变成## 之后,他变成『删除掉最长的那个资料』!亦即是:
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin |- 注意啊!最后面一个目录不见去!
- 这个%符号代表由最后面开始向前删除!所以上面得到的结果其实是来自如下:
- /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin
:/home/dmtsai/bin - 同样的, %% 代表的则是最长的符合字串,所以结果其实是来自如下:
- /usr/local/bin
:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin| - 这个部分就容易理解的多了!关键字在于那两个斜线,两斜线中间的是旧字串
- 后面的是新字串,所以结果就会出现如上述的特殊字体部分啰!
- 如果是两条斜线,那么就变成所有符合的内容都会被取代喔! |
- 10.3 命令别名、历史命令
- 前面省略
- 列出的资讯当中,共分两栏,第一栏为该指令在这个shell 当中的代码,
- 另一个则是指令本身的内容喔!至于会秀出几笔指令记录,则与HISTSIZE 有关!
- 在预设的情况下,会将历史纪录写入~/.bash_history当中!
- 10.4 Bash Shell 的操作环境
- 10.4.2 bash 的进站与欢迎信息: /etc/issue, /etc/motd
- 10.4.3 bash 的环境设定档
- .bash_profile
- Get the aliases and functions
- User specific environment and startup programs
- .bashrc
- User specific aliases and functions
- Source global definitions
- 10.4.4 终端机的环境设定: stty, set
- 那个$- 变数内容就是set 的所有设定啦!bash 预设是himBH 喔!
- 预设情况下,未设定/未宣告的变数都会是『空的』,不过,若设定-u 参数,
- 那么当使用未设定的变数时,就会有问题啦!很多的shell 都预设启用-u 参数。
- 若要取消这个参数,输入set +u 即可!
- 看见否?要输出的指令都会先被列印到萤幕上喔!前面会多出+ 的符号! |
- 10.5 资料流重导向
- 注意看,这两个档案的大小会一模一样!几乎像是使用cp来复制一般! |
- ls 很干脆的说明找不到该目录,但并没有touch 的错误,表示touch 并没有执行
- 10.6 管线命令(pipe)
- 1 | 2 | 3 | 4 | 5 | 6 |
- 如同上面的数字显示,我们是以『 : 』作为分隔,因此会出现/home/dmtsai/.local/bin
- 那么如果想要列出第3与第5呢?,就是这样:
- 注意看,每个资料都是排列整齐的输出!如果我们不想要『 declare -x 』时,就得这么做:
- 知道怎么回事了吧?用-c 可以处理比较具有格式的输出资料!
- 我们还可以指定某个范围的值,例如第12-20 的字元,就是cut -c 12-20 等等!
- last 可以输出『帐号/终端机/来源/日期时间』的资料,并且是排列整齐的
- 由输出的结果我们可以发现第一个空白分隔的栏位代表帐号,所以使用如上指令:
- 但是因为root pts/1 之间空格有好几个,并非仅有一个,所以,如果要找出
- pts/1 其实不能以cut -d ‘ ‘ -f 1,2 喔!输出的结果会不是我们想要的。 |
- 在取出root之后,利用上个指令cut的处理,就能够仅取得第一栏啰!
- 神奇的是,如果加上—color=auto的选项,找到的关键字部分会用特殊颜色显示喔! |
- 鸟哥省略很多的输出~由上面的资料看起来, sort是预设『以第一个』资料来排序,
- 而且预设是以『文字』型态来排序的喔!所以由a 开始排到最后啰!
- 看到特殊字体的输出部分了吧?怎么会这样排列啊?呵呵!没错啦~若单纯以第三栏位来处理则是:
- 如果是以文字型态来排序的话,原本就会是这样,想要使用数字排序:
- cat /etc/passwd | sort -t ‘:’ -k 3,3 -n
- 这样才行啊!用那个-n 来告知sort 以数字来排序啊!
- 从上面的结果可以发现reboot 有4 次, root 登入则有7 次!大部分是以dmtsai 来操作!
- wtmp 与第一行的空白都是last 的预设字元,那两个可以忽略的! |
- 输出的三个数字中,分别代表: 『行、字数、字元数』
- 由于last会输出空白行, wtmp, unknown, reboot等无关帐号登入的资讯,因此,我利用
- grep 取出非空白行,以及去除上述关键字那几行,再计算行数,就能够了解啰! |
- 这个范例可以让我们将last的输出存一份到last.list档案中;
- 这个范例则是将ls的资料存一份到~/homefile ,同时萤幕也有输出讯息!
- 要注意!tee后接的档案会被覆盖,若加上-a这个选项则能将讯息累加。 |
- 事实上,没有加上单引号也是可以执行的,如:『 last | tr [az] [AZ] 』
- 那个\r指的是DOS的断行字元,关于更多的字符,请参考man tr
- 处理过后,发现档案大小与原本的/etc/passwd就一致了! |
- 嘿嘿!如此一来, [tab]按键会被取代成为空白键,输出就美观多了! |
- 由输出的资料可以发现这两个档案的最左边栏位都是相同帐号!且以: 分隔
- 透过上面这个动作,我们可以将两个档案第一栏位相同者整合成一列!
- 第二个档案的相同栏位并不会显示(因为已经在最左边的栏位出现了啊!)
- 从上面可以看到,确实有相同的部分喔!赶紧来整合一下!
- 同样的,相同的栏位部分被移动到最前面了!所以第二个档案的内容就没再显示。
- 请读者们配合上述显示两个档案的实际内容来比对! |
- 注意喔!同一行中间是以[tab] 按键隔开的!
- 这个例子的重点在那个 “-**“ **的使用!那玩意儿常常代表stdin喔! |
- 行首的代表标志为^ ,这个我们留待下节介绍!先有概念即可!
- 发现差别了吗?没错~ [tab] 按键可以被cat -A 显示成为^I
- 仔细看一下上面的数字说明,因为我是以6 个字元来代表一个[tab] 的长度,所以,
- MAN… 到/usr 之间会隔12 (两个[tab]) 个字元喔!如果tab 改成9 的话,
- 情况就又不同了!这里也不好理解~您可以多设定几个数字来查阅就晓得! |
- 那个档名可以随意取的啦!我们只要写上前导文字,小档案就会以
- xxxaa, xxxab, xxxac 等方式来建立小档案的!
- 很简单吧?就用资料流重导向就好啦!简单!
- 重点在那个- 啦!一般来说,如果需要stdout/stdin 时,但偏偏又没有档案,
- 有的只是- 时,那么那个- 就会被当成stdin 或stdout ~ |
- 虽然使用$(cmd)可以预先取得参数,但可惜的是, id这个指令『仅』能接受一个参数而已!
- 所以上述的这个指令执行会出现错误!根本不会显示用户的ID 啊!
- 因为id并不是管线命令,因此在上面这个指令执行后,前面的东西通通不见!只会执行id!
- 依旧会出现错误!这是因为xargs一口气将全部的资料通通丢给id处理~但id就接受1个啊最多!
- 透过-n 来处理,一次给予一个参数,因此上述的结果就OK 正常的显示啰!
- 呵呵!这个-p 的选项可以让使用者的使用过程中,被询问到每个指令是否执行!
- 仔细与上面的案例做比较。也同时注意,那个-e’sync’是连在一起的,中间没有空白键。
- 上个例子当中,第六个参数是sync 啊,那么我们下达-e’sync’ 后,则分析到sync 这个字串时,
- 后面的其他stdin 的内容就会被xargs 舍弃掉了! |
- 聪明的读者应该会想到使用『 ls -l $(find /usr/sbin -perm /7000) 』来处理这个范例!
- 都OK!能解决问题的方法,就是好方法! |
10.1 认识BASH 这个Shell
10.1.1 硬件、核心与Shell
当你要电脑传输出来『音乐』的时候,你的电脑需要什么东西呢?
- 硬件:当然就是需要你的硬体有『音效卡晶片』这个配备,否则怎么会有声音;
- 核心管理:操作系统的核心可以支援这个晶片组,当然还需要提供晶片的驱动程式啰;
- 应用程式:需要使用者(就是你) 输入发生声音的指令!
也就是说,你必须要『输入』一个指令之后, 『硬件』才会透过你下达的指令来工作!那么硬体如何知道你下达的指令呢?那就是kernel (核心)的控制工作了!也就是说,我们必须要透过『 Shell 』将我们输入的指令与Kernel沟通,好让Kernel可以控制硬体来正确无误的工作!
基本上,我们可以透过底下这张图来说明一下:
图10.1.1、硬体、核心与使用者的相关性图示
我们可以发现应用程序其实是在最外层,就如同鸡蛋的外壳一样,因此也就被称呼为壳程式( shell)!
其实壳程式的功能只是提供使用者操作系统的一个介面,因此这个壳程式需要可以呼叫其他软体才好。
我们在第四章到第九章提到过很多指令,包括man, chmod, chown, vi, fdisk, mkfs 等等指令,这些指令都是独立的应用程式, 但是我们可以透过壳程式(就是指令列模式) 来操作这些应用程式,让这些应用程式呼叫核心来运作所需的工作!
| 只要能够操作应用程式的介面都能够称为壳程式。
狭义的壳程式指的是:指令列方面的软体,包括本章要介绍的bash 等。
广义的壳程式:则包括图形介面的软体!因为图形介面其实也能够操作各种应用程式来呼叫核心工作啊!
不过在本章中,我们主要还是在使用bash 啦! | | —- |
10.1.5 查询指令是否为Bash shell 的内建命令: type
我们在第四章提到关于 Linux的线上说明文件部分,也就是man page的内容,那么bash中的说明文件就是使用type指令来查看?
| [dmtsai@study ~]$ type [-tpa] name 选项与参数: :不加任何选项与参数时,type 会显示出name 是外部指令还是bash 内建指令 -t :当加入-t 参数时,type 会将name 以底下这些字眼显示出他的意义: file :表示为外部指令; alias :表示该指令为命令别名所设定的名称; builtin :表示该指令为bash 内建的指令功能; -p :如果后面接的name 为外部指令时,才会显示完整档名; -a :会由PATH 变量定义的路径中,将所有含name 的指令都列出来,包含alias
范例一:查询一下ls这个指令是否为bash内建?
[dmtsai@study ~]$ type ls
ls is aliased to ls --color=auto' <==未加任何参数,列出ls的最主要使用情况
[dmtsai@study ~]$ type -t ls
alias < ==仅列出ls执行时的依据
[dmtsai@study ~]$ type -a ls
ls is aliased tols —color=auto’ <==最先使用aliase
ls is /usr/bin/ls <= =还有找到外部指令在/bin/ls
范例二:那么cd呢? [dmtsai@study ~]$ type cd cd is a shell builtin <==看到了吗?cd是shell内建指令 | | —- |
由于利用type搜寻后面的名称时,如果后面接的名称并不能以**执行档**的状态被找到,那么该名称是不会被显示出来的。也就是说, type主要在找出『执行档』而不是一般档案档名!所以,这个type也可以用来作为类似which 指令的用途啦!找指令用的!
| 组合键 | 功能与示范 |
|---|---|
| [ctrl]+u / [ctrl]+k | 分别是从游标处向前删除指令串([ctrl]+u) 及向后删除指令串([ctrl]+k)。 |
| [ctrl]+a / [ctrl]+e | 分别是让游标移动到整个指令串的最前面([ctrl]+a) 或最后面([ctrl]+e)。 |
10.2 Shell 的变量功能
什么是『变量』:就是让某一个特定字串代表不固定的内容。
例如:『 y = ax + b 』这东西,在等号左边的(y)就是变量,在等号右边的(ax+b)就是变量内容。要注意的是,左边是未知数,右边是已知数喔! 讲的更简单一点,我们可以『用一个简单的”字眼”来取代另一个比较复杂或者是容易变动的资料』。这有什么好处啊?最大的好处就是『方便!』。
最后我们就简单的对『什么是变量』作个简单定义好了: 『变量就是以一组文字或符号等,来取代一些设定或者是一串保留的资料!』
例如:我设定了『myname』就是『VBird』,所以当你读取myname这个变量的时候,系统自然就会知道!哈!那就是VBird啦! 那么如何『显示变量』呢?这就需要使用到echo这个指令啦!
10.2.2 变量的取用与设定:echo, 变量设定规则, unset
说的口沫横飞的,也不知道『变量』与『变量代表的内容』有啥关系?那我们就将『变量』的『内容』拿出来给您瞧瞧好了。你可以利用echo 这个指令来取用变量。
写法:echo $variable 或者echo ${variable} (例如:echo $PATH/echo ${PATH})
现在我们知道了变量与变量内容之间的相关性了,好了,那么我要如何『设定』或者是『修改』 某个变量的内容?
- 答:使用“
=”来连接 变量 与 其对应的内容。 | [dmtsai@study ~]$ echo ${myname}
[dmtsai@study ~]$ myname=VBird [dmtsai@study ~]$ echo ${myname} VBird <==出现了!因为这个变量已经被设定了! | | —- |<==这里并没有任何资料~因为这个变量尚未被设定!是空的!
变量的设定规则
- 变量与变量内容以一个等号『=』来连结,如下所示:
『myname=VBird』
- 等号两边不能直接接空白字元,如下所示为错误:
~~『myname = VBird』或『myname=VBird Tsai』~~
- 变量名称只能是英文字母与数字,但是开头字元不能是数字,如下为错误:
~~『2myname=VBird』~~
- 变量内容若有空白字元可使用双引号『”』或单引号『’』将变量内容结合起来,但
- 双引号内的特殊字元如$等,可以保有原本的特性,如下所示:
『var="lang is $LANG"』则『echo $var』可得『lang is zh_TW.UTF-8』
- 单引号内的特殊字元则仅为一般字元(纯文字),如下所示:
『var='lang is $LANG'』则『echo $var』可得『lang is $LANG』
- 双引号内的特殊字元如$等,可以保有原本的特性,如下所示:
- 用跳脱字元『 \ 』将特殊符号(如[Enter], $, \,空白字元, ‘等)变成一般字元,如:
『``myname=VBird\ Tsai』
- 在一串指令的执行中,还需要其他额外的指令所提供的信息时,可以使用反单引号『
指令』或『$(指令)』。例如想要取得核心版本的设定:『version=$(uname -r)』再『echo $version』可得『3.10.0-229.el7.x86_64』
- 该变量为扩增变量内容时,则可用“$变量名称”或${变量}累加内容,如下所示:
『PATH="$PATH":/home/bin』或『PATH=${PATH} :/home/bin』
- 若该变量需要在其他子程序执行,则需要以export来使变量变成环境变量:
『export PATH』
- 通常大写字元为系统预设变量,自行设定变量可以使用小写字元,方便判断(纯粹依照使用者兴趣与嗜好) ;
- 取消变量的方法为使用unset:『unset变量名称』例如取消myname的设定:
『unset myname』
例如:
| 范例一:设定一变数name ,且内容为VBird [dmtsai@study ~]$ 12name=VBird bash: 12name=VBird: command not found… <==萤幕会显示错误!因为不能以数字开头! [dmtsai@study ~]$ name = VBird <==还是错误!因为有空白! [dmtsai@study ~]$ name=VBird <==OK的啦!
范例二:承上题,若变数内容为VBird’s name呢,就是变数内容含有特殊符号时: [dmtsai@study ~]$ name=VBird’s name
单引号与双引号必须要成对,在上面的设定中仅有一个单引号,因此当你按下enter后,
你还可以继续输入变数内容。这与我们所需要的功能不同,失败啦!
记得,失败后要复原请按下[ctrl]-c结束!
[dmtsai@study ~]$ name=”VBird’s name” <==OK的啦!
指令是由左边向右找→,先遇到的引号先有用,因此如上所示,单引号变成一般字元!
[dmtsai@study ~]$ name=’VBird’s name’ <==失败的啦!
因为前两个单引号已成对,后面就多了一个不成对的单引号了!因此也就失败了!
[dmtsai@study ~]$ name=VBird\‘s\ name <==OK的啦!
利用反斜线(\)跳脱特殊字元,例如单引号与空白键,这也是OK的啦!
范例三:我要在PATH这个变数当中『累加』:/home/dmtsai/bin这个目录 [dmtsai@study ~]$ PATH=$PATH:/home/dmtsai/bin [dmtsai@study ~]$ PATH=” $PATH”:/home/dmtsai/bin [dmtsai@study ~]$ PATH=${PATH}:/home/dmtsai/bin
上面这三种格式在PATH里头的设定都是OK的!但是底下的例子就不见得啰!
范例四:承范例三,我要将name的内容多出”yes”呢? [dmtsai@study ~]$ name=$nameyes
知道了吧?如果没有双引号,那么变数成了啥?name的内容是$nameyes这个变数!
呵呵!我们可没有设定过nameyes这个变数呐!所以,应该是底下这样才对!
[dmtsai@study ~]$ name=”$name”yes [dmtsai@study ~]$ name=${name}yes <==以此例较佳!
范例五:如何让我刚刚设定的name=VBird可以用在下个shell的程序? [dmtsai@study ~]$ name=VBird [dmtsai@study ~]$ bash <==进入到所谓的子程序 [dmtsai@study ~]$ echo $name <==子程序:再次的echo一下; <= =嘿嘿!并没有刚刚设定的内容喔! [dmtsai@study ~]$ exit <==子程序:离开这个子程序 [dmtsai@study ~]$ export name [dmtsai@study ~]$ bash <==进入到所谓的子程序 [dmtsai@study ~] $ echo $name <==子程序:在此执行! VBird <==看吧!出现设定值了! [dmtsai@study ~]$ exit <==子程序:离开这个子程序 | | —- |
『子程序』
什么是『子程序』呢?就是说,在我目前这个shell的情况下,去启用另一个新的shell ,**新的那个shell**就是子程序!在一般的状态下,父程序的自订变量是无法在子程序内使用的。但是透过export将变量变成环境变量后,就能够在子程序底下应用了!至于程序的相关概念,我们会在第十六章程序管理当中提到的!
10.2.3 环境变量的功能
用env 观察环境变量与常见环境变量说明
| 范例一:列出目前的shell环境下的所有环境变数与其内容。 [dmtsai@study ~]$ env HOSTNAME=study.centos.vbird <==这部主机的主机名称 TERM=xterm <==这个终端机使用的环境是什么类型 SHELL=/bin/bash <==目前这个环境下,使用的Shell是哪一个程式? HISTSIZE=1000 <== 『记录指令的笔数』在CentOS预设可记录1000笔 OLDPWD=/home/dmtsai <==上一个工作目录的所在 LCALL=en_US.utf8 <==由于语系的关系,鸟哥偷偷丢上来的一个设定 USER=dmtsai <==使用者的名称啊! LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd= 40;33;01: or=40;31;01:mi=01;05;37;41:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st= 37;44:ex=01;32: *.tar=01… <==一些颜色显示 MAIL=/var/spool/mail/dmtsai <==这个使用者所取用的mailbox位置 PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin PWD=/home/dmtsai <==目前使用者所在的工作目录(利用pwd取出!) LANG=zh_TW.UTF-8 <==这个与语系有关,底下会再介绍! HOME=/home/dmtsai <==这个使用者的家目录啊! LOGNAME=dmtsai <==登入者用来登入的帐号名称 =/usr/bin/env <==上一次使用的指令的最后一个参数(或指令本身) | | —- |
env 是environment (环境) 的简写。上面的例子当中,是列出来所有的环境变量。当然,如果使用export 也会是一样的内容。
那么上面这些变量有些什么功用呢?底下我们就一个一个来分析分析!
**
- 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宣告数值类型,然后这样做就可以了: | [dmtsai@study ~]$ declare -i number=$RANDOM*10/32768 ; echo $number 8 <==此时会随机取出0~9之间的数值喔! | | —- |
用set 观察所有变量(含环境变量与自订变量)
set 除了环境变量之外, 还会将其他在bash 内的变量通通显示出来哩!资讯很多,底下鸟哥仅列出几个重要的内容:
| [dmtsai@study ~]$ set BASH=/bin/bash <== bash的主程式放置路径 BASH_VERSINFO=([0]=”4” [1]=”2” [2]=”46” [3]=”1” [4]=”release” [5]=”x86_64-redhat-linux-gnu “) BASH_VERSION=’4.2.46(1)-release’ <==这两行是bash的版本啊! COLUMNS=90 <==在目前的终端机环境下,使用的栏位有几个字元长度 HISTFILE=/home/dmtsai/.bash_history <==历史命令记录的放置档案,隐藏档 HISTFILESIZE=1000 <= =存起来(与上个变数有关)的档案之指令的最大纪录笔数。 HISTSIZE=1000 <==目前环境下,记忆体中记录的历史命令最大笔数。 IFS=$’ \t\n’ <==预设的分隔符号 LINES=20 <==目前的终端机下的最大行数 MACHTYPE=x86_64-redhat-linux-gnu <==安装的机器类型 OSTYPE= linux-gnu <==作业系统的类型! PS1=’[\u@\h \W]\$ ‘ <== PS1就厉害了。这个是命令提示字元,也就是我们常见的 [root@www ~]#或[dmtsai ~]$的设定值啦!可以更动的! PS2=’> ‘ <==如果你使用跳脱符号(\)第二行以后的提示字元也 $ <==目前这个shell所使用的PID ? <==刚刚执行完指令的回传值。 …
有许多可以使用的函式库功能被鸟哥取消啰!请自行查阅! |
| —- |
『基本上,在Linux预设的情况中,使用{大写的字母}来设定的变量一般为系统内定需要的变量』。
那么上头那些变量当中,有哪些是比较重要的?
PS1:(提示字符的设定)
这是PS1 (数字的1不是英文字母),这个东西就是我们的『命令提示字元』当我们每次按下[Enter]按键去执行某个指令后,最后要再次出现提示字元时,就会主动去读取这个变量值了。上头PS1内显示的是一些特殊符号,这些特殊符号可以显示不同的资讯,每个distributions的bash预设的PS1变量内容可能有些许的差异,不要紧,『习惯你自己的习惯』就好了。你可以用man bash ( 注3 )去查询一下PS1的相关说明,以理解底下的一些符号意义。
- \d :可显示出『星期月日』的日期格式,如:”Mon Feb 2”
- \H :完整的主机名称。举例来说,鸟哥的练习机为『study.centos.vbird』
- \h :仅取主机名称在第一个小数点之前的名字,如鸟哥主机则为『study』后面省略
- \t :显示时间,为24 小时格式的『HH:MM:SS』
- \T :显示时间,为12 小时格式的『HH:MM:SS』
- \A :显示时间,为24 小时格式的『HH:MM』
- \@ :显示时间,为12 小时格式的『am/pm』样式
- \u :目前使用者的帐号名称,如『dmtsai』;
- \v :BASH 的版本资讯,如鸟哥的测试主机版本为4.2.46(1)-release,仅取『4.2』显示
- \w :完整的工作目录名称,由根目录写起的目录名称。但家目录会以~ 取代;
- \W :利用basename 函数取得工作目录名称,所以仅会列出最后一个目录名。
- # :下达的第几个指令。
- \$ :提示字元,如果是root 时,提示字元为# ,否则就是$ 。
让我们来看看CentOS 预设的PS1 内容吧:『[\u@\h \W]\$ 』,现在你知道那些反斜线后的资料意义了吧?要注意那个反斜线后的资料为PS1 的特殊功能,与bash 的变量设定没关系!不要搞混了!那你现在知道为何你的命令提示字元是:『 [dmtsai@study ~]$ 』了吧?好了,那么假设我想要有类似底下的提示字元:[dmtsai@study /home/dmtsai 16:50 #12]$
那个# 代表第12 次下达的指令。那么应该如何设定PS1 呢?可以这样啊:
| [dmtsai@study ~]$ cd /home [dmtsai@study home]$ PS1=’[\u@\h \w \A #\#]\$ ‘ [dmtsai@study /home 17:02 #85]$
看到了吗?提示字元变了!变的很有趣吧!其中,那个#85 比较有趣,
如果您再随便输入几次ls 后,该数字就会增加喔!为啥?上面有说明滴! |
| —- |
- $:(关于本shell 的PID)
钱字号本身也是个变量!代表的是『目前这个Shell 的执行绪代号』,亦即是所谓的PID (Process ID)。更多的程序观念,我们会在第四篇的时候提及。想要知道我们的shell 的PID ,就可以用:『 echo $$ 』即可!出现的数字就是你的PID 号码。
- ?:(关于上个执行指令的回传值)
在bash里面这个变量可重要的很!这个变量是:『**上一个执行的指令所回传的值**』,上面这句话的重点是『上一个指令』与『回传值』两个地方。当我们执行某些指令时,这些指令都会回传一个执行后的代码。一般来说,如果成功的执行该指令,则会回传一个0值,如果执行过程发生错误,就会回传『错误代码』才对!一般就是以非为0的数值来取代。我们以底下的例子来看看:
| [dmtsai@study ~]$ echo $SHELL /bin/bash <==可顺利显示!没有错误! [dmtsai@study ~]$ echo $? 0 <==因为没问题,所以回传值为0 [dmtsai@study ~]$ 12name=VBird bash: 12name=VBird: command not found… <==发生错误了!bash回报有问题 [dmtsai@study ~]$ echo $? 127 <==因为有问题,回传错误代码(非为0)
错误代码回传值依据软体而有不同,我们可以利用这个代码来搜寻错误的原因喔!
[dmtsai@study ~]$ echo $? 0
咦!怎么又变成正确了?这是因为”?” 只与『上一个执行指令』有关,
所以,我们上一个指令是执行『 echo $? 』,当然没有错误,所以是0 没错! |
| —- |
export: 自订变量转成环境变量
谈了env与set现在知道有所谓的环境变量与自订变量,那么这两者之间有啥差异呢?其实这两者的差异在于『 该变量是否会被子程序所继续引用』。
问:啥是父程序?子程序?这就得要了解一下指令的下达行为了。
- 当你登入Linux 并取得一个bash 之后,你的bash 就是一个独立的程序,这个程序的识别使用的是一个称为程序识别码,被称为PID 的就是。
- 接下来你在这个bash 底下所下达的任何指令都是由这个bash 所衍生出来的,那些被下达的指令就被称为子程序了。
可以用底下的图示来简单的说明一下父程序与子程序的概念:

图10.2.3、程序相关性示意图
我们在原本的bash 底下执行另一个bash ,结果操作的环境介面会跑到第二个bash 去(就是子程序), 那原本的bash 就会在暂停的情况(睡着了,就是sleep)。整个指令运作的环境是实线的部分!若要回到原本的bash 去, 就只有将第二个bash 结束掉(下达exit 或logout) 才行。
问:程序概念与变量有啥关系啊?
因为子程序仅会继承父程序的环境变量,子程序不会继承父程序的自订变量啦!
所以你在原本bash的自订变量在进入了子程序后就会消失不见,一直到你离开子程序并回到原本的父程序后,这个变量才会又出现!
问:如何将自订变量 转换为 环境变量?
使用:export
| [dmtsai@study ~]$ export 变数名称 |
|---|
如果仅下达export而没有接变量时,那么此时将会把所有的『环境变量』秀出来!例如:
| [dmtsai@study ~]$ export declare -x HISTSIZE=”1000” declare -x HOME=”/home/dmtsai” declare -x HOSTNAME=”study.centos.vbird” declare -x LANG=”zh_TW.UTF-8” declare -x LC_ALL=”en_US.utf8”
后面的鸟哥就都直接省略了!不然….浪费版面~ ^_^ |
| —- |
10.2.4 影响显示结果的 语系变量(locale)
当我们使用man command的方式去查询某个资料的说明档时,该说明档的内容可能会因为我们使用的语系不同而产生乱码。另外,利用ls查询档案的时间时,也可能会有乱码出现在时间的部分。那个问题其实就是语系的问题啦。
那么我们的Linux 到底支援了多少的语系呢?这可以由locale 这个指令来查询到!
| [dmtsai@study ~]$ locale -a ….(前面省略)…. zh_TW zh_TW.big5 <==大五码的中文编码 zh_TW.euctw zh_TW.utf8 <==万国码的中文编码 zu_ZA zu_ZA.iso88591 zu_ZA.utf8 | | —- |
问:如何修订这些编码呢?
| [dmtsai@study ~]$ locale <==后面不加任何选项与参数即可! LANG=en_US <==主语言的环境 LC_CTYPE=”en_US” <==字元(文字)辨识的编码 LC_NUMERIC=”en_US” <==数字系统的显示讯息 LC_TIME=”en_US” <==时间系统的显示资料 LC_COLLATE=”en_US” <==字串的比较与排序等 LC_MONETARY=”en_US” <==币值格式的显示等 LC_MESSAGES=”en_US” <==讯息显示的内容,如功能表、错误讯息等 LC_ALL= <==整体语系的环境 ….(后面省略)…. | | —- |
基本上,你可以逐一设定每个与语系有关的变量资料.
但事实上,如果其他的语系变量都未设定,且你有设定LANG或者是LC_ALL时,则其他的语系变量就会被这两个变量所取代!
你当然可以让每个使用者自己去调整自己喜好的语系,但是整体系统预设的语系定义在哪里呢?其实就是在/etc/locale.conf !这个档案在CentOS 7.x 的内容有点像这样:
| [dmtsai@study ~]$ cat /etc/locale.conf LANG=zh_TW.utf8 LC_NUMERIC=zh_TW.UTF-8 LC_TIME=zh_TW.UTF-8 LC_MONETARY=zh_TW.UTF-8 LC_PAPER=zh_TW.UTF-8 LC_MEASUREMENT=zh_TW.UTF-8 | | —- |
10.2.5 变量的有效范围
环境变量=全域变量
自订变量=区域变量
问:为什么环境变量的资料可以被子程序所引用呢?**这是因为记忆体配置的关系!理论上是这样的:
- 当启动一个shell,操作系统会分配一块内存给shell 使用,此内存块内的变量可让子程序取用;
- 若在父程序利用export 功能,可以让自订变量的内容写到上述的记忆区块当中(环境变量);
- 当载入另一个shell 时(亦即启动子程序,而离开原本的父程序了),子shell 可以将父shell 的环境变量所在的记忆区块导入自己的环境变量区块当中。
10.2.6 变量键盘读取、阵列与宣告: read, array, declare
上面提到的变量设定功能,都是由指令列直接设定的,那么,可不可以让使用者能够经由键盘输入?
- 是否记得某些程式执行的过程当中,会等待使用者输入”yes/no” 之类的讯息啊?在bash 里面也有相对应的功能喔!此外,我们还可以宣告这个变量的属性,例如:阵列或者是数字等等的。
read
要读取来自键盘输入的变量,就是用read 这个指令了。
| [dmtsai@study ~]$ read [-pt] variable 选项与参数: -p :后面可以接提示字元! -t :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦!
范例一:让使用者由键盘输入一内容,将该内容变成名为atest的变数 [dmtsai@study ~]$ read atest This is a test <==此时游标会等待你输入!请输入左侧文字看看 [dmtsai@study ~]$ echo ${atest} This is a test <==你刚刚输入的资料已经变成一个变数内容!
范例二:提示使用者30秒内输入自己的大名,将该输入字串作为名为named的变数内容 [dmtsai@study ~]$ read -p “Please keyin your name: “ -t 30 named Please keyin your name: VBird Tsai <==注意看,会有提示字元喔! [dmtsai@study ~]$ echo ${named} VBird Tsai <==输入的资料又变成一个变数的内容了! | | —- |
declare / typeset
declare或typeset是一样的功能,就是在『宣告变量的类型』。
- 如果使用declare后面并没有接任何参数,那么bash就会主动的将所有的变量名称与内容通通叫出来,就好像使用set一样啦! | [dmtsai@study ~]$ declare [-aixr] variable 选项与参数: -a :将后面名为variable 的变数定义成为阵列(array) 类型 -i :将后面名为variable 的变数定义成为整数数字(integer) 类型 -x :用法与export 一样,就是将后面的variable 变成环境变数; -r :将变数设定成为readonly 类型,该变数不可被更改内容,也不能unset
范例一:让变数sum进行100+300+50的加总结果 [dmtsai@study ~]$ sum=100+300+50 [dmtsai@study ~]$ echo ${sum} 100+300+50 <==咦!怎么没有帮我计算加总?因为这是文字型态的变数属性啊! [dmtsai@study ~]$ declare -i sum=100+300+50 [dmtsai@study ~]$ echo ${sum} 450 <==了乎?? | | —- |
由于在预设的情况底下, bash 对于变量有几个基本的定义:
- 变量类型预设为『字串』,所以若不指定变量类型,则1+2 为一个『字串』而不是『计算式』。所以上述第一个执行的结果才会出现那个情况的;
- bash 环境中的数值运算,预设最多仅能到达整数形态,所以1/3 结果是0;
现在你晓得为啥你需要进行变量宣告了吧?如果需要非字串类型的变量,那就得要进行变量的宣告才行啦!底下继续来玩些其他的declare 功能。
| 范例二:将sum变成环境变数 [dmtsai@study ~]$ declare -x sum [dmtsai@study ~]$ export | grep sum declare -ix sum=”450” <==果然出现了!包括有i与x的宣告!
范例三:让sum变成唯读属性,不可更动! [dmtsai@study ~]$ declare -r sum [dmtsai@study ~]$ sum=testing -bash: sum: readonly variable <==老天爷~不能改这个变数了!
范例四:让sum变成非环境变数的自订变数吧! [dmtsai@study ~]$ declare +x sum <==将-变成+可以进行『取消』动作 [dmtsai@study ~]$ declare -p sum <== -p可以单独列出变数的类型 declare -ir sum=”450” <==看吧!只剩下i, r的类型,不具有x啰! | | —- |
阵列(array) 变量类型
在bash 里头,阵列的设定方式是: **var[index]=content**
- 意思是说,我有一个阵列名称为var ,而这个阵列的内容为var[1]=小明, var[2]=大明, var[3]=好明…. 等等,那个index 就是一些数字啦,重点是用中刮号([ ]) 来设定的。目前我们bash 提供的是一维阵列。 | 范例:设定上面提到的var[1] ~ var[3]的变数。 [dmtsai@study ~]$ var[1]=”small min” [dmtsai@study ~]$ var[2]=”big min” [dmtsai@study ~]$ var[3]=”nice min” [dmtsai @study ~]$ echo “${var[1]}, ${var[2]}, ${var[3]}” small min, big min, nice min | | —- |
10.2.7 与档案系统及程序的限制关系: ulimit
想像一个状况:我的Linux主机里面同时登入了十个人,这十个人不知怎么搞的,同时开启了100个档案,每个档案的大小约10MBytes ,请问一下,我的Linux主机的记忆体要有多大才够?1010010 = 10000 MBytes = 10GBytes ..,系统会挂的。
为了要预防这个情况的发生,所以我们的bash是可以『限制使用者的某些系统资源』的,包括可以开启的档案数量,可以使用的CPU时间,可以使用的记忆体总量等等。
如何设定?用**ulimit**吧!
| [dmtsai@study ~]$ ulimit [-SHacdfltu] [配额] 选项与参数: -H :hard limit ,严格的设定,必定不能超过这个设定的数值; -S :soft limit ,警告的设定,可以超过这个设定值,但是若超过则有警告讯息。 在设定上,通常soft 会比hard 小,举例来说,soft 可设定为80 而hard 设定为100,那么你可以使用到90 (因为没有超过100),但介于80~100 之间时, 系统会有警告讯息通知你! -a :后面不接任何选项与参数,可列出所有的限制额度; -c :当某些程式发生错误时,系统可能会将该程式在记忆体中的资讯写成档案(除错用), 这种档案就被称为核心档案(core file)。此为限制每个核心档案的最大容量。 -f :此shell 可以建立的最大档案容量(一般可能设定为2GB)单位为Kbytes -d :程序可使用的最大断裂记忆体(segment)容量; -l :可用于锁定(lock) 的记忆体量 -t :可使用的最大CPU 时间(单位为秒) -u :单一使用者可以使用的最大程序(process)数量。
范例一:列出你目前身份(假设为一般帐号)的所有限制资料数值 [dmtsai@study ~]$ ulimit -a core file size (blocks, -c) 0 <==只要是0就代表没限制 data seg size (kbytes, -d) unlimited scheduling priority (-e) 0 file size (blocks, -f) unlimited <==可建立的单一档案的大小 pending signals (-i) 4903 max locked memory (kbytes, -l) 64 max memory size (kbytes, -m) unlimited open files (-n) 1024 <==同时可开启的档案数量 pipe size (512 bytes, -p) 8 POSIX message queues (bytes, -q) 819200 real-time priority (-r) 0 stack size (kbytes, -s) 8192 cpu time (seconds, -t) unlimited max user processes (-u) 4096 virtual memory (kbytes, -v) unlimited file locks (-x) unlimited
范例二:限制使用者仅能建立10MBytes以下的容量的档案 [dmtsai@study ~]$ ulimit -f 10240 [dmtsai@study ~]$ ulimit -a | grep ‘file size’ core file size (blocks, -c) 0 file size (blocks, -f) 10240 <==最大量为10240Kbyes,相当10Mbytes
[dmtsai@study ~]$ dd if=/dev/zero of=123 bs=1M count=20 File size limit exceeded (core dumped) <==尝试建立20MB的档案,结果失败了!
[dmtsai@study ~]$ rm 123 <==赶快将这个档案删除啰!同时你得要登出再次的登入才能解开10M的限制 | | —- |
10.2.8 变量内容的删除、取代与替换(Optional)
变量内容的删除、取代
删除:
1.从前向后删除:#
| 范例一:先让小写的path自订变数设定的与PATH内容相同 [dmtsai@study ~]$ path=${PATH} [dmtsai@study ~]$ echo ${path} /usr/local/bin: / usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
范例二:假设我不喜欢local/bin,所以要将前1个目录删除掉,如何显示? [dmtsai@study ~]$ echo ${path#/*local/bin:} /usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin | | —- |
上面这个范例很有趣的!他的重点可以用底下这张表格来说明:
| ${ variable#/*local/bin: } 上面的特殊字体部分是关键字!用在这种删除模式所必须存在的
${ variable #/*local/bin:} 这就是原本的变数名称,以上面范例二来说,这里就填写path这个『变数名称』啦!
${variable # /*local/bin:} 这是重点!代表『从变数内容的最前面开始向右删除』,且仅删除最短的那个
${variable# /local/bin: } 代表要被删除的部分,由于#代表由前面开始删除,所以这里便由开始的/写起。 需要注意的是,我们还可以透过万用字元 来取代0 到无穷多个任意字元
以上面范例二的结果来看, path这个变数被删除的内容如下所示:
/usr/local/bin: /usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/. local/bin:/home/dmtsai/bin |
| —- |
再来看看,#的应用:
| 范例三:我想要删除前面所有的目录,仅保留最后一个目录 [dmtsai@study ~]$ echo ${path#/*:} /usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
由于一个# 仅删除掉最短的那个,因此他删除的情况可以用底下的删除线来看:
/usr/local/bin: /usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
[dmtsai@study ~]$ echo ${path##/*:} /home/dmtsai/bin
嘿!多加了一个# 变成## 之后,他变成『删除掉最长的那个资料』!亦即是:
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin: /home/dmtsai/bin |
| —- |
因为在PATH 这个变量的内容中,每个目录都是以冒号『:』隔开的, 所以要从头删除掉目录就是介于斜线(/) 到冒号(:) 之间的资料!但是PATH 中不止一个冒号(:) 啊!所以# 与## 就分别代表:
**#**:符合取代文字的『最短的』那一个;**##**:符合取代文字的『最长的』那一个;
2.从后向前删除:%
| 范例四:我想要删除最后面那个目录,亦即从:到bin为止的字串 [dmtsai@study ~]$ echo ${path%:*bin} /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin
注意啊!最后面一个目录不见去!
这个%符号代表由最后面开始向前删除!所以上面得到的结果其实是来自如下:
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin :/home/dmtsai/bin
范例五:那如果我只想要保留第一个目录呢? [dmtsai@study ~]$ echo ${path%%:*bin} /usr/local/bin
同样的, %% 代表的则是最长的符合字串,所以结果其实是来自如下:
/usr/local/bin :/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin |
| —- |
取代
| 范例六:将path的变数内容内的sbin取代成大写SBIN: [dmtsai@study ~]$ echo ${path/sbin/SBIN} /usr/local/bin:/usr/bin:/usr/local/ SBIN :/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
这个部分就容易理解的多了!关键字在于那两个斜线,两斜线中间的是旧字串
后面的是新字串,所以结果就会出现如上述的特殊字体部分啰!
[dmtsai@study ~]$ echo ${path//sbin/SBIN} /usr/local/bin:/usr/bin:/usr/local/ SBIN :/usr/ SBIN :/home/dmtsai/.local/bin :/home/dmtsai/bin
如果是两条斜线,那么就变成所有符合的内容都会被取代喔! |
| —- |
做一个总结说明:
| 变数设定方式 | 说明 |
|---|---|
| ${变数#关键字} ${变数##关键字} |
若变数内容从头开始的资料符合『关键字』,则将符合的最短资料删除 若变数内容从头开始的资料符合『关键字』,则将符合的最长资料删除 |
| ${变数%关键字} ${变数%%关键字} |
若变数内容从尾向前的资料符合『关键字』,则将符合的最短资料删除 若变数内容从尾向前的资料符合『关键字』,则将符合的最长资料删除 |
| ${变数/旧字串/新字串} ${变数//旧字串/新字串} |
若变数内容符合『旧字串』则『第一个旧字串会被新字串取代』 若变数内容符合『旧字串』则『全部的旧字串会被新字串取代』 |
变量的测试、内容替换
在某些时刻我们常常需要『判断』某个变量是否存在:
- 若变量存在则使用既有的设定,
- 若变量不存在则给予一个常用的设定。
我们举底下的例子来说明好了。
| 范例一:测试一下是否存在username这个变数,若不存在则给予username内容为root [dmtsai@study ~]$ echo ${username} <==由于出现空白,所以username可能不存在,也可能是空字串 [dmtsai@study ~]$ username=${username-root} [dmtsai@study ~]$ echo ${username} root <==因为username没有设定,所以主动给予名为root的内容。 [dmtsai@study ~]$ username=”vbird tsai” <==主动设定username的内容 [dmtsai@study ~]$ username=${username-root} [dmtsai@study ~]$ 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 [dmtsai@study ~]$ username=”” [dmtsai@study ~]$ username=${username-root} [dmtsai@study ~]$ echo ${username} <==因为username被设定为空字串了!所以当然还是保留为空字串! [dmtsai@study ~]$ username=${username:-root} [dmtsai@study ~]$ echo ${username} root <==加上『 : 』后若变数内容为空或者是未设定,都能够以后面的内容替换! | | —- |
在大括号内有没有冒号『 : 』的差别是很大的!
- 加上冒号后,被测试的变量未被设定或者是已被设定为空字串时, 都能够用后面的内容(本例中是使用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 |
那个var 与str 为变量,我们想要针对str 是否有设定来决定var 的值喔!一般来说,
str:代表『str 没设定或为空的字串时』;至于str则仅为『没有该变量』。
根据上面这张表,我们来进行几个范例的练习。
- 首先让我们来测试一下,如果旧变量(str) 不存在时, 我们要给予新变量一个内容,若旧变量存在则新变量内容以旧变量来替换,结果如下: | 测试:先假设str不存在(用unset) ,然后测试一下减号(-)的用法: [dmtsai@study ~]$ unset str; var=${str-newvar} [dmtsai@study ~]$ echo “ var=${var}, str=${str}” var=newvar, str= <==因为str不存在,所以var为newvar
测试:若str已存在,测试一下var会变怎样?: [dmtsai@study ~]$ str=”oldvar”; var=${str-newvar} [dmtsai@study ~]$ echo “var=${var}, str=${str}” var=oldvar, str =oldvar <==因为str存在,所以var等于str的内容 | | —- |
关于减号(-) 其实上面我们谈过了!这里的测试只是要让你更加了解,这个减号的测试并不会影响到旧变数的内容。如果你想要将旧变数内容也一起替换掉的话,那么就使用等号(=) 吧!
| 测试:先假设str不存在(用unset) ,然后测试一下等号(=)的用法: [dmtsai@study ~]$ unset str; var=${str=newvar} [dmtsai@study ~]$ echo “ var=${var}, str=${str}” var=newvar, str=newvar <==因为str不存在,所以var/str均为newvar
测试:如果str已存在了,测试一下var会变怎样? [dmtsai@study ~]$ str=”oldvar”; var=${str=newvar} [dmtsai@study ~]$ echo “var=${var}, str=${str}” var=oldvar, str= oldvar <==因为str存在,所以var等于str的内容 | | —- |
10.3 命令别名、历史命令
10.3.1 命令别名设定: alias, unalias
查看目前所有的别名:
| [dmtsai@study ~]$ alias alias egrep=’egrep —color=auto’ alias fgrep=’fgrep —color=auto’ alias grep=’grep —color=auto’ alias l.=’ls -d .* —color=auto’ alias ll=’ls -l —color=auto’ alias lm=’ls -al | more’ alias ls=’ls —color=auto’ alias rm=’rm -i’ alias vi=’vim’ alias which=’alias | /usr/bin/which —tty-only —read-alias —show-dot —show-tilde’ | | —- |
设置别名:
举个例子来说,如果你要查询隐藏档,并且需要长的列出与一页一页翻看,那么需要下达『 ls -al | more』这个指令,要输入好几个单字!那可不可以使用lm来简化呢?当然可以,你可以在命令列下面下达:
| [dmtsai@study ~]$ alias lm=’ls -al | more’ |
|---|
要注意的是:『alias的定义规则与变数定义规则几乎相同』,所以你只要在alias后面加上你的 { 『``**别名』='指令选项...'** },以后你只要输入lm就相当于输入了ls -al|more这一串指令!很方便吧!
取消别名:
至于如果要取消命令别名的话,那么就使用unalias吧!例如要将刚刚的lm命令别名拿掉,就使用:
| [dmtsai@study ~]$ unalias lm |
|---|
命令别名与变数有什么不同:
- 命令别名是『新创一个新的指令,你可以直接下达该指令』的,
- 至于变数则需要使用类似『 echo 』指令才能够呼叫出变数的内容
10.3.2 历史命令:history
| [dmtsai@study ~]$ history [n] [dmtsai@study ~]$ history [-c] [dmtsai@study ~]$ history [-raw] histfiles 选项与参数: n :数字,意思是『要列出最近的n 笔命令列表』的意思! -c :将目前的shell 中的所有history 内容全部消除 -a :将目前新增的history 指令新增入histfiles 中,若没有加histfiles , 则预设写入~/.bash_history -r :将histfiles 的内容读到目前这个shell 的history 记忆中; -w :将目前的history 记忆内容写入histfiles 中!
范例一:列出目前记忆体内的所有history记忆 [dmtsai@study ~]$ history
前面省略
1017 man bash 1018 ll 1019 history 1020 history
列出的资讯当中,共分两栏,第一栏为该指令在这个shell 当中的代码,
另一个则是指令本身的内容喔!至于会秀出几笔指令记录,则与HISTSIZE 有关!
范例二:列出目前最近的3笔资料 [dmtsai@study ~]$ history 3 1019 history 1020 history 1021 history 3
范例三:立刻将目前的资料写入histfile当中 [dmtsai@study ~]$ history -w
在预设的情况下,会将历史纪录写入~/.bash_history当中!
[dmtsai@study ~]$ echo ${HISTSIZE} 1000 | | —- |
正常的情况下,历史命令的读取与记录是这样的:
当我们以bash 登入Linux 主机之后,系统会主动的由家目录的~/.bash_history 读取以前曾经下过的指令,那么~/.bash_history 会记录几笔资料呢?这就与你bash 的 HISTFILESIZE 这个变数设定值有关了!
假设我这次登入主机后,共下达过100次指令,『等我登出时,系统就会将101~1100这总共1000笔历史命令更新到~/.bash_history当中。』也就是说,历史命令在我登出时,会将最近的HISTFILESIZE笔记录到我的纪录档当中啦!
当然,也可以用history -w 强制立刻写入的!
- 那为何用『更新』两个字呢?因为~/.bash_history 记录的笔数永远都是HISTFILESIZE 那么多,旧的讯息会被主动的拿掉!仅保留最新的!
history 这个历史命令的其他应用:
| [dmtsai@study ~]$ !number
[dmtsai@study ~]$ !command
[dmtsai@study ~]$ !!
选项与参数:
number :执行第几笔指令的意思;
command :由最近的指令向前搜寻『指令串开头为command』的那个指令,并执行;
!! :就是执行上一个指令(相当于按↑按键后,按Enter)
[dmtsai@study ~]$ history 66 man rm 67 alias 68 man history 69 history [dmtsai@study ~]$ !66 <==执行第66笔指令 [dmtsai@study ~]$ !! <==执行上一个指令,本例中亦即!66 [dmtsai@study ~]$ !al <==执行最近以al为开头的指令(上头列出的第67个) | | —- |
同一帐号同时多次登入的history 写入问题
有些朋友在练习linux 的时候喜欢同时开好几个bash 介面,这些bash 的身份都是root 。这样会有~/.bash_history 的写入问题吗?
答:
想一想,因为这些bash 在同时以root 的身份登入, 因此所有的bash 都有自己的1000 笔记录在记忆体中。
因为等到登出时才会更新记录档,所以, 最后登出的那个bash 才会是最后写入的资料!如此一来其他bash 的指令操作就不会被记录下来了 (其实有被记录,只是被后来的最后一个bash 所覆盖更新了) 。
无法记录时间
历史命令还有一个问题,那就是无法记录指令下达的时间。
由于这1000 笔历史命令是依序记录的, 但是并没有记录时间,所以在查询方面会有一些不方便。
10.4 Bash Shell 的操作环境
10.4.1 路径与指令搜寻顺序
现在我们知道系统里面其实有不少的ls指令,或者是包括内建的echo指令,那么来想一想,如果一个指令(例如ls)被下达时,到底是哪一个ls被拿来运作?很有趣吧!基本上,指令运作的顺序可以这样看:
- 以相对/绝对路径执行指令,例如『 /bin/ls 』或『 ./ls 』;
- 由alias 找到该指令来执行;
- 由bash 内建的(builtin) 指令来执行;
- 透过$PATH 这个变数的顺序搜寻到的第一个指令来执行。
查询指令搜寻顺序:
type -a 指令名称
如:查询ls指令的搜寻顺序:
| llq@study ~]$ type -a ls ls is aliased to `ls —color=auto’ ls is /usr/bin/ls ls is /bin/ls |
|---|
10.4.2 bash 的进站与欢迎信息: /etc/issue, /etc/motd
还记得在终端机介面(tty1 ~ tty6) 登入的时候,会有几行提示的字串吗?那就是进站画面啊!那个字串写在哪里啊?呵呵!在/etc/issue 里面啊!先来看看:
| [dmtsai@study ~]$ cat /etc/issue\S
Kernel \r on an \m
|
| —- |
鸟哥是以完全未更新过的CentOS 7.1 作为范例,里面预设有三行,较有趣的地方在于\r 与\m。就如同$PS1 这变数一样,issue 这个档案的内容也是可以使用反斜线作为变数取用喔!你可以man issue 配合 man agetty 得到底下的结果:
| issue 内的各代码意义 |
|---|
| \d本地端时间的日期; \l显示第几个终端机介面; \m显示硬体的等级(i386/i486/i586/i686…); \n显示主机的网路名称; \O显示domain name; \r作业系统的版本(相当于uname -r) \t显示本地端时间的时间; \S作业系统的名称; \v作业系统的版本。 |
10.4.3 bash 的环境设定档
login 与non-login shell
在开始介绍bash 的设定档前,我们一定要先知道的就是login shell 与non-login shell!重点在于有没有登入(login) 啦!
login shell:取得bash 时需要完整的登入流程的,就称为login shell。
- 举例来说,你要由tty1 ~ tty6 登入,需要输入使用者的帐号与密码,此时取得的bash 就称为『 login shell 』啰;
non-login shell:取得bash 介面的方法不需要重复登入的举动,
- 举例来说,
- (1)你以X window 登入Linux 后, 再以X 的图形化介面启动终端机,此时那个终端介面并没有需要再次的输入帐号与密码,那个bash 的环境就称为non-login shell了。
- (2)你在原本的bash 环境下再次下达bash 这个指令,同样的也没有输入帐号密码, 那第二个bash (子程序) 也是non-login shell 。
- 举例来说,
由于我们需要登入系统,所以先谈谈login shell 会读取哪些设定档?一般来说,login shell 其实只会读取这两个设定档:
/etc/profile:这是系统整体的设定,你最好不要修改这个档案;~/.bash_profile或~/.bash_login或**~/.profile**:属于使用者个人设定,你要改自己的资料,就写入这里!
/etc/profile (login shell 才会读)
bash 在读完了整体环境设定的/etc/profile 并借此呼叫其他设定档后,接下来则是会读取使用者的个人设定档。在login shell 的bash 环境中,所读取的个人偏好设定档其实主要有三个,依序分别是:
~/.bash_profile~/.bash_login~/.profile
先让我们来看一下dmtsai的/home/dmtsai/.bash_profile的内容是怎样呢?
| [dmtsai@study ~]$ cat ~/.bash_profile
.bash_profile
Get the aliases and functions
if [ -f ~/.bashrc ]; then <==底下这三行在判断并读取~/.bashrc . ~/.bashrc fi
User specific environment and startup programs
PATH=$PATH:$HOME/.local/bin:$HOME/bin <==底下这几行在处理个人化设定 export PATH | | —- |
这个档案内有设定PATH 这个变数喔!而且还使用了export 将PATH 变成环境变数呢!由于PATH 在/etc/profile 当中已经设定过,所以在这里就以累加的方式增加使用者家目录下的~/bin/ 为额外的执行档放置目录。
这也就是说,你可以将自己建立的执行档放置到你自己家目录下的~/bin/ 目录啦!那就可以直接执行该执行档而不需要使用绝对/相对路径来执行该档案。
这个档案的内容比较有趣的地方在于if … then …那一段!那一段程式码我们会在第十二章shell script谈到,假设你现在是看不懂的。
该段的内容指的是『判断家目录下的~/.bashrc存在否,若存在则读入~/.bashrc的设定』。
bash设定档的读入方式比较有趣,主要是透过一个指令『 source 』来读取的!也就是说~/.bash_profile其实会再呼叫~/.bashrc的设定内容喔!最后,我们来看看整个login shell的读取流程:
图10.4.1、login shell 的设定档读取流程
实线的的方向是主线流程,虚线的方向则是被呼叫的设定档!
从上面我们也可以清楚的知道,在CentOS 的login shell 环境下,最终被读取的设定档是『 ~/.bashrc 』这个档案喔!所以,你当然可以将自己的偏好设定写入该档案即可。底下我们还要讨论一下source 与~/.bashrc 喔!
source :读入环境设定档的指令
由于/etc/profile 与~/.bash_profile 都是在取得login shell 的时候才会读取的设定档,所以, 如果你将自己的偏好设定写入上述的档案后,通常都是得登出再登入后,该设定才会生效。
那么,能不能直接读取设定档而不登出登入呢?可以的!那就得要利用**source **这个指令了!
| [dmtsai@study ~]$ source 设定档档名
范例:将家目录的~/.bashrc的设定读入目前的bash环境中 [dmtsai@study ~]$ source ~/.bashrc <==底下这两个指令是一样的! [dmtsai@study ~]$ . ~/.bashrc | | —- |
利用source 或小数点(.) 都可以将设定档的内容读进来目前的shell 环境中!
~/.bashrc (non-login shell 会读)
谈完了login shell 后,那么non-login shell 这种非登入情况取得bash 操作介面的环境设定档又是什么?
当你取得non-login shell 时,该bash 设定档仅会读取~/.bashrc 而已啦!
那么预设的~/.bashrc 内容是如何?
| [root@study ~]# 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 7.x还会主动的呼叫/etc/bashrc这个档案喔!为什么需要呼叫/etc/bashrc呢?
因为/etc/bashrc帮我们的bash定义出底下的资料:
- 依据不同的UID规范出umask的值;
- 依据不同的UID 规范出提示字元(就是PS1 变数);
- 呼叫/etc/profile.d/*.sh 的设定
这个/etc/bashrc 是CentOS 特有的(其实是Red Hat 系统特有的),其他不同的distributions 可能会放置在不同的档名就是了。
由于这个~/.bashrc 会呼叫/etc/bashrc 及/etc/profile.d/*.sh , 所以,万一你没有~/.bashrc (可能自己不小心将他删除了),那么你会发现你的bash 提示字元可能会变成这个样子:
| -bash-4.2$ |
|---|
其他相关设定档
事实上还有一些设定档可能会影响到你的bash 操作的,底下就来谈一谈:
**/etc/man_db.conf**
这的档案的内容『规范了使用 man的时候, man page的路径到哪里去寻找!』所以说的简单一点,这个档案规定了下达man的时候,该去哪里查看资料的路径设定!
~/.bash_history
预设的情况下,我们的历史命令就记录在这里啊!而这个档案能够记录几笔资料,则与HISTFILESIZE这个变数有关啊。每次登入bash后,bash会先读取这个档案,将所有的历史指令读入记忆体,因此,当我们登入bash后就可以查知上次使用过哪些指令。
~/.bash_logout
这个档案则记录了『当我登出bash后,系统再帮我做完什么动作后才离开』的意思。
预设的情况下,登出时, bash只是帮我们清掉萤幕的讯息而已。不过,你也可以将一些备份或者是其他你认为重要的工作写在这个档案中(例如清空暂存档),那么当你离开Linux的时候,就可以解决一些烦人的事情!
10.4.4 终端机的环境设定: stty, set
何查阅目前的一些按键内容呢?
- 可以利用stty (setting tty 终端机的意思) 呢!stty也可以帮助设定终端机的输入按键代表意义! | [dmtsai@study ~]$ stty [-a] 选项与参数: -a :将目前所有的stty 参数列出来;
范例一:列出所有的按键与按键内容
[dmtsai@study ~]$ stty -a
speed 38400 baud; rows 20; columns 90; line = 0;
intr = ^C ; quit = ^\; erase = ^? ; kill = ^U; eof = ^D ; eol =
在上头的列表当中,需要注意的是特殊字体那几个,此外,如果出现^表示[Ctrl]那个按键的意思。举例来说, intr = ^C表示利用[ctrl] + c来达成的。几个重要的代表意义是:
- intr : 送出一个interrupt (中断) 的讯号给目前正在run 的程序(就是终止啰!);
- quit : 送出一个quit 的讯号给目前正在run 的程序;
- erase : 向后删除字元,
- kill : 删除在目前指令列上的所有文字;
- eof : End of file 的意思,代表『结束输入』。
- start : 在某个程序停止后,重新启动他的output
- stop : 停止目前萤幕的输出;
- susp : 送出一个terminal stop 的讯号给正在run 的程序。
除了stty 之外,其实我们的bash 还有自己的一些终端机设定值呢!那就是利用set 来设定的!我们之前提到一些变数时,可以利用set 来显示,除此之外,其实set 还可以帮我们设定整个指令输出/输入的环境。例如记录历史命令、显示错误内容等等。
| [dmtsai@study ~]$ set [-uvCHhmBx] 选项与参数: -u :预设不启用。若启用后,当使用未设定变数时,会显示错误讯息; -v :预设不启用。若启用后,在讯息被输出前,会先显示讯息的原始内容; -x :预设不启用。若启用后,在指令被执行前,会显示指令内容(前面有++ 符号) -h :预设启用。与历史命令有关; -H :预设启用。与历史命令有关; -m :预设启用。与工作管理有关; -B :预设启用。与刮号[] 的作用有关; -C :预设不启用。若使用> 等,则若档案存在时,该档案不会被覆盖。
范例一:显示目前所有的set设定值 [dmtsai@study ~]$ echo $- himBH
那个$- 变数内容就是set 的所有设定啦!bash 预设是himBH 喔!
范例二:设定”若使用未定义变数时,则显示错误讯息” [dmtsai@study ~]$ set -u [dmtsai@study ~]$ echo $vbirding -bash: vbirding: unbound variable
预设情况下,未设定/未宣告的变数都会是『空的』,不过,若设定-u 参数,
那么当使用未设定的变数时,就会有问题啦!很多的shell 都预设启用-u 参数。
若要取消这个参数,输入set +u 即可!
范例三:执行前,显示该指令内容。 [dmtsai@study ~]$ set -x ++ printf ‘\033]0;%s@%s:%s\007’ dmtsai study ‘~’ #这个是在列出提示字元的控制码! [dmtsai@study ~]$ echo ${HOME}
- echo /home/dmtsai
/home/dmtsai
++ printf ‘\033]0;%s@%s:%s\007’ dmtsai study ‘~’
看见否?要输出的指令都会先被列印到萤幕上喔!前面会多出+ 的符号! |
| —- |
最后,我们将bash 预设的组合键给他汇整如下:
| 组合按键 | 执行结果 |
|---|---|
| Ctrl + C | 终止目前的命令 |
| Ctrl + D | 输入结束(EOF),例如邮件结束的时候; |
| Ctrl + M | 就是Enter 啦! |
| Ctrl + S | 暂停萤幕的输出 |
| Ctrl + Q | 恢复萤幕的输出 |
| Ctrl + U | 在提示字元下,将整列命令删除 |
| Ctrl + Z | 『暂停』目前的命令 |
10.4.5 万用字元与特殊符号
万用自元:
在bash 的操作环境中还有一个非常有用的功能,那就是万用字元(wildcard) !我们利用bash 处理资料就更方便了!底下我们列出一些常用的万用字元:
| 符号 | 意义 |
|---|---|
| * | 代表『 0 个到无穷多个』任意字元 |
| ? | 代表『一定有一个』任意字元 |
| [ ] | 同样代表『一定有一个在括号内』的字元(非任意字元)。例如[abcd] 代表『一定有一个字元, 可能是a, b, c, d 这四个任何一个』 |
| [ - ] | 若有减号在中括号内时,代表『在编码顺序内的所有字元』。例如[0-9] 代表 0 到9 之间的所有数字,因为数字的语系编码是连续的! |
| [^ ] | 若中括号内的第一个字元为指数符号(^) ,那表示『反向选择』,例如[^abc] 代表一定有一个字元,只要是非a, b, c 的其他字元就接受的意思。 |
使用万元自,进行练习:
| [dmtsai@study ~]$ LANG=C <==由于与编码有关,先设定语系一下
范例一:找出/etc/底下以cron为开头的档名 [dmtsai@study ~]$ ll -d /etc/cron* <==加上-d是为了仅显示目录而已
范例二:找出/etc/底下档名『刚好是五个字母』的档名 [dmtsai@study ~]$ ll -d /etc/????? <==由于?一定有一个,所以五个?就对了
范例三:找出/etc/底下档名含有数字的档名 [dmtsai@study ~]$ ll -d /etc/[0-9] <==记得中括号左右两边均需*
范例四:找出/etc/底下,档名开头非为小写字母的档名: [dmtsai@study ~]$ ll -d /etc/[^az] <==注意中括号左边没有
范例五:将范例四找到的档案复制到/tmp/upper中 [dmtsai@study ~]$ mkdir /tmp/upper; cp -a /etc/[^az]* /tmp/upper | | —- |
特殊符号:
bash 环境中的特殊符号有哪些呢?底下我们先汇整一下:
| 符号 | 内容 |
|---|---|
| # | 注解符号:这个最常被使用在script 当中,视为说明!在后的资料均不执行 |
| \ | 跳脱符号:将『特殊字元或万用字元』还原成一般字元 |
| | | 管线(pipe):分隔两个管线命令的界定(后两节介绍); |
| ; | 连续指令下达分隔符号:连续性命令的界定(注意!与管线命令并不相同) |
| ~ | 使用者的家目录 |
| $ | 取用变数前置字元:亦即是变数之前需要加的变数取代值 |
| & | 工作控制(job control):将指令变成背景下工作 |
| ! | 逻辑运算意义上的『非』 not 的意思! |
| / | 目录符号:路径分隔的符号 |
| >, >> | 资料流重导向:输出导向,分别是『取代』与『累加』 |
| <, << | 资料流重导向:输入导向(这两个留待下节介绍) |
| ‘ ‘ | 单引号,不具有变数置换的功能($ 变为纯文字) |
| “ “ | 具有变数置换的功能!($ 可保留相关功能) |
|
两个『 ` 』中间为可以先执行的指令,亦可使用$( ) |
| ( ) | 在中间为子shell 的起始与结束 |
| { } | 在中间为命令区块的组合! |
以上为bash 环境中常见的特殊符号汇整!理论上,你的『档名』尽量不要使用到上述的字元啦!
10.5 资料流重导向
资料流重导向:就是将某个指令执行后应该要出现在萤幕上的资料, 给他传输到其他的地方,例如档案或者是装置(例如印表机之类的)。
10.5.1 什么是资料流重导向
一般来说,如果你要执行一个指令,通常他会是这样的:

图10.5.1、指令执行过程的资料传输情况
我们执行一个指令的时候,这个指令可能会由档案读入资料,经过处理之后,再将资料输出到萤幕上。在上图当中, standard output 与standard error output 分别代表『标准输出(STDOUT)』与『标准错误输出(STDERR)』, 这两个玩意儿预设都是输出到萤幕上面!
standard output 与standard error output
标准输出指的是『指令执行所回传的正确的讯息』,
标准错误输出可理解为『指令执行失败后,所回传的错误讯息』。
举个简单例子来说,我们的系统预设有/etc/crontab但却无/etc/vbirdsay,此时若下达『 cat /etc/crontab /etc/vbirdsay 』这个指令时,cat会进行:
- 标准输出:读取/etc/crontab 后,将该档案内容显示到萤幕上;
- 标准错误输出:因为无法找到/etc/vbirdsay,因此在萤幕上显示错误讯息
资料流重导向可以将standard output (简称stdout) 与standard error output (简称stderr) 分别传送到其他的档案或装置去,而分别传送所用的特殊字元则如下所示:
- 标准输入(stdin) :代码为0 ,使用< 或<< ;
- 标准输出(stdout):代码为1 ,使用> 或>> ;
- 标准错误输出(stderr):代码为2 ,使用2> 或2>>** **;
standard output 的正确资料:
我们先来进行一个范例的练习:
| 范例一:观察你的系统根目录(/)下各目录的档名、权限与属性,并记录下来 [dmtsai@study ~]$ ll / <==此时萤幕会显示出档名资讯
[dmtsai@study ~]$ ll / > ~/rootfile <==萤幕并无任何资讯 [dmtsai@study ~]$ ll ~/rootfile <==有个新档被建立了! -rw-rw-r—. 1 dmtsai dmtsai 1078 Jul 9 18:51 /home/dmtsai/rootfile | | —- |
因为原本『 ll / 』所显示的资料已经被重新导向到~/rootfile 档案中了!那个~/rootfile 的档名可以随便你取。如果你下达『 cat ~/rootfile 』那就可以看到原本应该在萤幕上面的资料。
该档案的建立方式是:
- 该档案(本例中是~/rootfile) 若不存在,系统会自动的将他建立起来,但是
- 当这个档案存在的时候,那么系统就会先将这个档案内容清空,然后再将资料写入!
- 也就是若以> 输出到一个已存在的档案中,那个档案就会被覆盖掉!
如果我想要将资料累加而不想要将旧的资料删除,那该如何是好?利用两个大于的符号(>>)就好啦!
standard error output 的错误资料:
?那就透过2> 及 2>> 啰!同样是覆盖(2>) 与累加(2>>) 的特性!我们在刚刚才谈到stdout 代码是1 而stderr 代码是2 , 所以这个2> 是很容易理解的,而如果仅存在> 时,则代表预设的代码1 啰!也就是说:
- 1> :以覆盖的方法将『正确的资料』输出到指定的档案或装置上;
- 1>>:以累加的方法将『正确的资料』输出到指定的档案或装置上;
- 2> :以覆盖的方法将『错误的资料』输出到指定的档案或装置上;
- 2>>:以累加的方法将『错误的资料』输出到指定的档案或装置上;
『 1>> 』以及『 2>> 』中间是没有空格的。
如何一起使用:
| 范例三:承范例二,将stdout与stderr分存到不同的档案去 [dmtsai@study ~]$ find /home -name .bashrc > list_right 2> list_error | | —- |
此时『萤幕上不会出现任何讯息』!因为刚刚执行的结果中,有Permission 的那几行错误资讯都会跑到list_error 这个档案中,至于正确的输出资料则会存到list_right 这个档案中。
/dev/null 垃圾桶黑洞装置与特殊写法
如何将错误信息忽略掉而不显示或存储?
- 答:使用黑洞装置
/dev/null。这个/dev/null 可以吃掉任何导向这个装置的资讯喔! | 范例四:承范例三,将错误的资料丢弃,萤幕上显示正确的资料 [dmtsai@study ~]$ find /home -name .bashrc 2> /dev/null /home/dmtsai/.bashrc <==只有stdout会显示到萤幕上, stderr被丢弃了 | | —- |
如何将正确与错误资料通通写入同一个档案去呢?
- 答:使用特殊写法:
| 范例五:将指令的资料全部写入名为list的档案中 [dmtsai@study ~]$ find /home -name .bashrc > list 2> list <==错误 [dmtsai@study ~]$ find /home - name .bashrc > list 2>&1 <==正确** [dmtsai@study ~]$ find /home -name .bashrc &> list <==正确** | | —- |
standard input : < 与<<
了解了stderr与stdout后,那么那个 **<** 又是什么呀?
- 答:是『将原本需要由键盘输入的资料,改由档案内容来取代』的意思。
例如:
- 我可以使用【键盘输入】数据: | 范例六:利用cat指令来建立一个档案的简单流程 [dmtsai@study ~]$ cat > catfile testing cat file test <==这里按下[ctrl]+d来离开
[dmtsai@study ~]$ cat catfile testing cat file test | | —- |
- 我可以使用某个文件中的内容来取代 由键盘中的输入:
| 范例七:用stdin取代键盘的输入以建立新档案的简单流程
[dmtsai@study ~]$ cat > catfile < ~/.bashrc
[dmtsai@study ~]$ ll catfile ~/.bashrc
-rw-r— r—. 1 dmtsai dmtsai 231 Mar 6 06:06 /home/dmtsai/.bashrc
-rw-rw-r—. 1 dmtsai dmtsai 231 Jul 9 18:58 catfile
注意看,这两个档案的大小会一模一样!几乎像是使用cp来复制一般! |
| —- |
再来看看:<<
他代表的是『结束的输入字元』的意思!
例如:我要用cat 直接将输入的讯息输出到catfile 中, 且当由键盘输入eof 时,该次输入就结束,那我可以这样做:
| [dmtsai@study ~]$ cat > catfile << “eof”
This is a test. OK now stop eof <==输入这关键字,立刻就结束而不需要输入[ctrl]+d
[dmtsai@study ~]$ cat catfile This is a test. OK now stop <==只有这两行,不会存在关键字那一行! | | —- |
那么为何要使用命令输出重导向呢?
- 萤幕输出的资讯很重要,而且我们需要将他存下来的时候;
- 背景执行中的程式,不希望他干扰萤幕正常的输出结果时;
- 一些系统的例行命令(例如写在/etc/crontab 中的档案) 的执行结果,希望他可以存下来时;
- 一些执行命令的可能已知错误讯息时,想以『 2> /dev/null 』将他丢掉时;
- 错误讯息与正确讯息需要分别输出时。
10.5.2 命令执行的判断依据: ; , &&, ||
很多指令我想要一次输入去执行,而不想要分次执行时,该如何是好?基本上你有两个选择,
- 一个是透过第十二章要介绍的shell script 撰写脚本去执行,
- 一种则是透过底下的介绍来一次输入多重指令喔!
; 分号(cmd1 ; cmd2 不考虑指令之间的相关性)
一次可以执行多个指令。
例如:
| [root@study ~]# sync; sync; shutdown -h now |
|---|
在指令与指令中间利用分号(;) 来隔开,这样一来,分号前的指令执行完后就会立刻接着执行后面的指令了。
$? (指令回传值) 与&& 或||
两个指令之间有相依性,而这个相依性主要判断的地方就在于前一个指令执行的结果是否正确。
『若前一个指令执行的结果为正确,在Linux底下会回传一个$? = 0的值』
我们怎么透过这个回传值来判断后续的指令是否要执行呢?这就得要藉由『 && 』及『 || 』的帮忙了!
| 指令下达情况 | 说明 |
|---|---|
| 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 [dmtsai@study ~]$ ls /tmp/abc && touch /tmp/abc/hehe ls: cannot access /tmp/abc: No such file or directory
ls 很干脆的说明找不到该目录,但并没有touch 的错误,表示touch 并没有执行
[dmtsai@study ~]$ mkdir /tmp/abc [dmtsai@study ~]$ ls /tmp/abc && touch /tmp/abc/hehe [dmtsai@study ~]$ ll /tmp/abc -rw-rw-r—. 1 dmtsai dmtsai 0 Jul 9 19:16 hehe | | —- |
10.6 管线命令(pipe)
管线命令使用的是『 | 』这个界定符号!另外,管线命令与『连续下达命令』是不一样的!
其实这个管线命令『 | 』仅能处理经由前面一个指令传来的正确资讯,也就是standard output的资讯,对于stdandard error并没有直接处理的能力。那么整体的管线命令可以使用下图表示:
图10.6.1、管线命令的处理示意图
在每个管线后面接的第一个资料必定是『指令』,而且这个指令必须要能够接受standard input的资料才行,这样的指令才可以是为『管线命令』。
也就是说,管线命令主要有两个比较需要注意的地方:
- 管线命令仅会处理standard output,对于standard error output 会予以忽略
- 管线命令必须要能够接受来自前一个指令的资料成为standard input 继续处理才行。
10.6.1 截取命令: cut, grep
就是将一段资料经过分析后,取出我们所想要的。或者是经由分析关键字,取得我们所想要的那一行!
一般来说,撷取讯息通常是针对『一行一行』来分析的,并不是整篇讯息分析的喔~底下我们介绍两个很常用的讯息撷取命令:
cut
这个指令可以将一段讯息的某一段给他『切』出来~ 处理的讯息是以『行』为单位。
| [dmtsai@study ~]$ cut -d’分隔字元’ -f fields <==用于有特定分隔字元 [dmtsai@study ~]$ cut -c字元区间 <==用于排列整齐的讯息 选项与参数: -d :后面接分隔字元。与-f 一起使用; -f :依据-d 的分隔字元将一段讯息分割成为数段,用-f 取出第几段的意思; -c :以字元(characters) 的单位取出固定字元区间;
范例一:将PATH变数取出,我要找出第五个路径。 [dmtsai@study ~]$ echo ${PATH} /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
1 | 2 | 3 | 4 | 5 | 6 |
[dmtsai@study ~]$ echo ${PATH} | cut -d ‘:’ -f 5
如同上面的数字显示,我们是以『 : 』作为分隔,因此会出现/home/dmtsai/.local/bin
那么如果想要列出第3与第5呢?,就是这样:
[dmtsai@study ~]$ echo ${PATH} | cut -d ‘:’ -f 3,5
范例二:将export输出的讯息,取得第12字元以后的所有字串 [dmtsai@study ~]$ export declare -x HISTCONTROL=”ignoredups” declare -x HISTSIZE=”1000” declare -x HOME=”/home/dmtsai” declare -x HOSTNAME=”study.centos.vbird” …..(其他省略)…..
注意看,每个资料都是排列整齐的输出!如果我们不想要『 declare -x 』时,就得这么做:
[dmtsai@study ~]$ export | cut -c 12- HISTCONTROL=”ignoredups” HISTSIZE=”1000” HOME=”/home/dmtsai” HOSTNAME=”study.centos.vbird” …..(其他省略)…..
知道怎么回事了吧?用-c 可以处理比较具有格式的输出资料!
我们还可以指定某个范围的值,例如第12-20 的字元,就是cut -c 12-20 等等!
范例三:用last将显示的登入者的资讯中,仅留下使用者大名 [dmtsai@study ~]$ last root pts/1 192.168.201.101 Sat Feb 7 12:35 still logged in root pts/1 192.168.201.101 Fri Feb 6 12:13 - 18:46 (06:33) root pts/1 192.168.201.254 Thu Feb 5 22:37 - 23:53 (01:16)
last 可以输出『帐号/终端机/来源/日期时间』的资料,并且是排列整齐的
[dmtsai@study ~]$ last | cut -d ‘ ‘ -f 1
由输出的结果我们可以发现第一个空白分隔的栏位代表帐号,所以使用如上指令:
但是因为root pts/1 之间空格有好几个,并非仅有一个,所以,如果要找出
pts/1 其实不能以cut -d ‘ ‘ -f 1,2 喔!输出的结果会不是我们想要的。 |
| —- |
cut 主要的用途在于将『同一行里面的资料进行分解!』最常使用在分析一些数据或文字资料的时候。
grep
grep 则是分析一行讯息, 若当中有我们所需要的资讯,就将该行拿出来。
| [dmtsai@study ~]$ grep [-acinv] [—color=auto] ‘搜寻字串’ filename 选项与参数: -a :将binary 档案以text 档案的方式搜寻资料 -c :计算找到’搜寻字串’ 的次数 -i :忽略大小写的不同,所以大小写视为相同 -n :顺便输出行号 -v :反向选择,亦即显示出没有’搜寻字串’ 内容的那一行! —color=auto :可以将找到的关键字部分加上颜色的显示喔!
范例一:将last当中,有出现root的那一行就取出来; [dmtsai@study ~]$ last | grep ‘root’
范例二:与范例一相反,只要没有root的就取出! [dmtsai@study ~]$ last | grep -v ‘root’
范例三:在last的输出讯息中,只要有root就取出,并且仅取第一栏 [dmtsai@study ~]$ last | grep ‘root’ |cut -d ‘ ‘ -f1
在取出root之后,利用上个指令cut的处理,就能够仅取得第一栏啰!
范例四:取出/etc/man_db.conf内含MANPATH的那几行 [dmtsai@study ~]$ grep —color=auto ‘MANPATH’ /etc/man_db.conf ….(前面省略)… . MANPATH _MAP /usr/games /usr/share/man MANPATH _MAP /opt/bin /opt/man MANPATH _MAP /opt/sbin /opt/man
神奇的是,如果加上—color=auto的选项,找到的关键字部分会用特殊颜色显示喔! |
| —- |
10.6.2 排序命令: sort, wc, uniq
sort
他可以帮我们进行排序,而且可以依据不同的资料型态来排序。
例如数字与文字的排序就不一样。 此外,排序的字元与语系的编码有关,因此, 如果您需要排序时,建议使用LANG=C 来让语系统一,资料排序比较好一些。
| [dmtsai@study ~]$ sort [-fbMnrtuk] [file or stdin] 选项与参数: -f :忽略大小写的差异,例如A 与a 视为编码相同; -b :忽略最前面的空白字元部分; -M :以月份的名字来排序,例如JAN, DEC 等等的排序方法; -n :使用『纯数字』进行排序(预设是以文字型态来排序的); -r :反向排序; -u :就是uniq ,相同的资料中,仅出现一行代表; -t :分隔符号,预设是用[tab] 键来分隔; -k :以那个区间(field) 来进行排序的意思
范例一:个人帐号都记录在/etc/passwd下,请将帐号进行排序。 [dmtsai@study ~]$ cat /etc/passwd | sort ab rt:x:173:173::/etc/abrt:/sbin/nologin ad m:x:3:4:adm:/var/adm:/ sbin/nologin al ex:x:1001:1002::/home/alex:/bin/bash
鸟哥省略很多的输出~由上面的资料看起来, sort是预设『以第一个』资料来排序,
而且预设是以『文字』型态来排序的喔!所以由a 开始排到最后啰!
范例二:/etc/passwd内容是以:来分隔的,我想以第三栏含后面资料来排序,该如何? [dmtsai@study ~]$ cat /etc/passwd | sort -t ‘:’ -k 3 root:x: 0:0:root:/root:/bin/bash dmtsai:x: 1000:1000:dmtsai:/ home/dmtsai:/bin/bash alex:x: 1001:1002::/home/alex:/bin/bash arod:x: 1002:1003::/home/arod:/bin/bash
看到特殊字体的输出部分了吧?怎么会这样排列啊?呵呵!没错啦~若单纯以第三栏位来处理则是:
[dmtsai@study ~]$ cat /etc/passwd | sort -t ‘:’ -k 3,3
如果是以文字型态来排序的话,原本就会是这样,想要使用数字排序:
cat /etc/passwd | sort -t ‘:’ -k 3,3 -n
这样才行啊!用那个-n 来告知sort 以数字来排序啊!
范例三:利用last ,将输出的资料仅取帐号,并加以排序 [dmtsai@study ~]$ last | cut -d ‘ ‘ -f1 | sort | | —- |
uniq
如果我排序完成了,想要将重复的资料仅列出一个显示,可以怎么做呢?
| [dmtsai@study ~]$ uniq [-ic] 选项与参数: -i :忽略大小写字元的不同; -c :进行计数
范例一:使用last将帐号列出,仅取出帐号栏,进行排序后仅取出一位; [dmtsai@study ~]$ last | cut -d ‘ ‘ -f1 | sort | uniq
范例二:承上题,如果我还想要知道每个人的登入总次数呢? [dmtsai@study ~]$ last | cut -d ‘ ‘ -f1 | sort | uniq -c 1 6 (unknown 47 dmtsai 4 reboot 7 root 1 wtmp
从上面的结果可以发现reboot 有4 次, root 登入则有7 次!大部分是以dmtsai 来操作!
wtmp 与第一行的空白都是last 的预设字元,那两个可以忽略的! |
| —- |
wc
他可以帮我们计算输出的讯息的整体资料!
| [dmtsai@study ~]$ wc [-lwm] 选项与参数: -l :仅列出行; -w :仅列出多少字(英文单字); -m :多少字元;
范例一:那个/etc/man_db.conf里面到底有多少相关字、行、字元数? [dmtsai@study ~]$ cat /etc/man_db.conf | wc 131 723 5171
输出的三个数字中,分别代表: 『行、字数、字元数』
范例二:我知道使用last 可以输出登入者,但是last 最后两行并非帐号内容,那么请问, 我该如何以一行指令串取得登入系统的总人次? [dmtsai@study ~]$ last | grep [a-zA-Z] | grep -v ‘wtmp’ | grep -v ‘reboot’ | \
grep -v ‘unknown’ |wc -l
由于last会输出空白行, wtmp, unknown, reboot等无关帐号登入的资讯,因此,我利用
grep 取出非空白行,以及去除上述关键字那几行,再计算行数,就能够了解啰! |
| —- |
10.6.3 双向重导向: tee
> 会将资料流整个传送给档案或装置,因此我们除非去读取该档案或装置, 否则就无法继续利用这个资料流。
万一我想要将这个资料流的处理过程中将某段讯息存下来,应该怎么做?利用tee 就可以啰~我们可以这样简单的看一下:
图10.6.2、tee 的工作流程示意图
tee 会同时将资料流分送到档案去与萤幕(screen);而输出到萤幕的,其实就是stdout ,那就可以让下个指令继续处理!
| [dmtsai@study ~]$ tee [-a] file 选项与参数: -a :以累加(append) 的方式,将资料加入file 当中!
[dmtsai@study ~]$ last | tee last.list | cut -d “ “ -f1
这个范例可以让我们将last的输出存一份到last.list档案中;
[dmtsai@study ~]$ ls -l /home | tee ~/homefile | more
这个范例则是将ls的资料存一份到~/homefile ,同时萤幕也有输出讯息!
[dmtsai@study ~]$ ls -l / | tee -a ~/homefile | more
要注意!tee后接的档案会被覆盖,若加上-a这个选项则能将讯息累加。 |
| —- |
tee 可以让standard output 转存一份到档案内并将同样的资料继续送到萤幕去处理!
- 这样除了可以让我们同时分析一份资料并记录下来之外,
- 还可以作为处理一份资料的中间暂存档记录之用。
10.6.4 字元转换命令: tr, col, join, paste, expand
tr
tr 可以用来删除一段讯息当中的文字,或者是进行文字讯息的替换!
| [dmtsai@study ~]$ tr [-ds] SET1 … 选项与参数: -d :删除讯息当中的SET1 这个字串; -s :取代掉重复的字元!
范例一:将last输出的讯息中,所有的小写变成大写字元: [dmtsai@study ~]$ last | tr ‘[az]’ ‘[AZ]’
事实上,没有加上单引号也是可以执行的,如:『 last | tr [az] [AZ] 』
范例二:将/etc/passwd输出的讯息中,将冒号(:)删除 [dmtsai@study ~]$ cat /etc/passwd | tr -d ‘:’
范例三:将/etc/passwd转存成dos断行到/root/passwd中,再将^M符号删除 [dmtsai@study ~]$ cp /etc/passwd ~/passwd && unix2dos ~/passwd [dmtsai@ study ~]$ file /etc/passwd ~/passwd /etc/passwd: ASCII text /home/dmtsai/passwd: ASCII text, with CRLF line terminators <==就是DOS断行 [dmtsai@study ~]$ cat ~/passwd | tr -d ‘\r’ > ~/passwd.linux
那个\r指的是DOS的断行字元,关于更多的字符,请参考man tr
[dmtsai@study ~]$ ll /etc/passwd ~/passwd* -rw-r—r—. 1 root root 2092 Jun 17 00:20 /etc/passwd -rw-r—r—. 1 dmtsai dmtsai 2133 Jul 9 22:13 /home/dmtsai/passwd -rw-rw-r—. 1 dmtsai dmtsai 2092 Jul 9 22:13 /home/dmtsai/passwd.linux
处理过后,发现档案大小与原本的/etc/passwd就一致了! |
| —- |
col
很多时候,他可以用来简单的处理将[tab] 按键取代成为空白键!
| [dmtsai@study ~]$ col [-xb] 选项与参数: -x :将tab 键转换成对等的空白键
范例一:利用cat -A显示出所有特殊按键,最后以col将[tab]转成空白 [dmtsai@study ~]$ cat -A /etc/man_db.conf <==此时会看到很多^I的符号,那就是tab [dmtsai@study ~]$ cat /etc/man_db.conf | col -x | cat -A | more
嘿嘿!如此一来, [tab]按键会被取代成为空白键,输出就美观多了! |
| —- |
join
他是在处理两个档案之间的资料,而且,主要是在处理『两个档案当中,有“相同资料” 的那一行,才将他加在一起』的意思。
| [dmtsai@study ~]$ join [-ti12] file1 file2 选项与参数: -t :join 预设以空白字元分隔资料,并且比对『第一个栏位』的资料, 如果两个档案相同,则将两笔资料联成一行,且第一个栏位放在第一个! -i :忽略大小写的差异; -1 :这个是数字的1 ,代表『第一个档案要用那个栏位来分析』的意思; -2 :代表『第二个档案要用那个栏位来分析』的意思。
范例一:用root的身份,将/etc/passwd与/etc/shadow相关资料整合成一栏 [root@study ~]# head -n 3 /etc/passwd /etc/shadow ==> /etc/passwd <== root : x : 0:0 : root:/root :/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin
==> /etc/shadow <== root:$6$wtbCCce/PxMeE5wm$KE2IfSJr…:16559:0:99999:7::: bin::16372:0:99999:7::: daemon::16372:0:99999:7:::
由输出的资料可以发现这两个档案的最左边栏位都是相同帐号!且以: 分隔
[root@study ~]# join -t ‘:’ /etc/passwd /etc/shadow | head -n 3 root :x:0:0:root:/root:/bin/bash: $6$wtbCCce/PxMeE5wm$ KE2IfSJr…:16559:0:99999:7::: bin :x:1:1:bin:/bin:/sbin/nologin: :16372:0:99999:7::: daemon :x:2 :2:daemon:/sbin:/sbin/nologin: :16372:0:99999:7:::
透过上面这个动作,我们可以将两个档案第一栏位相同者整合成一列!
第二个档案的相同栏位并不会显示(因为已经在最左边的栏位出现了啊!)
范例二:我们知道/etc/passwd 第四个栏位是GID ,那个GID 记录在 /etc/group当中的第三个栏位,请问如何将两个档案整合? [root@study ~]# head -n 3 /etc/passwd /etc/group ==> /etc/passwd <== root:x:0: 0 :root:/root:/bin/bash bin:x:1: 1 :bin:/bin:/sbin/nologin daemon:x:2: 2 :daemon:/sbin:/sbin/nologin
==> /etc/group <== root:x: 0 : bin:x: 1 : daemon:x: 2 :
从上面可以看到,确实有相同的部分喔!赶紧来整合一下!
[root@study ~]# join -t ‘:’ -1 4 /etc/passwd -2 3 /etc/group | head -n 3 0 :root:x:0:root:/root:/bin/bash: root:x: 1 :bin:x:1:bin:/bin:/sbin/nologin: bin:x: 2 :daemon:x:2:daemon:/sbin:/sbin/nologin: daemon:x:
同样的,相同的栏位部分被移动到最前面了!所以第二个档案的内容就没再显示。
请读者们配合上述显示两个档案的实际内容来比对! |
| —- |
在使用join之前,你所需要处理的档案应该要事先经过排序(sort)处理!否则有些比对的项目会被略过呢!特别注意了!
paste
paste就直接『将两行贴在一起,且中间以[tab]键隔开』而已!简单的使用方法:
| [dmtsai@study ~]$ paste [-d] file1 file2 选项与参数: -d :后面可以接分隔字元。预设是以[tab] 来分隔的!
- :如果file 部分写成- ,表示来自standard input 的资料的意思。
范例一:用root身份,将/etc/passwd与/etc/shadow同一行贴在一起 [root@study ~]# paste /etc/passwd /etc/shadow root : x : 0:0 : root:/root :/bin/bash root:$6$wtbCCce/PxMeE5wm$KE2IfSJr…:16559:0:99999:7::: bin:x:1:1:bin:/bin:/sbin/nologin bin::16372:0:99999:7::: daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon::16372:0:99999:7:::
注意喔!同一行中间是以[tab] 按键隔开的!
范例二:先将/etc/group读出(用cat),然后与范例一贴上一起!且仅取出前三行 [root@study ~]# cat /etc/group|paste /etc/passwd /etc/shadow -|head -n 3
这个例子的重点在那个 “-**“ **的使用!那玩意儿常常代表stdin喔! |
| —- |
expand
将[tab] 按键转成空白键啦。
| [dmtsai@study ~]$ expand [-t] file 选项与参数: -t :后面可以接数字。一般来说,一个tab 按键可以用8 个空白键取代。 我们也可以自行定义一个[tab] 按键代表多少个字元呢!
范例一:将/etc/man_db.conf内行首为MANPATH的字样就取出;仅取前三行; [dmtsai@study ~]$ grep ‘^MANPATH’ /etc/man_db.conf | head -n 3 MANPATH_MAP /bin /usr/share/man MANPATH_MAP /usr/bin /usr/share/man MANPATH_MAP /sbin /usr/share/man
行首的代表标志为^ ,这个我们留待下节介绍!先有概念即可!
范例二:承上,如果我想要将所有的符号都列出来?(用cat) [dmtsai@study ~]$ grep ‘^MANPATH’ /etc/man_db.conf | head -n 3 |cat -A MANPATH_MAP^I/bin^I^I^I/usr/share/man$ MANPATH_MAP^I/usr/bin^I^I/usr/share/man$ MANPATH_MAP^I/sbin^I^I^I/usr/share/man$
发现差别了吗?没错~ [tab] 按键可以被cat -A 显示成为^I
范例三:承上,我将[tab]按键设定成6个字元的话? [dmtsai@study ~]$ grep ‘^MANPATH’ /etc/man_db.conf | head -n 3 | expand -t 6 - | cat -A MANPATH_MAP /bin /usr/share/man$ MANPATH_MAP /usr/bin /usr/share/man$ MANPATH_MAP /sbin /usr/share/man$ 123456123456123456123456123456123456123456123456…
仔细看一下上面的数字说明,因为我是以6 个字元来代表一个[tab] 的长度,所以,
MAN… 到/usr 之间会隔12 (两个[tab]) 个字元喔!如果tab 改成9 的话,
情况就又不同了!这里也不好理解~您可以多设定几个数字来查阅就晓得! |
| —- |
10.6.5 分割命令: split
如果你有档案太大,导致一些携带式装置无法复制的问题,找split 就对了!
他可以帮你将一个大档案,依据档案大小或行数来分割,就可以将大档案分割成为小档案了!快速又有效啊!
| [dmtsai@study ~]$ split [-bl] file PREFIX 选项与参数: -b :后面可接欲分割成的档案大小,可加单位,例如b, k, m 等; -l :以行数来进行分割。 PREFIX :代表前置字元的意思,可作为分割档案的前导文字。
范例一:我的/etc/services有六百多K,若想要分成300K一个档案时? [dmtsai@study ~]$ cd /tmp; split -b 300k /etc/services services [dmtsai@study tmp]$ ll -k services* -rw-rw-r—. 1 dmtsai dmtsai 307200 Jul 9 22:52 services aa -rw-rw-r—. 1 dmtsai dmtsai 307200 Jul 9 22:52 services ab -rw-rw-r—. 1 dmtsai dmtsai 55893 Jul 9 22:52 services ac
那个档名可以随意取的啦!我们只要写上前导文字,小档案就会以
xxxaa, xxxab, xxxac 等方式来建立小档案的!
范例二:如何将上面的三个小档案合成一个档案,档名为servicesback [dmtsai@study tmp]$ cat services* >> servicesback
很简单吧?就用资料流重导向就好啦!简单!
范例三:使用ls -al /输出的资讯中,每十行记录成一个档案 [dmtsai@study tmp]$ ls -al / | split -l 10 - lsroot [dmtsai@study tmp]$ wc -l lsroot* 10 lsrootaa 10 lsrootab 4 lsrootac 24 total
重点在那个- 啦!一般来说,如果需要stdout/stdin 时,但偏偏又没有档案,
有的只是- 时,那么那个- 就会被当成stdin 或stdout ~ |
| —- |
10.6.6 参数代换: xargs
这个玩意儿就是在产生某个指令的参数的意思!
xargs可以读入stdin的资料,并且以空白字元或断行字元作为分辨,将stdin的资料分隔成为arguments 。
因为是以空白字元作为分隔,所以,如果有一些档名或者是其他意义的名词内含有空白字元的时候, xargs可能就会误判。
| [dmtsai@study ~]$ xargs [-0epn] command 选项与参数: -0 :如果输入的stdin 含有特殊字元,例如`, \, 空白键等等字元时,这个-0 参数 可以将他还原成一般字元。这个参数可以用于特殊状态喔! -e :这个是EOF (end of file) 的意思。后面可以接一个字串,当xargs 分析到这个字串时, 就会停止继续工作! -p :在执行每个指令的argument 时,都会询问使用者的意思; -n :后面接次数,每次command 指令执行时,要使用几个参数的意思。 当xargs 后面没有接任何的指令时,预设是以echo 来进行输出喔!
范例一:将/etc/passwd内的第一栏取出,仅取三行,使用id这个指令将每个帐号内容秀出来 [dmtsai@study ~]$ id root uid=0(root) gid=0( root) groups=0(root) #这个id指令可以查询使用者的UID/GID等资讯
[dmtsai@study ~]$ id $(cut -d ‘:’ -f 1 /etc/passwd | head -n 3)
虽然使用$(cmd)可以预先取得参数,但可惜的是, id这个指令『仅』能接受一个参数而已!
所以上述的这个指令执行会出现错误!根本不会显示用户的ID 啊!
[dmtsai@study ~]$ cut -d ‘:’ -f 1 /etc/passwd | head -n 3 | id uid=1000(dmtsai) gid=1000(dmtsai) groups=1000(dmtsai),10(wheel) #我不是要查自己啊!
因为id并不是管线命令,因此在上面这个指令执行后,前面的东西通通不见!只会执行id!
[dmtsai@study ~]$ cut -d ‘:’ -f 1 /etc/passwd | head -n 3 | xargs id
依旧会出现错误!这是因为xargs一口气将全部的资料通通丢给id处理~但id就接受1个啊最多!
[dmtsai@study ~]$ cut -d ‘:’ -f 1 /etc/passwd | head -n 3 | xargs -n 1 id uid=0(root) gid=0(root) groups=0(root) uid=1(bin) gid=1(bin) groups=1(bin) uid=2(daemon) gid=2(daemon) groups=2(daemon)
透过-n 来处理,一次给予一个参数,因此上述的结果就OK 正常的显示啰!
范例二:同上,但是每次执行id时,都要询问使用者是否动作? [dmtsai@study ~]$ cut -d ‘:’ -f 1 /etc/passwd | head -n 3 | xargs -p -n 1 id id root ?… y uid=0(root) gid=0(root) groups=0(root) id bin ?… y …..(底下省略)…..
呵呵!这个-p 的选项可以让使用者的使用过程中,被询问到每个指令是否执行!
范例三:将所有的/etc/passwd内的帐号都以id查阅,但查到sync就结束指令串 [dmtsai@study ~]$ cut -d ‘:’ -f 1 /etc/passwd | xargs -e ‘sync’ -n 1 id
仔细与上面的案例做比较。也同时注意,那个-e’sync’是连在一起的,中间没有空白键。
上个例子当中,第六个参数是sync 啊,那么我们下达-e’sync’ 后,则分析到sync 这个字串时,
后面的其他stdin 的内容就会被xargs 舍弃掉了! |
| —- |
使用xargs的原因是, 很多指令其实并不支援管线命令,因此我们可以透过xargs来提供该指令引用standard input之用!举例来说,我们使用如下的范例来说明:
| 范例四:找出/usr/sbin底下具有特殊权限的档名,并使用ls -l列出详细属性 [dmtsai@study ~]$ find /usr/sbin -perm /7000 | xargs ls -l -rwx—s—x. 1 root lock 11208 Jun 10 2014 /usr/sbin/lockdev -rwsr-xr-x. 1 root root 113400 Mar 6 12:17 /usr/sbin/mount.nfs -rwxr-sr-x. 1 root root 11208 Mar 6 11:05 /usr/sbin/netreport …..(底下省略)…..
聪明的读者应该会想到使用『 ls -l $(find /usr/sbin -perm /7000) 』来处理这个范例!
都OK!能解决问题的方法,就是好方法! |
| —- |
10.6.7 关于减号- 的用途
在管线命令当中,常常会使用到前一个指令的stdout 作为这次的stdin, 某些指令需要用到档案名称(例如tar) 来进行处理时,该stdin 与stdout 可以利用减号”-“ 来替代, 举例来说:
| [root@study ~]# mkdir /tmp/homeback [root@study ~]# tar -cvf - /home | tar -xvf - -C /tmp/homeback # 上面这个例子是说:『我将/home 里面的档案给他打包,但打包的资料不是纪录到档案,# 而是传送到stdout; 经过管线后,将tar -cvf - /home 传送给后面的tar - xvf - 』。 | | —- |
后面的这个-则是取用前一个指令的stdout, 因此,我们就不需要使用filename 了!这是很常见的例子喔!注意注意!
