Linux的基本学习(七)——BASH

前言:

上一部分,我们简单介绍了Bash,今天我们来接着上面的内容继续学习

 

 

 

 

 

 


BASH

Shell的变量功能:

我们用一个简单的“字眼”来替换另一个比较复杂或是容易变动的数据,这个字眼就是变量

  • 变量的可变性与方便性
  • 影响bash环境操作的变量

某些特定变量会影响到bash的环境。如PATH变量(能否在任何目录下执行某个命令与PATH变量有很大的关系),例如在执行ls这个命令时,系统就是通过PATH变量里面的内容所记录的路径顺序来查找命令的,如果在找完PATH变量内的路径都找不到ls这个命令时,就会在屏幕显示”command not found”的错误信息;
在Linux下,所有的执行都是需要一个执行码。用户真正以shell来跟Linux通信,是在正确的登录Linux之后。这个时候就拥有一个bash的执行程序,也才可以真正经由bash来跟系统通信。而在进入shell之前,由于系统需要一些变量(环境变量)来提供给它数据的访问(或者是一些环境的设置参数值,例如是否要显示彩色等的);所以就有一些所谓的”环境变量”需要来读入系统中了,这些环境变量例如PATH、HOME、MAIL、SHELL等;为了区别与自定义变量的不同,环境变量通常以大写字符来表示。

  • 脚本程序设计(shell script)的好帮手

变量的使用与设置:echo、变量设置规则、unset:

变量的查看:

使用 echo $变量名 或是 echo  ${变量名} 就可以查看变量的定义

变量的设置与修改:

直接用等号(=)连接变量与它的内容就好

在bash中,当一个变量名称尚未被设置时,默认的内容就是【空】,另外,变量名的设置需要符合以下规定:

  • 等号两边不能直接接空格
  • 变量名称只能是英文字符与数字,但是开头字符不能是数字
  • 变量内容若有空格可使用双引号【”】或者单引号【’】将变量内容结合起来,但是
    • 双引号内的特殊字符如“$”等,可以保有原本的特性,例如:
      • var=”lang is $LANG”,则echo $var就会得到 lang is zh_CN.UTF-8
    • 单引号内的特殊字符则仅为一般字符(纯文本),不过
      • 可以用转义符【\】将特殊符号(如【Enter】、$、\、空格、’ 等)变成一般字符
  • 在一连串的命令执行中,还需要借由其他额外的命令所提供的信息时,可以使用反单引号【`】(这是1左边那个键)命令或【$(命令)】。
  • 若该变量为扩增变量时,则可用”$变量名称”或 ${变量}累加内容,例如 PATH:”$PATH”:/home/bin 或 PATH=${PATH}:/home/bin
  • 若该变量需要在其他子程序执行,则需要以export来使变量变成环境变量
  • 通常大写字符为系统默认变量,自行设置变量可以使用小写字符,方便判断(纯粹依照用户兴趣与嗜好)

若要取消变量的设置,可以使用unset:

unset myname

给变量累加值的话,可以如下:

让刚刚设置的变量应用在下个shell程序

利用export使其成为环境变量

什么是子进程,就是说,在我目前这个shell的情况下,去启用另一个新的shell,新的那个shell就是子进程。在一般情况下,父进程的自定义变量无法在子进程内使用,但是通过export将变量设置成为环境变量后,就能够在子进程里边使用了。

我们还可以巧妙利用变量:

例如这个,利用变量进入目前内核的模块目录,uname -r的值就是内核版本,正好就省了写着麻烦的目录

 

环境变量功能:

环境变量可以帮助我们实现很多功能,包括根目录(主文件夹)的变换、提示字符的显示、执行文件查找的路径等,还有很多很多。

用env观察环境变量与常见环境变量的说明

env是environment(环境)的缩写,上面的例子中,是列出来所有的环境变量,当然,如果使用export也会是一样的内容,只不过,export还有其他额外的功能,我们等一下再来提这个export。上面的各种变量的信息:

  • HOME:

代表用户的根目录。

  • SHELL:

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

  • HISTSIZE

这个与历史命令有关,即我们曾经执行过的命令可以被系统记录下来,而记录的条数则是由这个值来设置的

  • MAIL

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

  • PATH

就是执行文件查找的路径,目录与目录中间以冒号(:)分隔,由于文件的查找是依赖path变量的目录来查询,所以,目录的顺序很重要

  • LANG

这个很重要,就是语系数据,很多信息都会用到它,举例来说,当我们在启动某些perl的程序语言文件时,它会主动地去分析语系数据文件,如果发现有它无法解析的编码语系,可能会产生错误。一般来说,我们中文编码通常是zh_CN.GB2312或是zh_CN.UTF-8,这两个编码偏偏不容易被解码出来,所以有的时候,可能需要自定义一下语系数据,这部分后面说。

  • RANDOM

这是随机数的变量。目前的大多数Linux的发行版,都会有随机数生成器,那就是/dev/random 这个文件。我们可以通过这个随机数文件相关的变量($RANDOM)来随机取得随机数值。在BATH环境下,这个RANDOM变量的内容介于0~32767的数值,当我们想要0~9的数值,丽丽declare声明数值类型,然后这样做即可

declare -i number=$RANDOM*10/32768 ; echo $number

学过编程应该好理解,一个int型数字赋值给number

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

set除了环境变量之外,还会将其他在bash内的变量通通显示出来,信息很多

我这个上面的并不全,我们来看鸟哥推荐的几个比较重要的吧:

  • PS1:(提示字符的设置,默认是 ‘[\u@\h  \W]\$’)
    • \d :可以显示日期格式【星期 月 日】,如:【Mon Feb 2】
    • \H :完整的主机名
    • \h :仅取得主机名在第一个小数点之前的名字
    • \t :显示时间(24小时格式)【HH:MM:SS】
    • \T :显示时间(12小时格式)【HH:MM:SS】
    • \A :显示时间(24小时格式)【HH:MM】
    • @ :显示时间(12小时格式)【am/pm格式】
    • \u :目前使用者的账号
    • \v :BASH的版本信息
    • \w :完整的工作目录名称
    • \W :利用basename函数取的工作目录名称
    • \# :下达的第几个指令
    • \$ :提示字符,如果是root时,提示字符为#,否则就是$

  • $:(关于本shell的PID)

美元符号本身也是个变量。这个东西代表的是目前这个shell的进程号,即所谓的PID(Process ID)。想要知道我们的shell的PID,可以使用 echo $$

  • ?:(关于上个执行命令的返回值)

当我们执行一些命令时,这些命令都会返回一个执行后的代码。一般来说,如果成功的执行该命令,则返回一个0值,如果执行过程发生错误,就会返回错误代码(非0的数字)

  • OSTYPE、HOSTTYPE、MACHTYPE:(主机硬件与内核的等级)

目前个人CPU主要分为32位与64位,其中32位又可分为i386、i586、i686,而64位则称为x86-64。由于不同等级之间的CPU指令集不太相同,因此你的软件可能会针对某些CPU进行优化,以求得较佳的软件性能。所以软件就有i386、i686、x86-64之分。另外强调一下,你可以在x86-64的硬件上安装i386的Linux操作系统,但不能再i686的硬件上安装x86-64的Linux操作系统

  • export:(自定义变量转成环境变量)

环境变量与自定义变量主要有啥差异?其实就是在于【该变量是否会被子进程所继续引用】,那什么是父进程什么是子进程,这就要了解一下命令的执行操作了。

当你登录Linux并取得一个bash之后,你的bash就是一个独立的进程,这个进程的识别使用的是进程表示符(PID),接下来你在这个bash下面所执行的任何命令都是由这个bash所衍生出的,那些被执行的命令就被称为子进程。

注意:子进程会继承父进程的环境变量,但不会继承父进程的自定义变量

 

影响显示结果的语系变量(locate)

当我们使用man command的方式去查询某个数据文件时,该说明文件的内容可能会因为我们使用的语系不同而产生乱码。这个就是语系的问题。

我们可以用命令——locale  -a 来查询我们的Linux支持多少语系

locale -a

我们可以用locale 来直接查询这些变量


[root@www ~]# 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这两个变量时,则其他的变量都会被这两个变量所替换。

这也是为什么我们在 Linux 当中,通常说明仅设置 LANG 这个变量而已,因为它是最主要的设置变量。

但是即使我们设置了【LANG=zh_CN.utf8】,在终端仍然会乱码,这时因为在Linux主机的终端环境下是无法显示像中文这么复杂的编码文字,所以产生乱码,也就是如此,我们才会必须在tty1~tty6的环境下,安装一些中文界面的软件,才能够看到中文。

 

默认的语系定义在:

[root@www ~]# cat /etc/sysconfig/i18n
LANG="zh_TW.UTF-8"

 

变量的有效范围:

联系编程知识:

  • 环境变量=全局变量
  • 自定义变量=局部变量

为什么环境变量的数据可以被子进程所引用?这是应为内存配置的关系,理论上是这样的:

  • 当启动一个shell,操作系统会分配一内存区域给shell使用,此内存中的变量可让子进程使用
  • 若在父进程利用export功能,可以让自定义变量的内容写到上述的内存区域中(环境变量)
  • 当加载另一个shell时(即启动子进程,而离开原本的父进程),子shell可以将父shell的环境变量所在的内存区域导入自己的环境变量区块中

另外需要提醒的是:环境变量与bash的操作环境意思不太一样,例子:PS1并不是环境变量,但是这个PS1会影响到bash界面(提示字符嘛),所以这些变量要理清楚。

 

变量键盘读取、数组与声明(read、array、declare):

我们上面提到的变量设置功能,都是由命令行直接设置的,那么,可不可以让用户能够经由键盘输入?什么意思?是否记得某些应用程序执行过程当中,会等待用户输入“yes/no”之类的信息?在bash里面也有相对应的功能。此外,我们还可以定义这个变量的属性,例如数组或是数字等

read

要读取来自键盘输入的变量,就是用read这个命令。这个命令最常被用在shell脚本的编写当中,想要跟用户交互?用这个命令就对了,关于脚本的写法,我们以后再说,先看read的相关语法吧!

read  [-pt] variable
-p:后面可以接提示字符
-t:后面可以接等待的【秒数】,这个比较有趣,不会一直等待使用者

declare,typeset

declare或typeset是一样的功能,就是声明变量的类型。如果使用declare后面没有接任何的参数,那么bash就会主动的将所有的变量名称与内容通通显示出来,就好像使用set一样,那么declare还有什么语法呢?

declare [-aixr] variable
-a:将后面名为variable的变量定义成为数组(array)类型
-i:将后面名为variable的变量定义成为整数(integer)类型
-x:用法与export一样,就是将后面的variable编程环境变量
-r:将变量设置成为readonly类型,该变量不可被改变内容,也不能被unset

由于在默认的情况下,bash对于变量有几个基本的定义:

  • 变量类型默认为字符串,所以若不指定变量类型,则1+2为一个字符串而不是计算式
  • bash环境里的数值运算,默认最多仅能达整数形态,所以1/3结果是0

如果你将变量变成readonly(只读),通常要注销再登陆才能恢复该变量的类型

数组(array)变量类型:

在bash中数组的设置方式是:

var[index]=content

意思是说,我有一个数组名为var,而这个数组的内容为 var[1]=小明,var[2]=大刚,var[3]=小红等,index是一些数字。

目前bash提供的是一维数组。

与文件系统及程序的限制关系(ulimit):

我们的bash是可以限制用户的某些系统资源的,包括可以开启的文件数量,可以使用的CPU时间,可以使用的内存总量等,如何设置呢?用ulimit:

ulimit [-SHacdfltu] [配额]
-H:hard limit,严格的设置,必定不能超过这个设置的数值
-S:soft limit,警告的设置,可以超过这个设置值,但是若超过
    #则有警告信息。
-a:后面不接任何选项与参数,可列出所有的配置额度
-c:当某些程序发生错误时,系统可能会将该程序在内存中的信息
    #写成文件(除错用),这种文件称为内核文件(core file),此
    #为限制内核文件的最大容量
-f:此shell可以建立的最大文件容量(一般可能设置为2GB)单位为Kbytes
-d:程序可使用的最大段内存(segment)数量
-l:可用于锁定(lock)的内存量
-t:可使用的最大CPU时间(单位为秒)
-u:单一使用者可以使用的最大进程数量

限制单一文件大小的示例:

你要注销再次登录才能解除10MB的限制

变量内容的删除、取代、替换

删除

变量的内容可以很简单的通过几个东西来进行删除,我们使用PATH这个变量的内容来做测试,

${variable#/*local/bin:}
variable:是原本变量的名称
#:代表从前往后删除,尽删除最短的那个
/*local/bin::代表从/到:范围中符合*local/bin

#是从前到后最短的字符串(非贪婪匹配)

##则是最长的字符串(贪婪匹配)

最后一个/root/bin某位没有:,不符合筛选规则,所以没被删掉

与#对应的是%,代表从后往前:

%最短

%%最长

替换

/代表替换一个

//代表多有满足条件的全部替换

测试

判断某个变量是否存在,若变量存在则使用既有的值,否则则给与一个常用的设置

示例:

除了减号还有很多,请参考表格

变量配置方式 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

expr就是报错信息

命令别名与历史命令:

命令别名的设置:alias、unalias

alias的定义规则与变量定义规则几乎相同,所以你只要在alias后面加上你的{【别名】=’命令 选项…’},就可以定义命令别名了。

使用 alias 去查看你的PC设置的变量别名。

如果你要取消某个设置,可以使用命令 unalias

那么命令别名和变量有什么不同?命令别名是新创一个新的命令,你可以直接执行该命令,至于变量则是需要使用类似【echo】命令才能够调用出变量的内容,这两者当然不一样

历史命令:history

bash有提供命令历史的服务,那么如何查询我们曾经执行过的命令呢?就使用history,当然,如果觉得history要输入的字符太多太麻烦,可以使用命令别名来设置。

history的用法:

history [n]
history [-c]
history [-raw] histfiles
n:数字,列出最近n条
-c:将目前shell中所有的history历史删除
-a:将目前新增的history命令新增入histfiles中,
    #若没有加histfiles,则默认写入~/.bash_history
-r:将histfiles的内容读到目前这个shell的history记录中
-w:将目前的history记录内容写入histfiles中
  • 当我们以bash登录Linux主机之后,系统会主动地由家目录的~/.bash_history 读取以前曾经执行过得命令,那么 ~/.bash_history 会记录几条数据?这就与你的变量 HISTFILESIZE 的值有关了
  • 假设我这次登录主机后,共执行过100次命令,等我注销时,系统就会将101~1100这总共1000条历史命令更新到~/.bash_history当中,也就是说,系统会将最近的HISTFILE条数据记录在文件中
  • 当然,也可以使用history -w强制立刻写入。那为何用【更新】俩个字?因为~/.bash_history记录的条数永远是 HISTFILE 这么多,旧的信息会被主动地删除

history 还可以帮助我们执行命令:

!number
!command
!!
number:执行第几条命令的意思
command:由最近的命令向前找开头为command的命令,并执行
!!:执行上一个命令

基本上history的用途强大,但是需要小心安全问题,尤其是root的历史记录文件,因为如果不小心会将root下执行的很多重要命令记录在~/.bash_history当中,如果这个文件被解析的话,后果不堪。无论如何,使用history配合【!】曾经使用过的命令执行是很有效率的执行方法。

下面来说两个问题:

  • 同一账号同时多次登录的history写入问题

有些朋友在练习Linux的时候喜欢同时开好几个bash界面,这些bash的身份都是root,那么注销时写入会被写入吗?会被写入,但是又会被最后一个注销的bash的记录覆盖。

  • 无法写入时间

历史命令还有一个问题,那就是无法记录命令执行的时间。由于这100多条历史命令是依序记录的,但是并没有记录时间,所以在查询方面会有一些不方便。

 

Bash shell 的操作环境

路径与命令查找顺序:

基本上,命令执行的顺序可以这样看:

  1. 以相对/绝对路径执行命令,例如【/bin/ls】或【./ls】
  2. 由alias找到该命令来执行
  3. 由bash内置的(builtin)命令来执行
  4. 通过$PATH这个变量的顺序查找到的第一个命令来执行

使用 type -a命令 也可以查询到命令的顺序

 bash的登录与欢迎信息:/etc/issue、/etc/motd

我们可以通过cat /etc/issue来查看我们bash的登录信息

里面的信息对应内容如下:

\d:本地端时间的日期
\I:显示第几个终端页面
\m:显示硬件等级(i386/i486/i586)
\n:显示主机的网络名称
\O:显示domain name
\r:操作系统的版本(相当于 uname -r)
\t:显示本地端时间的时间
\S:操作系统的名称
\v:操作系统的版本

接下来,任由你改!

另外,除了/etc/issue之外还有个/etc/issue.net,这是提供给telnet,这个远程登录程序用的。当我们使用telnet连接到主机时,主机的登录画面就会显示/etc/issue.net。

至于如果您想要让用户登录后取得一些信息,例如您想要大家都知道的信息,那么将信息加入到/etc/motd里面。

例如这样:

bash的环境配置文件

系统有一些环境变量配置文件的存在,让bash在启动时直接读取这些配置文件,以规划好bash的操作环境。而这些配置文件又可以分为全局系统配置文件以及用户个人偏好配置文件。

要注意,我们前面做的命令别名、自定义变量,在你注销bash后就会生效,所以你想要保留你的设置,就得将这些设置写入配置文件才行。

login与non-login shell:

  • login shell:取得bash时需要完整的登录流程,就需要login shell。
  • non-login shell:取得bash的方法不需要重复登录的操作。举例,你在X Window登录Linux后,再以X的图形化接口启动终端,此时这个终端接口并没有需要登录,这个bash就是non-login shell;又或者你在bash又开启一个bash进程,也不用输入密码账号,这也是non-login shell

这两个取得shell的情况中,读取的配置文件并不一致。

由于我们需要登录系统,所以先谈谈login shell会读取哪些配置文件?一般来说,login shell会读取这两个配置文件:

  1. /etc/profile:这是系统整体的设置,你最好不要修改它
  2. ~/.bash_profile 或 ~/.bash_login 或 ~/.profile:属于用户个人设置,你要添加自己的数据,就写入这里。

/etc/profile(login_file才会读)

这个配置文件可以利用用户标识符(UID)来决定很多重要的变量数据,这也是每个用户登录取得bash时一定会读取的配置文件。所以如果你想要帮所有用户设置整体环境,那就是改这里,不过,没事不要随便改这个文件。

  • PATH:会根据UID决定PATH变量要不要含有sbin的系统命令目录
  • MAIL:根据账号设置好用户的mailbox到/var/spool/mail/账号名
  • USER:根据用户的账号设置此变量的内容
  • HOSTNAME:根据主机的hostname命令决定此变量内容
  • HISTSIZE:历史命令记录条数
  • umask:包括root默认为022而一般用户默认为002等

/etc/profile可不止会做这些事而已,它还会去调用外部的配置文件,在CentOS 7.x 默认的情况下,下面这些文件会依序被调用

  • /etc/profile.d/*.sh

只要在/etc/profile.d/这个目录下且扩展名为 .sh,另外,用户必须具有r的权限,那么该文件就会被/etc/profile 调用。在CentOS 7.x 中,这个目录下面的文件规定了bash操作界面的颜色、语系、ll与ls命令的命令别名、vi的命令别名、which的命令别名等。如果你需要帮所有用户设置一些共享的命令别名时,可以在这个目录下面自行建立扩展名为 .sh 的文件,将所需要的数据写入即可。

  • /etc/locale.conf

这个文件是由/etc/profile.d/lang.sh 调用的,这也是我们决定bash 默认使用何种语系的重要配置文件。文件里最重要的就是 LANG/LC_ALL 这几个变量的设置。

  • /usr/share/bash-completion/completions/*

记得tab吗?除了命令补齐、文件名补齐之外,还可以进行命令的选项/参数补齐功能。那就是从这个目录里面找到相对应的命令来处理,其实这个目录下面的内容是由 /etc/profile.d/bash_completion.sh 这个文件加载的。

反正你只要记得,bash 的 login shell 情况下所读取的整体环境配置文件其实只有 /etc/profile,但是 /etc/profile 还会呼叫出其他的配置文件,所以让我们的 bash 操作接口变的非常的友善啦! 接下来,让我们来瞧瞧,那么个人偏好的配置文件又是怎么回事?

 

~/.bash_profile (login shell 才会读)

bash 在读完了整体环境配置的 /etc/profile 并借此调用其他配置文件后,接下来则是会读取使用者的个人配置文件。 在 login shell 的 bash 环境中,所读取的个人偏好配置文件其实主要有三个,依序分别是:

  • ~/.bash_profile
  • ~/.bash_login
  • ~/.profile

其实 bash 的 login shell 配置只会读取上面三个文件的其中一个, 而读取的顺序则是依照上面的顺序。也就是说,如果 ~/.bash_profile 存在,那么其他两个文件不论有无存在,都不会被读取。 如果 ~/.bash_profile 不存在才会去读取 ~/.bash_login,而前两者都不存在才会读取 ~/.profile 的意思。 会有这么多的文件,其实是因应其他 shell 转换过来的使用者的习惯而已。

我们来看看luoluo的/home/dmtsai/.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/bin   <==底下这几行在处理个人化配置
export PATH

这个文件内有配置 PATH 这个变量,而且还使用了 export 将 PATH 变成环境变量。由于 PATH 在 /etc/profile 当中已经配置过,所以在这里就以累加的方式添加用户家目录下的 ~/bin/ 为额外的运行文件放置目录。这也就是说,你可以将自己创建的执行文件放置到你自己家目录下的 ~/bin/ 目录, 那就可以直接运行该文件而不需要使用绝对/相对路径来运行该文件。

这个文件的内容比较有趣的地方在于 if … then … 那一段。那一段程序代码我们会在后面谈到,假设你现在是看不懂的。 该段的内容指的是:判断家目录下的 ~/.bashrc 存在否,若存在则读入 ~/.bashrc 的设置。 bash 配置文件的读入方式比较有趣,主要是通过一个命令 source 来读取的! 也就是说 ~/.bash_profile 其实会再调用 ~/.bashrc 的配置内容。最后,我们来看看整个 login shell 的读取流程:

实线的方向是主流线程,虚线的方向则是被调用的配置文件。从上面我们也可以清楚的知道,在CentOS的login shell环境下,最终被读取的配置文件是【~/.bashrc】这个文件,所以你可以将一些自己的偏好写入该文件即可。

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

source 配置文件文件名

由于/etc/profile 与 ~/.bash_profile 都是在取得 login_shell的时候才会读取的配置文件,所以,如果你将自己的偏好设置写入上述的文件后,通常都要注销再登录,配置才能生效。

那么,使用source命令就可以直接生效,无需再注销

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

举一反三,我们可以编辑不同的环境变量配置,当需要哪一个就source哪一个即可。

~/.bashrc(non-login shell才会读)

读完了login shell后,那么 non-login shell 这种非登录情况取得 bash 操作界面的环境配置文件又是什么,仅仅是一个文件——~/.bashrc

里面的内容如下(人人可能不一样)

我们发现,在~/.bashrc 里面调用了/etc/bashrc,为什么要调用它呢?因为/etc/bashrc帮助我们的 bash 定义了下面的内容:

  • 根据不同的UID设置umask的值
  • 根据不同的UID设置提示字符(PS1变量)
  • 调用/etc/profile.d/*.sh 的设置

这个/etc/bashrc 是 CentOS 特有的(其实是 Red Hat 系统特有的),其他不同的Linux发行版可能会使用不同的文件名。由于这个 ~/.bashrc 会调用 /etc/bashrc 及 /etc/profile.d/*.sh ,所以,万一你没有 ~/.bashrc (比如你不小心删除了它),那么你会发现你的 bash 提示字符可能会变成这个样子:

-bash -4.2$

这是正常的,因为你没有调用 /etc/bashrc 来规范 PS1 变量,而且这样的情况也不会影响你使用bash。如果你想要命令提示符,那么可以复制 /etc/skel/.bashrc 到你的家目录,再定义一下你想要的内容,并使用 source 去调用 ~/.bashrc ,命令提示符就回来了。

 其他相关配置文件:

还有一些其他的文件会影响你的操作

  • /etc/man_db.conf

这文件的内容规范了使用man的时候,man page 的路径到哪里去寻找。所以说的简单一点,这个文件规定了执行man的时候,该去哪里查看数据的设置。

那么什么时候要来修改这个文件?如果你是以 tarball 的方式来安装你的软件,那么你的 man page 可能就会放置在 /usr/local/softpackage/man 里面,这个softpackage是你的软件名称,这个时候你就得以手动的方式将该路径加到 /etc/man_db.conf 里面,否则使用man 的时候就会找不到相关的说明文件。

  • ~/.bash_history

默认的情况下,我们的历史记录就记录在这里。而这几个文件能够记录几条数据,则与SISTFILESIZE 这个变量有关。

  • ~/.bash_logout

这个文件则记录了【当我注销 bash 后,系统再帮我做完什么操作后才离开】的意思。默认情况下,系统只是帮助我们清屏,不过你也可以写一些烦人的任务在这里面,比如清空缓存。

终端的环境设置:stty、set

登录的时候我们可以取得一些字符设置的功能,比如Background删除字符,Ctrl+C 强制终止命令运行,这些是怎么办到的?很简单,因为登录终端的时候,会自动获取一些终端的输入环境的设置。

那么怎么查看目前的一些按键的内容?可以利用 stty (setting tty 终端的意思),stty也可以帮助设置终端的输入按键代表的意义

stty [-a]
-a:将目前所有的stty参数都列出来

我们可以利用 stty -a 来列出目前环境中所有的按键列表,再上面的按键当中,需要注意的是特殊字体那几个,此外,如果出现 ^ 表示 Ctrl 那个按键的意思。这些个关键字的意思是:

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

如果你想要重新设置某一项:

stty erase ^h

 

除了 stty 之外,其实我们的 bash 还有自己的一些终端设置值,那就是利用 set 来设置。我们之前提到的一些变量时,可以利用 set 来显示,除此之外,其实 set 还可以帮我们设置整个命令输出/输入的环境。例如记录历史命令、显示错误内容等

set [-uvCHhmBx] 
-u:默认关,启用后,当使用未设置变量时,会报错
-v:默认关,启用后,在信息被输出前、会先显示信息的原始内容
-x:默认关,启用后,在命令被执行前,显示命令的内容(前面有++)
-h:默认开,与历史命令有关
-H:默认开,与历史命令有关
-m:默认开,与任务管理有关
-B:默认开,与中括号 [ ] 的作用有关
-C:默认关,若使用>等,则若文件存在时,该文件不会被重叠

使用示例:

通配符与特殊符号:

在bash的操作环境中还有一个非常有用的功能,那就是通配符(widcard)。下面我们列出一些:

符号 意义
* 代表『 0 个到无穷多个』任意字符
? 代表『一定有一个』任意字符
[ ] 同样代表『一定有一个在括号内』的字符(非任意字符)。例如 [abcd] 代表『一定有一个字符, 可能是 a, b, c, d 这四个任何一个』
[ – ] 若有减号在中括号内时,代表『在编码顺序内的所有字符』。例如 [0-9] 代表 0 到 9 之间的所有数字,因为数字的语系编码是连续的!
[^ ] 若中括号内的第一个字符为指数符号 (^) ,那表示『反向选择』,例如 [^abc] 代表 一定有一个字符,只要是非 a, b, c 的其他字符就接受的意思。

应用示例:


除了这些通配符之外,还有一些常见的特殊字符:

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

 

数据流重定向:

什么是数据流重定向

一般来说,如果你要执行一个命令,通常它会是这样:

我们执行一个命令的时候,这个命令可能会由文件读入数据,经过处理之后,再将数据输出到屏幕上。在上图中,standard output与standard error output 分别代表【标准输出(STDOUT)】、【标准错误输出(STDERR)】,这两个玩意儿默认都是输出到屏幕上边的。

standard output 与 standard error output

简单来说,标准输出指的是命令执行所返回的正确信息,而标准错误输出可理解为命令执行失败后,所返回的错误信息。

数据流重定向可以将standard output(简称 stdout)与standard error output(简称 stderr)分别传送到其它的文件或设备,而分别传送所需要的特殊字符如下:

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

我们看例子,将根目录下的文件及其属性保存到另一个文件里

如果我们再执行向这个rootfile文件流出别的信息,rootfile原本的数据就会被覆盖,这是因为改文件的建立方式是:

  • 该文件若不存在,系统会自动地将它建立起来
  • 当这个文件存在的时候,那么这个系统就会先将这个文件的内容清空,然后再将数据写入
  • 也就是若以>输出到一个已存在的文件中,这个文件就会被覆盖掉

如果你想要累加而不是删除原本内容,用 >> 即可。

上面谈到的标准输出是正确的输出,那如果是错误的输出呢?那就要通过 2> 以及 2>> 来重定向。

标准输出的代码是1,所以 > 、 >>  其实完整的写是 1> 、1>> ,只是平时省略了1而已;标准错误输出的代码是2,所以是 2>、2>>,代码同>、>>之间绝对不能有空格 

若既有输入正确的信息也有错误的信息,则可以  > test_right 2> test_error

/dev/null 垃圾桶黑洞设备与特殊写法

想象一下,如果我知道错误信息会发生,所以要将错误信息忽视掉而不显示或存储?此时黑洞设备/dev/null就很重要了,这个/dev/null可以吃掉任何导向这个设备的信息。

例如:

asdfas 2> /etc/null 

就会什么也不输出,因为原本要输出的错误信息重定向到黑洞里了

又比如,如果你想要将一个命令的正确信息和错误信息输出到同一个文件内,千万不要使用 > test 2> test ,因为此时两股数据同时写入一个文件,又没有使用特殊的语法,此时两股数据可能会交叉写入该文件内,造成次序的错乱,虽然也会有文件生成,但是次序就乱了。正确的方法应该如下:

命令 > test 2>&1
命令 &> test

这两种那种都可以,属于重定向的特殊语法。

standard input:<与<<

这个 < 是什么呢?简单来说就是,将原本需要由键盘输入的内容,改由文件内容来替换

我们先利用标准输出>和cat建立一个文件

输入Ctrl+D就可以退出输入文件内容。由于加入>在cat后,所以整个catfile文件会被主动地建立,而内容就是刚刚键盘上输入的那几行数据了。这是键盘输入的,那我们该怎么利用已经存在的文件来替换键盘的输入呢?如下

如同复制(cp)一样。

这个东西非常有帮助,尤其是用在类似mail这种命令上。

理解 < 之后,我们再来理解一下 << ,它的意思是【结束的输入字符】,即设置一个敏感字符,一旦输入它并回车,本次输入就结束

看到了吧,利用<<右侧的控制字符,我们可以终止一次输入,而不必按下Ctrl+d来结束,这对程序写作很有帮助。

 

命令执行的判断根据:;、&&、||

在某些情况下,很多命令我想要一次输入去执行,而不想要分次执行时,有两个选择,一个是利用shell脚本去执行,另一个就是通过下面介绍的东西:

cmd;cmd(不考虑命令相关性的连续命令执行)

示例,关机前同步数据:

sync;sync;shutdown -h now

这些命令具有相关性,前一个命令是否成功执行与后一个命令是否要执行有关。

$?(命令返回值)与&&或||

如同上面谈到,两个命令具有依赖性,而这个依赖性主要判断的地方就在于前一个命令执行的结果是否正确,其实也就是判断命令返回值$?为0(成功)还是为其他。

那么我们怎么通过这个返回值来判断后续的命令是否要执行?这就得要借由【&&】及【||】的帮忙了

命令执行情况
说明
cmd1 && cmd2 1. 若 cmd1 运行完毕且正确运行($?=0),则开始运行 cmd2。
2. 若 cmd1 运行完毕且为错误 ($?≠0),则 cmd2 不运行。
cmd1 || cmd2 1. 若 cmd1 运行完毕且正确运行($?=0),则 cmd2 不运行。
2. 若 cmd1 运行完毕且为错误 ($?≠0),则开始运行 cmd2。

我们来看个例子,我不知道/tmp/abc;是否存在,但我就是要建立/tmp/abc/hehe 文件

,可以使用如下命令:

ls /tmp/abc || mkdir /tmp/abc && touch  /tmp/abc/hehe

又或者,判断一个文件是否存在(存在输出:exist,不存在输出:not exist)

ls /tmp/luoluoing && echo "exist"  ||  echo "not exist"

时刻记住,Linux是从左向右执行命令

 

管道命令(pipe)

管道命令使用的是【|】这个界定符号。另外,管道命令与连续执行命令是不一样的,这点下面我们在说明。下面我们先举一个例子来说明管道命令。

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

ls -al /etc | less

注意,管道命令【|】仅能处理经由前一个命令传来的正确信息,也就是标准输出(STDOUT)信息,对于标准错误信息没有直接处理的能力。

在每个管道后面接的第一个数据必定是【命令】,而且这个命令必须要能够接受标准输入的数据才行,这样的命令才可为管道命令。也就是说,管道命令要注意两个点:

  • 管道命令仅会处理标准输出,对于标准错误会予以忽略。
  • 管道命令必须能够接受来自前一个命令的数据成为标准输入继续处理才行。

选取命令(cut、grep):

cut:

cut -d‘分隔字符’ -f fields    <==用于有特定分隔字符
cut -c 字符区间              <==用于排列整齐的信息
-d:后面接分隔字符,与-f一起使用
-f:根据-d的分隔字符将一段信息划分成为数段,用-f取出第几段的意思
-c:以字符(characters)的单位取出固定字符区间

第一种用法示例:

第二种用法示例:

cut主要的用途在于将同一行里面的数据进行分解,最常使用在分析一些数据或文字数据的时候。不过,cut在处理多空格相连的时候,可能会吃力一点,后面我们会介绍awk。

grep:

刚刚的cut是将一行的信息中,取出我们想要的,而grep则是一行信息,若有我们想要的,就拿出来。

grep [-acinv] [--color=auto] '查找字符' filename
-a:将二进制文件以文本文件的方式查找数据
-c:计算找到 ‘查找字符’ 的次数
-i:忽略大小写差异,即大小写不敏感
-n:顺便输出行号
-v:反向选择,亦即显示出没有“查找字符”内容的那一行
--color=auto:可以将找出的关键字部分加上颜色的内容

grep是个很棒的命令!

排序命令(sort)(wc)(uniq):

sort:

sort是个有趣的命令,他可以帮助我们进行排序,而且可以根据不同的数据形式来排序,例如数字与文字的排序就不一样。此外,排序的字符与语系的编码有关,因此,如果你要需要排序时,建议使用 LANG=C 来让语系统一。

sort [-fbMnrtuk] [file or stdin]
-f:忽略大小写的差异,例如A与a视为编码相同
-b:忽略最前面的空字符部分
-M:以月份的名字来排序,例如JAN、DEC等的排序方法
-n:使用【纯数字】进行排序(默认以汉字形式来排序的)
-r:反向排序
-t:分隔符号,默认用Tab分隔
-u:就是uniq,各行不出现重复数据
-k:以哪个区间(field)来进行排序的意思

uniq:

uniq [-ic]
-i:忽略大小写字符的不同
-c:进行计数

wc:

如果我想知道 /etc/man_db.conf 这个文件有多少字?多少行?可以用 wc 这个命令

wc [-lwm]
-l:仅输出行
-w:仅列出多少字(英文)
-m:多少字符

 

双向重定向:tee

想个简单的东西,我们由前一节知道>会将数据流整个传送给文件或设备,因此我们除非去读取某文件或设备,否则就无法继续利用这个数据流。万一我想要将这个数据流的处理过程中将某段信息存下来,应该怎么做?利用tee就可以,我们可以这样简单看一下:

tee会同时将数据流分送到文件与屏幕(screen),而输出到屏幕的,其实就是stdout,那就可以让下个命令继续处理:

 tee [-a] file
-a:以累加(append)的方式,将数据加入file当中

tee可以让standard output 转存一份到文件内并将同样的数据继续送到屏幕中去处理,这样除了可以让我们同时分析一份数据并记录下来之外,还可以作为处理一份数据的中间缓存记录之用。

 

字符转换命令:tr、col、join、paste、expand

我们知道DOS与UNIX换行符不同,并且可以使用dos2unix与unix2dos来完成转换。下面我们来介绍几个类似这两的起到替换作用的命令在管道中的用法。

tr

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

tr [-ds] SET1 ...
-d:删除信息当中SET1 这个字符
-s:替换掉重复的字符

col

col [-xb]
-x:将tab转换成对等的空格键

join

它是在处理两个文件的数据,而且主要是处理【两个文件当中,有相同数据的那一行,才将他们加在一起】

join [-ti12] file1 file2
-t:join默认以空格字符分隔数据,并且比对【第一个栏位的数据】的数据
    #若两个文件相同,则将两条数据连成一行,且第一个栏位放在第一个
-i:忽略大小写差异
-1:代表【第一个文件要用哪个栏位来分析】
-2:代表【第二个文件要用哪个栏位来分析】

特别需要注意的是,在使用join之前,最好对两个文件都事先排序(sort)处理,否则有些比对项目会被忽略

paste

直接将文件每行粘贴

paste [-d] file1 file2
-d:后面可以接分隔字符,默认是以Tab
-:如果file部分写成-,表示来自标准输入的数据

expand

将【tab】转换成空格

expand [-t] file
-t:后面可接数字,定义Tab被几个空格替换

使用cat -A可以查看文本里的制表符

另外还有 unexpand 来将空格转换成 tab

 

划分命令:split

split可以将一个大文件依据文件大小或行数进行划分,就可以将大文件划分为小文件了

split [-bl] file PREFIX
-b:后面可接欲划分成的文件大小,单位b、k、m等
-l:以行数来进行划分。
PREFIX:前缀字符的意思,可作为划分文件的前缀文字

文件名可随意取,我们只要写上前缀文字,小文件就会默认加上aa、ab、ac等

分割完了怎么复原呢?利用重定向即可:

cat services* >> servicesback

我们再看一个按行划分的:

参数代换:xargs

xargs是在做什么呢?字面上看,x是加减乘除的乘号,args则是argument(参数)的意思,所以说,这个玩意儿就是在产生某个命令的参数的意思。xargs可以读入stdin的数据,并且以空格符或换行符作为标识符,将stdin的数据分隔成为参数。因为是以空格符作为分隔,所以,如果有一些文件名或其他意义的名词内含有空格符的时候,xargs可能就会误判了,它的用法其实也是蛮简单的,就来看一看:

xargs [-0epn] command
-0:如果输入的stdin含有特殊字符,例如·、\、空格等,这个-0参数
     #可以把它还原成一般字符,这个参数可以用于特殊状态
-e:这个EOF(End Of File)的意思,后面可以接一个字符,当xargs
     #分析到这个字符时,就会停止工作
-p:在执行每个命令时,都会询问使用者的意思
-n:后面接次数,每次command命令执行时,要使用几个参数的意思
当xargs后面没有接任何命令时,默认是以echo来进行输出

xargs是个很好的命令,因为很多命令其实并不支持管道命令,因此我们可以通过xargs来提供该命令使用标准输入。

两个方法都可以参考

 

关于减号【-】的用途

减号可以替代 stdin 或 stdout

示例:

tar -cvf - /home | tar -xvf - -C /tmp/homeback
我将/home里面的文件打包,但打包的数据不是记录到文件,而是传送给stdout,
经过管道后,将tar -cvf - /home 传送给后面的 tar -xvf -

 

 


商业转载 请联系作者获得授权,非商业转载 请标明出处,谢谢

 

发表评论