6.1 认识BASH和Shell
- 为何要学文字接口的 shell?
文字接口的传输速度一定比较快, 而且,较 不容易出现断线或者是信息外流的问题,
- 那么目前我们的 Linux (以 CentOS 7.x 为例) 有多少我们可以使用的 shells 呢?
/bin/sh (已经被 /bin/bash 所取代)
/bin/bash (就是 Linux 预设的 shell)
/bin/tcsh (整合 C Shell ,提供更多的功能)
/bin/csh (已经被 /bin/tcsh 所取代)
- 虽然各家 shell 的功能都差不多,但是在某些语法的下达方面则有所不同,因此建议你还是得要选择 某一种 shell 来熟悉一下较佳。 Linux 预设就是使用 bash ,所以最初你只要学会 bash 就可以了
6.2 Bash shell的功能
既然 /bin/bash 是 Linux 预设的 shell , bash 是 GNU 计划中 重要的工具软件之一,目前也是 Linux distributions 的标准 shell 。 bash主要兼容于 sh ,并且依据一些使用者需求而加强的 shell版本。
6.2.1 bash 主要的优点有底下几个:
- 命令编修能力 (history):
- 这么多的指令记录在哪里呢?在你的家目录内的 .bash_history.
~/.bash_history
记录的是前一次登入以前所执行过的指令, 而至于这一次登入所执行的指令都被暂 存在内存中,当你成功的注销系统后,该指令记忆才会记录到.bash_history
当中
- 命令与文件补全功能: ([tab] 按键的好处)
- [Tab] 接在一串指令的第一个字的后面,则为命令补全;
- [Tab] 接在一串指令的第二个字以后时,则为『文件补齐』!
- 若安装 bash-completion 软件,则在某些指令后面使用 [tab] 按键时,可以进行『选项/参数的补齐』功能!
- 命令别名设定功能: (alias)
- alias lm='ls -al'
- 工作控制、前景背景控制: (job control, foreground, background)
- 使用前、背景的控制可以让工作进行的更为顺利!
- 至于工作控制(jobs)的用途则更广,可以让我们随时将工作丢到背景中执行!而不怕不小心使用了 [Ctrl] + c 来停掉该程序!
- 此外,也可以在单一登录的环境中,达到多任务的目的呢!
- 程序化脚本: (shell scripts)
- 在DOS年代还记得将一堆指令写在一起的所谓的『批处理文件』.
- 在 Linux 底下的 shell scripts 则发挥更为强大的功能,可以将你平时管理系统常需要下达的连续指令写成一个文件, 该文件并且 可以透过对谈交互式的方式来进行主机的侦测工作!也可以藉由 shell 提供的环境变量及相关指令来 进行设计.
- 通配符: (Wildcard)
- 除了完整的字符串之外, bash 还支持许多的通配符来帮助用户查询与指令下达。
- 举例来说,想要 知道 /usr/bin 底下有多少以 X 为开头的文件吗?使用:『 ls -l /usr/bin/X* 』就能够知道.
6.3 查询指令是否为 Bash shell 的内建命令: type
- 那我怎么知道这个指令是来自于外部指令(指的是其他非 bash 所提供的指令) 或是内建在 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 内建?
[root@study ~]# type ls
ls 是 `ls --color=auto' 的别名 <==未加任何参数,列出 ls 的最主要使用情况
[root@study ~]# type -t ls
alias <==仅列出 ls 执行时的依据
[root@study ~]# type -a ls
ls 是 `ls --color=auto' 的别名 <==最先使用 aliase
ls 是 /usr/bin/ls <==还有找到外部指令在 /bin/ls
# 那么 cd 呢?
[xiaoqi@study ~]$ type cd
cd 是 shell 内嵌
- 这个 type 也可以用 来作为类似 which 指令的用途
6.4 指令的下达与快速编辑按钮
范例:如果指令串太长的话,如何使用两行来输出?
[dmtsai@study ~]$ cp /var/spool/mail/root /etc/crontab\
> /etc/fstab /root
上面这个指令用途是将三个文件复制到 /root 这个目录下而已。不过,因为指令太长,于是利用『 [Enter] 』来将 [Enter] 这个按键『跳脱!』开来,让 [Enter] 按键不再具有『开始执行』的 功能!好让指令可以继续在下一行输入.
需要特别留意, [Enter] 按键是紧接着反斜杠 () 的,两者
中间没有其他字符。因为 仅跳脱『紧接着的下一个字符』而已
如果顺利跳脱 [Enter] 后,下一行最前面就会主动出现 > 的符号, 你可以继续输入指令啰!也就是
说,那个 > 是系统自动出现的.
- 快速组合键
组合键 | 功能与示范 |
---|---|
[ctrl]+u/[ctrl]+k | 分别是从光标处向前删除指令串 ([ctrl]+u) 及向后删除指令串 ([ctrl]+k)。 |
[ctrl]+a/[ctrl]+e | 分别是让光标移动到整个指令串的最前面 ([ctrl]+a) 或最后面 ([ctrl]+e)。 |
总之,当我们顺利的在终端机 (tty) 上面登入后, Linux 就会依据 /etc/passwd 文件的设定给我们一 个 shell (预设是 bash),然后我们就可以依据上面的指令下达方式来操作 shell, 之后,我们就可以 透过 man 这个在线查询来查询指令的使用方式与参数说明.
6.5 Shell 的变量功能
变量是 bash 环境中非常重要的一个玩意儿,我们知道 Linux 是多人多任务的环境,每个人登入系 统都能取得一个 bash shell, 每个人都能够使用 bash 下达 mail 这个指令来收受『自己』的邮件等 等
6.5.1 变量的取用:echo
你可以利用 echo 这个指令来取用变量,但是,变量在被取用时,前面 必须要加上钱字号$
才行.
[xiaoqi@study ~]$ echo $variable
[xiaoqi@study ~]$ echo $PATH
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:/home/xiaoqi/bin
[xiaoqi@study ~]$ echo ${PATH} #使用这种方法获取变量比较规范
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:/home/xiaoqi/bin
6.5.2 变量设定规则
- 变量与变量内容以一个等号『=』来连结,如下所示:
myname=xiaoqi
- 等号两边不能直接接空格符,如下所示为错误:
myname = xiaoqi』或『myname=xiao qi
错误设定. - 变量名称只能是英文字母与数字,但是开头字符不能是数字,如下为错误:
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
- 在一串指令的执行中,还需要藉由其他额外的指令所提供的信息时,可以使用反单引号`指令`或 $(指 令)。特别注意,那个 ` 是键盘上方的数字键 1 左边那个按键,而不是单引号! 例如想要取得核心版本的设定:
version=$(uname -r)
再echo $version
可得3.10.0-229.el7.x86_64
- 若该变量为扩增变量内容时,则可用 "$变量名称"** 或 **${变量} 累加内容,如下所示:
PATH="$PATH":/home/bin
或PATH=${PATH}:/home/bin
- 若该变量需要在其他子程序执行,则需要以export来使变量变成环境变量:
export PATH
- 通常大写字符为系统默认变量,自行设定变量可以使用小写字符,方便判断 (纯粹依照使用者兴趣与嗜好) ;
10.取消变量的方法为使用 unset :unset 变量名称
例如取消 myname name的设定:unset myname
- 变量命名示例
[xiaoqi@study ~]$ name=xiaoqi
[xiaoqi@study ~]$ name="xiao qi"
[xiaoqi@study ~]$ name=xiao\ qi
#我要在 PATH 这个变量当中『累加』:/home/dmtsai/bin 这个目录
[xiaoqi@study ~]$ PATH=$PATH:/home/xiaoqi/bin/
[xiaoqi@study ~]$ PATH="$PATH":/home/xiaoqi/bin/
[xiaoqi@study ~]$ PATH=${PATH}:/home/xiaoqi/bin/
# 以上三种格式在PATH里头的设定都是可以的
#我要将 name 的内容多出 "yes" 呢?
[xiaoqi@study ~]$ echo name="$name"yes
name=xiao qiyes
[xiaoqi@study ~]$ echo name=${name}yes
name=xiao qiyes #常用这个设定方式.
# 如何让我刚刚设定的 name=xiaoqi 可以用在下个 shell 的程序?
[root@study xiaoqi]# name=xiaoqi #设定变量
[root@study xiaoqi]# bash #进入到新的子程序
[root@study xiaoqi]# echo $name #输出name变量
<--并没有输出设定变量的内容
[root@study xiaoqi]# exit #退出子程序
exit
[root@study xiaoqi]# export name #设置环境变量
[root@study xiaoqi]# bash #再次进入到新子程序
[root@study xiaoqi]# echo $name #再次输出name的内容
xiaoqi <---此时正常输出变量内容
[root@study xiaoqi]# exit
- 什么是『子程序』呢?
就是说,在我目前这个 shell 的情况下,去启用另一个新的 shell ,新的那个 shell 就是子程序啦!在一般的状态下,父程序的自定义变量是无法在子程序内使用的。但是透过 export 将变量变成环境变量后,就能够在子程序底下应用了!
- 如何进入到您目前核心的模块目录?
# 方法一
[root@study xiaoqi]# cd /lib/modules/`uname -r`/kernel
[root@study kernel]# pwd
/lib/modules/3.10.0-327.el7.x86_64/kernel
#方法二
[root@study ~]# cd /lib/modules/$(uname -r)/kernel
[root@study kernel]# pwd
/lib/modules/3.10.0-327.el7.x86_64/kernel
使用方法二比较规范
- 每个 Linux 都能够拥有多个核心版本,且几乎 distribution 的核心版本都不相同。
- 以 CentOS 7.1 (未更新前) 为例,他的预设核心版本是
3.10.0-229.el7.x86_64
,所以核心模块目录在
/lib/modules/3.10.0-229.el7.x86_64/kernel/
内。也由于每个 distributions 的这个值都不相同,但是我 们却可以利用uname -r
这个指令先取得版本信息。就可以透过上面指令当中的内含指令$(uname -r)
先取得版本输出到 cd ... 那个指令当中,就能够顺利的进入目前核心的驱动程序所放置的目录!
6.5.3 unset的使用
[root@study kernel]# echo $name
xiaoqi
[root@study kernel]# unset name
[root@study kernel]# echo $name
[root@study kernel]#
根据上面的案例你可以试试看!就可以了解变量的设定!这个是很重要的!请勤加练习! 其中,较为重要的一些特殊符号的使用!例如单引号、双引号、跳脱字符、钱字号、反单引号等等,
6.5.4 环境变量的功能
- 环境变量可以帮我们达到很多功能~包括家目录的变换啊、提示字符的显示啊、执行文件搜寻的路径等等.
- 既然环境变量有那么多的功能,目前我的shell环境中, 有多少默认的环境变量呢?我们可以利用两个指令来查阅,分别是
env
与export
!
#列出目前的 shell 环境下的所有环境变量与其内容。
XDG_SESSION_ID=1
HOSTNAME=study.centos.xiaoqi <---主机名
SELINUX_ROLE_REQUESTED=
SHELL=/bin/bash <---目前这个环境使用的shell是哪一个程序
TERM=xterm <---这个终端使用的是环境是什么类型
HISTSIZE=1000 <---记录指令的笔数(Centos默认记录1000笔)
SSH_CLIENT=10.10.1.222 53851 22 <---ssh登录者IP和端口
SELINUX_USE_CURRENT_RANGE=
OLDPWD=/root <---上一个工作目录的所在
SSH_TTY=/dev/pts/0 <---ssh链接使用的那个终端窗口
USER=xiaoqi <---使用者的名字
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;31:*.tgz=01;
31:*.arc=01;31:.... <---一些颜色的显示
PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:/home/xiaoqi/bin:/home/xiaoqi/bin/:/home/xiaoqi/bin/:/home/xiaoqi/bin/
MAIL=/var/spool/mail/xiaoqi <---这个用户所取用的 mailbox 位置
PWD=/lib/modules/ <---目前用户所在的工作目录 (利用 pwd 取出!)
LANG=zh_CN.UTF-8 <---这个与语系有关,底下会再介绍!
SELINUX_LEVEL_REQUESTED=
HISTCONTROL=ignoredups
HOME=/root <---这个用户的家目录!
SHLVL=2
LOGNAME=xiaoqi <---登入者用来登入的账号名称
SSH_CONNECTION=10.10.1.222 53851 10.10.1.150 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
XDG_RUNTIME_DIR=/run/user/1000
_=/usr/bin/env <---上一次使用的指令的最后一个参数(或指令本身)
env
是environment
(环境) 的简写啊,上面的例子当中,是列出来所有的环境变量.- 当然,如果使用
export
也会是一样的内容~ 只不过,export
还有其他额外的功能.
各个变量名之间的关系
- HOME
代表用户的家目录。还记得我们可以使用 cd ~ 去到自己的家目录吗?或者利用 cd 就可以直接回到用户家 目录了。
- SHELL
告知我们,目前这个环境使用的 SHELL 是哪支程序? Linux 预设使用 /bin/bash .
- HISTSIZE
这个与『历史命令』有关,亦即是, 我们曾经下达过的指令可以被系统记录下来,而记录的『笔数』则是由这个值来设定的。
当我们使用 mail 这个指令在收信时,系统会去读取的邮件信箱文件 (mailbox)。
- PATH
就是执行文件搜寻的路径啦~目录与目录中间以冒号(:)分隔,由于文件的搜寻是依序由 PATH 的变量内的 目录来查询,所以,目录的顺序也是重要的。
- LANG
这个重要!就是语系数据啰~很多讯息都会用到他, 举例来说,当我们在启动某些 perl 的程序语言文件 时,他会主动的去分析语系数据文件, 如果发现有他无法解析的编码语系,可能会产生错误喔!一般来说, 我们中文编码通常是 zh_CN.GBK 或者是 zh_CN.UTF-8,这两个编码偏偏不容易被解译出来,所以,有的时候,可能需要修订一下语系数据。
- RANDOM
这个玩意儿就是『随机随机数』的变量啦!目前大多数的 distributions 都会有随机数生成器,那就是 /dev/random 这个文件。
我们可以透过这个随机数文件相关的变量 ($RANDOM) 来随机取得随机数值喔。 在 BASH 的环境下,这个 RANDOM 变量的内容,介于 0~32767 之间,所以,你只要 echo $RANDOM 时, 系统就会主动的随机取出一个介于 0~32767 的数值。万一我想要使用 0~9 之间的数值呢?呵呵~利用 declare 宣告数值类型, 然后这样做就可以了:
[root@study kernel]# declare -i number=$RANDOM*10/32767;echo $number
6 <---此时会随机取出 0~9 之间的数值!
6.5.5 用set观察所有变量 (含环境变量与自定义变量)
bash 可不只有环境变量喔,还有一些与 bash 操作接口有关的变量,以及用户自己定义的变量存在 的。
那么这些变量如何观察呢?这个时候就得要使用 set 这个指令了。 set 除了环境变量之外, 还 会将其他在 bash 内的变量通通显示出来.
BASH=/usr/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=113 <== 在目前的终端机环境下,使用的字段有几个字符长度
HISTFILE=/root/.bash_history <== 历史命令记录的放置文件,隐藏档
HISTFILESIZE=1000 <== 存起来(与上个变量有关)的文件之指令的最大纪录笔数。
HISTSIZE=1000 <== 目前环境下,内存中记录的历史命令最大笔数。
IFS=$' \t\n' <== 预设的分隔符
LINES=32 <== 目前的终端机下的最大行数
MACHTYPE=x86_64-redhat-linux-gnu <== 安装的机器类型
OSTYPE=linux-gnu <== 操作系统的类型!
PS1='[\u@\h \W]\$ ' <== PS1 就厉害了。这个是命令提示字符,也就是我们常见的
[root@www ~]# 或 [dmtsai ~]$ 的设定值啦!可以更动的!
PS2='> ' <== 如果你使用跳脱符号 (\) 第二行以后的提示字符也
$ <== 目前这个 shell 所使用的 PID
? <== 刚刚执行完指令的回传值。
....
一般来说,不论是否为环境变量,只要跟我们目前这个 shell 的操作接口有关的变量, 通常都会被 设定为大写字符,也就是说,『基本上,在 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 内容
[root@study ~]# cd /home/
[root@study home]# PS1='[\u@\h \w \A #\#]\$ '
[root@study /home 01:01 #61]#
# 看到了吗?提示字符变了!变的很有趣吧!其中,那个 #85 比较有趣
$:(关于本 shell 的 PID)
钱字号本身也是个变量喔!这个代表的是『目前这个 Shell 的线程代号』,亦即是所谓的 PID (Process ID)。
想要知道我们的 shell 的 PID , 就可以用:echo $$
即可!出现的数字就是 PID 号码。
?:(关于上个执行指令的回传值)
问号也是一个特殊的变量?没错!在 bash 里面这个变量可重要的很! 这个变数是:『上 一个执行的指令所回传的值』, 上面这句话的重点是『上一个指令』与『回传值』两个地方。当 我们执行某些指令时, 这些指令都会回传一个执行后的代码。一般来说,如果成功的执行该指令, 则会回传一个 0 值,如果执行过程发生错误,就会回传『错误代码』才对!一般就是以非为 0 的 数值来取代。
[root@study ~]# echo $SHELL
/bin/bash
[root@study ~]# echo $?
0
[root@study ~]# lsa
bash: lsa: 未找到命令...
[root@study ~]# echo $?
127
[root@study ~]# echo $?
0
#由于上一个指令是正常执行所以返回值也是0
6.5.6 OSTYPE, HOSTTYPE, MACHTYPE:(主机硬件与核心的等级)
目前个人计算机的 CPU 主要分 为 32/64 位,其中 32 位又可分为 i386, i586, i686,而 64 位则称为 x86_64。
由于不同等级的 CPU 指令集不太相同,因此你的软件可能会针对某些 CPU 进行优化,以求取较佳的软件性能。
所以软件就有 i386, i686 及 x86_64 之分。以目前 (2015) 的主流硬件来说,几乎都是 x86_64 的 天下!
因此 CentOS 7 开始,已经不支持 i386 兼容模式的安装光盘
6.5.7 export:自定义变量转成环境变量
谈了env
与set
现在知道有所谓的环境变量与自定义变量,那么这两者之间有啥差异呢?其实这两 者的差异在于『 该变量是否会被子程序所继续引用』.
当你登入 Linux 并取得一个 bash 之后,你的 bash 就是一个独立的程序,这个程序的识别使用的是 一个称为程序标识符,被称为 PID 的就是。 接下来你在这个 bash 底下所下达的任何指令都是由这 个 bash 所衍生出来的,那些被下达的指令就被称为子程序了。
- 如图所示:
如上所示,我们在原本的 bash 底下执行另一个 bash ,结果操作的环境接口会跑到第二个 bash 去(就是子程序), 那原本的 bash 就会在暂停的情况 (睡着了,就是 sleep)。整个指令运作的环境是实 线的部分!若要回到原本的 bash 去, 就只有将第二个 bash 结束掉 (下达 exit 或 logout) 才行。
这个程序概念与变量有啥关系呢?因为子程序仅会继承父程序的环境变量, 子程序不会继承父程序的自定义变量!所以你在原本 bash 的自定义变量在进入了子程序后就会消失不见,一直到你离开子程序并回到原本的父程序后,这个变量才会又出现!
换个角度来想,也就是说,如果我能将自定义变量变成环境变量的话,那不就可以让该变量值继续存在于子程序,此时,那个 export 指令就很有用!如你想要让该变量内容继续的在子程序中使用,那么就请执行:
[root@study ~]# export 变量名称
这东西用在『分享自己的变量设定给后来呼叫的文件或其他程序』,主控文 后面呼叫其他附属文件(类似函式的功能),但是主控文件与附属文件内都有相同的变量名称, 若 一再重复设定时,要修改也很麻烦,此时只要在原本的第一个文件内设定好『 export 变量 』, 后 面所呼叫的文件就能够使用这个变量设定了!而不需要重复设定,这非常实用于 shell script 当中.如果仅下达 export 而没有接变量时,那么此时将会把所有的『环境变量』输出.
[root@study ~]# export
declare -x HISTCONTROL="ignoredups"
declare -x HISTSIZE="1000"
declare -x HOME="/root"
declare -x HOSTNAME="study.centos.xiaoqi"
declare -x LANG="zh_CN.UTF-8"
declare -x LESSOPEN="||/usr/bin/lesspipe.sh %s"
declare -x LOGNAME="xiaoqi"
......
- 那如何将环境变量转成自定义变量呢?可以使用
declare
.
6.5.8 影响显示结果的语系变量 (locale)
目前大多数的 Linux distributions 已经都是支持日渐流行的万国码了,也都支持大部分的国家语系。 那么我们的 Linux 到底支持了多少的语系呢?
- 这可以由 locale 这个指令来查询
[root@study ~]# locale -a | more -
aa_DJ
aa_DJ.iso88591
aa_DJ.utf8
aa_ER
aa_ER@saaho
aa_ER.utf8
aa_ER.utf8@saaho
...
- 简体中文语系至少支持了两种以上的编码,那么我们如何修改这些编码呢?其实可以透过底下这些变量:
[root@study ~]# locale <==后面不加任何选项与参数即可!
LANG=zh_CN.UTF-8 <==主语言的环境
LC_CTYPE="zh_CN.UTF-8" <==字符(文字)辨识的编码
LC_NUMERIC="zh_CN.UTF-8" <==数字系统的显示讯息
LC_TIME="zh_CN.UTF-8" <==时间系统的显示数据
LC_COLLATE="zh_CN.UTF-8" <==字符串的比较与排序等
LC_MONETARY="zh_CN.UTF-8" <==币值格式的显示等
LC_MESSAGES="zh_CN.UTF-8" <==讯息显示的内容,如菜单、错误讯息等
......
LC_ALL= <==整体语系的环境
基本上,你可以逐一设定每个与语系有关的变量数据,但事实上,如果其他的语系变量都未设定, 且你有设定LANG
或者是LC_ALL
时,则其他的语系变量就会被这两个变量所取代!这也是为什么我们在 Linux 当中,通常说明仅设定LANG
或LC_ALL
这两个变量而已,因为他是最主要的设定 变量!为什么在 Linux 主机的终端机接口 (tty1 ~ tty6) 的环境 下,如果设定『 LANG=zh_TW.utf8 』这个设定值生效后,使用 man 或者其他讯息输出时, 都会 有一堆乱码,尤其是使用 ls -l 这个参数时?
因为在 Linux 主机的终端机接口环境下是无法显示像中文这么复杂的编码文字,所以就会产生乱码了。也就是如此,我们才会必须要在 tty1 ~ tty6 的环境下,加装一些中文化接口的软件,才能够看到中文!不过,如果你是在 MS Windows 主机以远程联机服务器的软件联机到主机的话,那么,其实文字接口确实是可以看到中文的。此时反而你得要在 LC_ALL 设定中文编码才好!
- 无论如何,如果发生一些乱码的问题,那么设定系统里面保有的语系编码
例如: en_US 或 en_US.utf8 等等的设定,应该就 OK了那么系统默认支持多少种语系呢? 当我们使用 locale
时,系统是列出目前 Linux 主机内保有的语系文件, 这些语系文件都放置在: /usr/lib/locale/ 这个目录中。你当然可以让每个使用者自己去调整自己喜好的语系,但是整体系统默认的语系定义在哪里呢? 其实就是在 /etc/locale.conf
[root@study ~]# cat /etc/locale.conf
LANG="zh_CN.UTF-8"
6.5.9 变量的有效范围
变量也有使用的『范围』?没错~我们在上头的 export 指令说明中,就提到了这个概念了。如果在跑程序的时候,有父程序与子程序的不同程序关系时,则『变量』可否被引用与 export 有关。 被 export 后的变量,我们可以称他为『环境变量』! 环境变量可以被子程序所引用,但是其他的自定义变量内容就不会存在于子程序中。
- 环境变量=全局变量
- 自定义变数=局部变量
为什么环境变量的数据可以被子程序所引用呢?
- 当启动一个 shell,操作系统会分配一记忆区块给 shell 使用,此内存内之变量可让子程序取用
- 若在父程序利用 export 功能,可以让自定义变量的内容写到上述的记忆区块当中(环境变量);
- 当加载另一个 shell 时 (亦即启动子程序,而离开原本的父程序了),子 shell 可以将父 shell 的环境变量所在的记忆区块导入自己的环境变量区块当中。
透过这样的关系,我们就可以让某些变量在相关的程序之间存在,以帮助自己更方便的操作环境喔!不过要提醒的是,这个『环境变量』与『bash 的操作环境』意思不太一样,举例来说, PS1 并不是环境变量, 但是这个 PS1 会影响到 bash 的接口 (提示字符)!
6.5.10 变量键盘读取、数组与声明: read, array, declare
我们上面提到的变量设定功能,都是由指令列直接设定的,那么,可不可以让用户能够经由键盘输入?什么意思呢?是否记得某些程序执行的过程当中,会等待使用者输入 "yes/no" 之类的讯息?
在 bash 里面也有相对应的功能喔!此外,我们还可以宣告这个变量的属性,例如:数组或者是数字等等.
read
要读取来自键盘输入的变量,就是用 read 这个指令了。这个指令最常被用在 shell script 的撰写当中.
[dmtsai@study ~]$ read [-pt] variable
选项与参数:
-p :后面可以接提示字符!
-t :后面可以接等待的『秒数!』这个比较有趣~不会一直等待使用者啦!
# 让用户由键盘输入一内容,将该内容变成名为 atest 的变量
[root@study ~]# read atest
this is a test
[root@study ~]# echo ${atest}
this is a test
# 提示使用者 30 秒内输入自己的大名,将该输入字符串作为名为 named 的变量内容
[root@study ~]# read -p "please keyin your name: " -t 30 named
please keyin your name: xiaoqi
[root@study ~]# echo ${named}
xiaoqi
read 之后不加任何参数,直接加上变量名称,那么底下就会主动出现一个空白行等待你的输入(如范 例一)。如果加上 -t 后面接秒数,例如上面的范例二,那么 30 秒之内没有任何动作时, 该指令就会自动略过了~
如果是加上-p!在输入的光标前就会有比较多可以用的提示字符给我们参考!
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 的加总结果
[root@study ~]# sum=100+300+50
[root@study ~]# echo ${sum}
100+300+50 <---这是字符型的相加
[root@study ~]# declare -i sum=100+3+50 <---整数型的相加
[root@study ~]# echo ${sum}
153
- 由于在默认的情况底下, bash 对于变量有几个基本的定义:
变量类型默认为『字符串』,所以若不指定变量类型,则 1+2 为一个『字符串』而不是『计算式』。 所以 上述第一个执行的结果才会出现那个情况的;bash 环境中的数值运算,预设最多仅能到达整数形态,所以 1/3 结果是 0;
# 将 sum 变成环境变量
[root@study ~]# declare -x sum
[root@study ~]# export | grep sum
declare -ix sum="153" <---环境变量包括了 i 与 x 的定义
#让 sum 变成只读属性,不可更动!
[root@study ~]# declare -r sum
[root@study ~]# sum=tesgting
bash: sum: 只读变量
#让 sum 变成非环境变量的自定义变量!
[root@study ~]# declare +x sum <---将 - 变成 + 可以进行『取消』动作
[root@study ~]# declare -p sum <----p 可以单独列出变量的类型
declare -ir sum="153" <---只剩下 i, r 的类型,不具有 x.
declare 也是个很有用的功能~尤其是当我们需要使用到底下的数组功能时,他也可以帮我们宣告数组的属性!不过,老话一句,数组也是在 shell script 比较常用! 比较有趣的是,如果你不小 心将变量设定为『只读』,通常得要注销再登入才能复原该变量的类型了.
数组 (array) 变量类型
某些时候,我们必须使用数组来宣告一些变量,这有什么好处啊?在一般人的使用上, 果然是看不 出来有什么好处的不过,如果您曾经写过程序的话,那才会比较了解数组的意义~ 数组对写数值 程序的设计师来说,可是不能错过学习的重点之一.
- 在 bash 里头,数组的设定方式是:
var[index]=content
意思是说,我有一个数组名为 var ,而这个数组的内容为 var[1]=小明,var[2]=大明,var[3]=好明 .... 等等,那个 index 就是一些数字,重点是用中刮号 ([ ]) 来设定的。 目前我们 bash 提供的是一 维数组。如果您不必写一些复杂的程序, 那么这个数组的地方,可以先略过,等到有需要 再来学习即可!因为要制作出数组, 通常与循环或者其他判断式交互使用才有比较高的存在意义!
#设定上面提到的 var[1] ~ var[3] 的变数。
[root@study ~]# echo "${var[1]},${var[2]},${var[3]}"
small min,big min,nice min
数组的变量类型比较有趣的地方在于『读取』,一般来说,建议直接以 ${数组} 的方式来读取,比较正确无误的.
6.5.11 文件系统及程序的限制关系:ulimit
想象一个状况:我的 Linux 主机里面同时登入了十个人,这十个人不知怎么搞的, 同时开启了 100 个文件,每个文件的大小约 10MBytes ,请问一下, 我的 Linux 主机的内存要有多大才够?10*100*10 = 10000 MBytes = 10GBytes ... 为了要预防这个情况 的发生,所以我们的 bash 是可以『限制用户的某些系统资源』的,包括可以开启的文件数量, 可 以使用的 CPU 时间,可以使用的内存总量等等
[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)数量。
#列出你目前身份(假设为一般账号)的所有限制数据数值
[root@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) 7240
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) 7240
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
# 限制用户仅能建立 10MBytes 以下的容量的文件
[root@study ~]# ulimit -f 10240
[root@study ~]# ulimit -a | grep 'file size'
core file size (blocks, -c) 0
file size (blocks, -f) 10240 <--最大量为 10240Kbyes,相当 10Mbytes
[root@study ~]# dd if=/dev/zero of=123 bs=1M count=20
文件大小超出限制(吐核) <--尝试建立20M文件,报文件超出大小
[root@study ~]# rm 123
rm:是否删除普通文件 "123"?y <--删除文件,同时你得要注销再次的登入才能解开 10M 的限制
单一 filesystem 能够支持的单一文件大小与 block 的大小有关。但是文件系统的限制容量都允许的太大了!如果想要让使用者建立的文件不要太大时,我们是可以考虑用 ulimit 来限制使用者可以建立的文件大小!利用 ulimit -f 就可以来设定了!例如上面的范例二,要注意单位喔!单位是 Kbytes。
想要复原 ulimit 的设定最简单的方法就是注销再登入,否则就是得要重新以 ulimit 设定才行! 不过,要注意的是,一般身份使用者如果以 ulimit 设定了 -f 的文件大小,那么他只能继续减小文件 容量,不能增加文件容量喔!
若想要管控使用者的 ulimit 限值,可以使用
pam
5.6.12 变量内容的删除、取代与替换 (Optional)
变量内容的删除
变量的内容可以很简单的透过几个咚咚来进行删除喔!我们使用 PATH 这个变量的内容来做测试好 了。
# 先让小写的 path 自定义变量设定的与 PATH 内容相同
[root@study ~]# path=${PATH}
[root@study ~]# echo ${path}
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:/home/xiaoqi/bin
#假设我不喜欢 local/bin,所以要将前 1 个目录删除掉,如何显示?
[root@study ~]# echo ${path#/*local/bin:}
/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:/home/xiaoqi/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
- 这样了解了 # 的功能了,接下来让我们来看看底下的范例三
#我想要删除前面所有的目录,仅保留最后一个目录
[root@study ~]# echo ${path#/*:}
/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:/home/xiaoqi/bin
# 由于一个 # 仅删除掉最短的那个,因此他删除的情况可以用底下的删除线来看:
# ~~/usr/local/bin:~~/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:/home/xiaoqi/bin
[root@study ~]# echo ${path##/*:}
/home/xiaoqi/bin
# 嘿!多加了一个 # 变成 ## 之后,他变成『删除掉最长的那个数据』!亦即是:
# ~~/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:~~/home/xiaoqi/bin
因为在 PATH 这个变量的内容中,每个目录都是以冒号『:』隔开的,所以要从头删除掉目录就是介于斜线 (/) 到冒号 (:) 之间的数据!但是 PATH 中不止一个冒号 (:)! 所 以 # 与 ## 就分别代表:
#
:符合取代文字的『最短的』那一个;##
:符合取代文字的『最长的』那一个
上面谈到的是『从前面开始删除变量内容』,那么如果想要『从后面向前删除变量内容』呢? 这个 时候就得使用百分比 (%) 符号.
- 来看看范例四怎么做
#我想要删除最后面那个目录,亦即从 : 到 bin 为止的字符串
[root@study ~]# echo ${path%:*bin}
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin
# 注意啊!最后面一个目录不见了!
# 这个 % 符号代表由最后面开始向前删除!所以上面得到的结果其实是来自如下:
# /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:~~/home/xiaoqi/bin~~
# 那如果我只想要保留第一个目录呢?
[root@study ~]# echo ${path%%:*bin}
/usr/local/bin
# 同样的, %% 代表的则是最长的符合字符串,所以结果其实是来自如下:
# /usr/local/bin~~:/usr/bin:/usr/local/sbin:/usr/sbin:/home/xiaoqi/.local/bin:/home/xiaoqi/bin~~
由于我是想要由变量内容的后面向前面删除,而我这个变量内容最后面的结尾是『/home/dmtsai/bin』.所以可以看到上面我删除的数据最终一定是『bin』,亦即是『:bin』那个 代表通配符! 至于 % 与 %% 的意义其实与 # 及 ## 类似!
例题:
假设你是 xiaoqi ,那你的 MAIL 变量应该是 /var/spool/mail/xiaoqi 。假设你只想要保留最后面那个档名 (xiaoqi), 前面的目录名称都不要了,如何利用 $MAIL 变量来达成?
题意其实是这样『/var/spool/mail/xiaoqi』,亦即删除掉两条斜线间的所有数据(最长符合)。 这个时候你就可 以这样做即可:
[root@study ~]# echo ${MAIL##/*/}
xiaoqi
相反的,如果你只想要拿掉文件名,保留目录的名称,亦即是『/var/spool/mail/xiaoqi』 (最短符合)。但假设 你并不知道结尾的字母为何,此时你可以利用通配符来处理即可,如下所示:
[root@study ~]# echo ${MAIL%/*}
/var/spool/mail
变量内容的取代
接上以上范例.
#将 path 的变量内容内的 sbin 取代成大写 SBIN:
[root@study ~]# echo ${path/sbin/SBIN}
/usr/local/bin:/usr/bin:/usr/local/SBIN:/usr/sbin:/home/xiaoqi/.local/bin:/home/xiaoqi/bin
# 这个部分就容易理解的多了!关键词在于那两个斜线,两斜线中间的是旧字符串
# 后面的是新字符串,所以结果就会出现如上述的特殊字体部分!
[root@study ~]# echo ${path//sbin/SBIN}
/usr/local/bin:/usr/bin:/usr/local/SBIN:/usr/SBIN:/home/xiaoqi/.local/bin:/home/xiaoqi/bin
# 如果是两条斜线,那么就变成所有符合的内容都会被取代!
- 我们将这部份作个总结说明一下:
变量设定方式 | 说明 |
---|---|
${变量#关键词} | 若变量内容从头开始的数据符合『关键词』,则将符合的最短数据删除 |
${变量##关键词} | 若变量内容从头开始的数据符合『关键词』,则将符合的最长数据删除 |
${变量%关键词} | 若变量内容从尾向前的数据符合『关键词』,则将符合的最短数据删除 |
${变量%%关键词} | 若变量内容从尾向前的数据符合『关键词』,则将符合的最长数据删除 |
${变量/旧字符串/新字符串} | 若变量内容符合『旧字符串』则『第一个旧字符串会被新字符串取代』 |
${变量//旧字符串/新字符串} | 若变量内容符合『旧字符串』则『全部的旧字符串会被新字符串取代』 |
变量的测试与内容替换
在某些时刻我们常常需要『判断』某个变量是否存在,若变量存在则使用既有的设定,若变量不存在则给予一个常用的设定。
- 我们举底下的例子来说明"
#测试一下是否存在 username 这个变量,若不存在则给予 username 内容为 root
[root@study ~]# echo ${usename}
<==由于出现空白,所以 username 可能不存在,也可能是空字符串
[root@study ~]# usename=${usename-root}
[root@study ~]# echo ${usename}
root <==因为 username 没有设定,所以主动给予名为 root 的内容。
[root@study ~]# usename="xiaoqi" <==主动设定 username 的内容
[root@study ~]# usename=${usename-root}
[root@study ~]# echo ${usename}
xiaoqi <==因为 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@study ~]# usename=""
[root@study ~]# usename=${usename-root}
[root@study ~]# echo ${usename}
<==因为 username 被设定为空字符串了!所以当然还是保留为空字符串!
[root@study ~]# usename=${usename:-root}
[root@study ~]# echo ${usename}
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 |
版权属于:龙之介大人
本文链接:https://i7dom.cn/153/2019/17/linux-bash-shell-01.html
本站所有原创文章采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 您可以自由的转载和修改,但请务必注明文章来源和作者署名并说明文章非原创且不可用于商业目的。