BASH

系统内核:
让运算器、控制器、存储器、输入/输出设备等各种硬件设备各司其职且又能协同运行。对硬件资源的分配、调度等管理任务。


shell:
由于系统内核对计算机的正常运行来讲是太重要
了,不建议直接去编辑内核中的参数,而是让用户通过基于系统调用接口开发出的程序或服务来管理计算机,以满足日常工作的需要


shell的作用:

  1. 交互式
    解释执行用户的命令,用户输入一条命令,shell就解释执行一条
  1. 批处理
    事先写一个shell脚本,其中有很多流程控制语句,但shell脚本是解释执行的,不需要编译,shell程序从脚本中一行一行读取并执行这些命令,相当于一个用户把脚本的命令一行一行敲到shell提示符下执行。

shell版本:

  1. ch(Bourne Shell): 由steve Bourne开发,各种UNIX系统都有sh。

  2. csh(C shell): 由Bill Joy开发,随BSD UNIX发布,它的流程控制语句很像C语言,支持很多Bour Shell所不支持的功能:作业控制,命令历史,命令行编辑。

  3. ksh(Korn Shell): 由David Korn开发,向后兼容sh的功能,并且添加了csh引入的新功能,是目前很多UNIX系统标准配置的Shell,在这些系统上/bin/sh往往是指向/bin/ksh的符号链接。

  1. tcsh(TENEX C Shell): 是csh的增加版本,引入了命令䃼全等功能,在FreeBSD、MacOS X等系统上替代了csh。

  2. bash(Bourne Again Shell): 由GUN开发的Shell,主要目标是与POSIX标准保持一致,同时兼顾对sh的兼容,bash从csh和ksh借鉴了很多功能,是各种Linux发行版标准配置的Shell,在Linux系统上/bin/sh往往是指向/bin/bash的符号链接。虽然如此,bash和sh还是有很多不同的,一方面,bash扩展了一些命令和参数,另一方面,bash并不完全和sh兼容,有些行为并不一致,所以bash需要模拟sh的行为:当我们通过sh这个程序名启动bash时,bash可以假装自己是sh,不认扩展的命令,并且行为与sh保持一致。

查询指令是否为Bash shell的内置命令

[root@VM_0_13_centos ~]# type ls
ls is aliased to `ls --color=auto' 

[root@VM_0_13_centos ~]# type -t ls
alias


[root@VM_0_13_centos ~]# type -p man
/usr/bin/man

[root@VM_0_13_centos ~]# type -a ls
ls is aliased to `ls --color=auto'
ls is /usr/bin/ls

选项与参数:
    :不加任何选项与参数时,type会显示出是外部指令还是bash内置指令
-t  :file表示外部指令;alias表示该指令为命令别名所设置的名称;builtin表示该指令为bash内置的指令功能;
-p  :如果为外部指令,会显示完整文件名
-a  :会由PATH变量定义的路径中,将所有含该指令的都列出来,包含alias

通过type指我们可以知道每个指令是否为bash的内置指令。此外,由于利用type搜寻后面的名称时,如果后面接的名称并不能以可执行文件的状态被找到,那么该名称是不会被显示出来的。



Shell的变量功能

影响bash环境操作的变量
某些特定的变量会影响到bash的环境,例如PATH变量!能不能在任何目录下执行某个指令,与PATH这个变量有很多的关系。

如下达ls这个指令时,系统就是通过PATH这个变量里面的内容所记录的路径顺序来搜寻的!如果搜寻完PATH变量的路径还不到ls这个指令时,就会在屏幕上显示“command not found”的错误信息。

变量的取用

[root@VM_0_13_centos /]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

变量的取胜就如同上面的范例,利用echo就能够读出,只需要在变量名称前面加上$,或者是以${变量名}的方式来取用都可以!

变量的设置规则

  • 变量与变量内容以一个等号=来链接如myname=VBird
  • 等号两边不能接空白字符
  • 变量名称只能是英语字母与数字,但是开头字符不能是数字
  • 变量内容若有空白字符可使用双引号或单引号将内是内容结合起来,但双引号内特殊字符,可以保有原本的特性,单引号内的特殊字符则仅为一般字符(纯文本)
  • 可用跳脱字符\将特殊符号变成一般字符
  • 在一串指令的执行中,还需要借由其他额外的指令所提供的信息时,可以使用反单引号dfd$(指令)`。
  • 若该变量为扩增变量内容时,则可以用$变量名称${变量}累加内容,如:
PATH="$PATH":/home/bin
或
PATH=${PATH}:/home/bin
  • 通常大写字符为系统默认变量,自行设置变量可以使用小写字符

取消变量

  • 取消变量的方法为使用unset unset 变量名称

子程序

什么是子程序,在目前这个shell的情况下,去启用另一个新的shell,新的那个shell就是子程序啦!

在一般的状态下,父程序的自订变量是无法在子程序 内使用的。但是通过export将变量变成环境变量后,就能够在子程序下面应用了!

环境变量的功能

环境变量可以帮我们达到很多功能

  • 主文件夹的变换
  • 提示字符显示
  • 可执行文件搜寻的路径

查阅环境变量

  1. env观察环境变量
[root@VM_0_13_centos ~]# env
XDG_SESSION_ID=4517
HOSTNAME=VM_0_13_centos 这部主机的主机名称
TERM=xterm 这个终端机使用的环境是什么类型
SHELL=/bin/bash 目前这个环境下,使用的 Shell 是哪一个程序?
HISTSIZE=3000 “记录指令的笔数”在 CentOS 默认可记录 1000 笔
SSH_CLIENT=113.111.146.32 8057 22
SSH_TTY=/dev/pts/0
USER=root
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:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=01;36:*.au=01;36:*.flac=01;36:*.mid=01;36:*.midi=01;36:*.mka=01;36:*.mp3=01;36:*.mpc=01;36:*.ogg=01;36:*.ra=01;36:*.wav=01;36:*.axa=01;36:*.oga=01;36:*.spx=01;36:*.xspf=01;36:
MAIL=/var/spool/mail/root 这个使用者所取用的 mailbox 位置
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin
PWD=/root 目前使用者所在的工作目录
LANG=en_US.utf8
SHLVL=1
HOME=/root 这个使用者的主文件夹啊!
LOGNAME=root 使用者帐号
SSH_CONNECTION=113.111.146.32 8057 172.27.0.13 22
LESSOPEN=||/usr/bin/lesspipe.sh %s
PROMPT_COMMAND=history -a; printf "\033]0;%s@%s:%s\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/~}"
XDG_RUNTIME_DIR=/run/user/0
HISTTIMEFORMAT=%F %T 
_=/usr/bin/env

env是environment(环境)的简写,上面的例子当中,是列出来所有环境变量。

  1. set用观察所有变量(含环境变量与自订变量)
    bash可不只有环境变量,还有一些与bash操作接口有关的变量,以及使用者自己定义的变量存在,那么这些变量如何观察呢?这个时候就得要使用set这个指令了。set除了环境变量之外,还会将其他的bash内的变量通通显示出来。
[root@VM_0_13_centos var]# set

一般来说,不论是否为环境变量,只要跟我们目前这个shell的操作接口有关的变量,通常都会被设置为大写字符,也就是说,“基本上,在linux默认的情况中,使用大写字符来设置的变量一般为系统 内定需要的变量”

  1. export:自订变量转成环境变量
    环境变量与自订变量之间的差异,其它这两者的差异在于“该变量是否会被子程序所继续引用”

当你登陆一个称Linux并取得一个bash之后,你的bash就是一个独立的程序,这个程序的识别使用的是一个称为程序识别码,被称为PID,接下来你在这个bash下面所下达的任何指令者由这个bash所衍生出来的,那些被下达的指令就是被称为子程序了。

执行另一个bash,操作的环境接口会跑到第二个bash(子程序),那原本的bash就会sleep,若要回到原本的bash去,就只有将第二个bash结束掉(exit或logout)才行。

子程序仅会继承父程序的环境变量,子程序不会继承父程序的自订变量,所以你在原本bash自订变量在进入了子程序后就会消失不见,一直到离开子程序并加到父程序,这个变量才会出现 !

自订变量转换成环境变量:

[root@VM_0_13_centos ~ 19:25 #10]#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 之间,所以,你只要 echoRANDOM 时,系统就会主动的随机取出
    一个介于 0~32767 的数值。万一我想要使用 0~9 之间的数值呢?呵呵~利用 declare 宣
    告数值类型, 然后这样做就可以了:
    [dmtsai@study ~]RANDOM*10/32768 ; echo $number
    8 <== 此时会随机取出 0~9 之间的数值喔!

  • 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 函数取得工作目录名称,所以仅会列出最后一个目录名。

  • # :下达的第几个指令。

  • 啰~

CentOS默认的PS1内容:[u\@\h\W]$

更改命令指示的样式

[root@VM_0_13_centos ~]# PS1='[\u@\h \w \A #\#]\$'
[root@VM_0_13_centos ~ 19:11 #10]#
#代表第10次下达的指令
  • 语系变量(locale)
    目前大多数的linux已经都是支持万国码了,也都支持大部分的国家语系。那么我们的linux到底支持了多少的语系呢?这可以由locale这个指令来查询!
[root@VM_0_13_centos ~ 19:25 #10]#locale -a
aa_DJ
aa_DJ.iso88591
aa_DJ.utf8
aa_ER
aa_ER@saaho
aa_ER.utf8
aa_ER.utf8@saaho
……

后面不加任何选项与参数

[root@VM_0_13_centos ~ 19:29 #11]#locale
LANG=en_US.utf8 主语言的环境
LC_CTYPE="en_US.utf8" 字符(文字)辨识的编码
LC_NUMERIC="en_US.utf8" 数字系统的显示讯息
LC_TIME="en_US.utf8" 时间系统的显示数据
LC_COLLATE="en_US.utf8" 字串的比较与排序等
LC_MONETARY="en_US.utf8" 币值格式的显示等
LC_MESSAGES="en_US.utf8" 讯息显示的内容,如功能表、错误讯息等
LC_PAPER="en_US.utf8" 
LC_NAME="en_US.utf8"
LC_ADDRESS="en_US.utf8"
LC_TELEPHONE="en_US.utf8"
LC_MEASUREMENT="en_US.utf8"
LC_IDENTIFICATION="en_US.utf8"
LC_ALL=

可以逐一设置每个与语系有关的变量数据,但事实上,如果其他的语系变量都未设置,且你有设置LANG或者LC_ALL时,则其他的语系变量就会被这两个变量所取代!
这也是为什么我们在Linux当中,通常说明仅设置LANG或LC_ALL这两个变量而已,因为他是最主要的设置变量!

那要觉得奇怪的是,为什么在linux主机的终端机接口(tty1~tty6)的环境下,如果设置“LANG=zh_TW.utf8”这个设置值生效后,使用man或者其他输出时,都会有一堆乱码,尤其是使用ls-l这个参数时?

因为在Linux主机的终端机接口环境下是无法显示像中文这么复杂的编码文字,所以就会产生乱码了。也就是,我们才会必须要在tty1~tty6的环境下,加装一些中文化接口的软件,才能够看到中文。不过如果使用windows远程连接确实是可以看到中文的。

变量的有效范围

如果在跑程序的时候,有父程序与子程序的不同程序关系时,则“变量”可否被引用与export后的变量,我们可以称他为“环境变量”!环境变量可以被子程序所引用,但是其他的自订变量内容就不会存在于子程序 中。

为什么环境变量的数据可以被程序所引用呢?这是因为内存配置的关系!

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

变量键盘读取、阵列与宣告:read,array,declare

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

  • read
    通过键盘获取赋值

要读取来自键盘输入的变量,就是用read这个指令了。这个指令最常被用在shell script的撰写当中,想要跟使用者谈?用这个指令就对了。关于script的写法,我们会在第十三章介绍,下面先来瞧一瞧read的相关语法!

[root@VM_0_13_centos /]# read -pt variable
选项与参数:
-p  :后面可以接提示字符
-t  :后面可以接等待的秒数

请使用者由键盘输入一个内容,将该内容变成名为atest的变量

[root@VM_0_13_centos /]# read variable
this is a test 此时光标会等待你输入
[root@VM_0_13_centos /]# echo $variable
this ia a test 刚刚输入的数据已经变成一个变量内容

提示使用者30秒内输入自己的大名,将该输入字串作 为名named 的变量内容

[root@VM_0_13_centos /]# read -p "Please keyin your name:" -t 30 nemad
Please keyin your name: yove
[root@VM_0_13_centos /]# echo ${named}
yove
  • delare/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

范例二:将 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=tesgting
-bash: sum: readonly variable <==老天爷~不能改这个变量了!

范例四:让 sum 变成非环境变量的自订变量吧!

[dmtsai@study ~]$ declare +x sum <== 将 - 变成 + 可以进行“取消”动作
[dmtsai@study ~]$ declare -p sum <== -p 可以单独列出变量的类型
declare -ir sum="450" <== 看吧!只剩下 i, r 的类型,不具有 x 啰!

阵列(array)变量类型
某些时候,我们必须使用阵列来宣告一些变量,这有什么好处啊?在一般人的使用上,果然是看不出来有什么好处的!不过,如果您曾经写过程序的话,那才会比较了解阵列的意义,阵列对数值程序的设计师来说,可是不能错过的学习的重点之一

var[index]=content

意思是说,我有一个阵列名称var,而这个阵列的内容为var[i]=小明,var[2]=大明,,var[3]=好明……,那个index就是一些数字啦,重点是中括号来设置的。目前我们bash提供的是一维阵列。老实说,如果您不写一些复杂的程序,那么这个阵列的地方,可以先略过,等到有需要再来学习即可!因为要制作出阵列,通常与循环或者其他判断式交互使用才有比较高的存在意义!

设置上面提到的var[1]~var[3]的变量

[root@VM_0_13_centos /]# var[1]="small min"
[root@VM_0_13_centos /]# var[2]="big min"
[root@VM_0_13_centos /]# var[3]="nice min"
[root@VM_0_13_centos /]# echo "${var[1]},${var[2]},${var[3]}"
small min,big min,nice min

阵列的变量类型比较有趣的地方在于“读取”,一般来说,建议直接以${变量}来记忆的原因!

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

限制使用者的某些系统资源,包括:

  • 可以打开的文件数量
  • 可以使用的CPU时间
  • 可以使用的内存总量
  • ……
[root@VM_0_13_centos /]# 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 的限制

变量内容的删除、取代与远的

变量除了可以直接设置来修改原本的内容之外,有没有办法通过简单的动作来将变量的内容进行微调呢?

  • 变量内容的删除与取代
范例一:先让小写的 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

范例三:我想要删除前面所有的目录,仅保留最后一个目录
[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/

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



命令别名与历史命令



命令别名设置:alias,unalias

命令别名可以增设默认的选项有一些惯用的指令上面,alias的定义规则与变量定义规则几乎相同

[dmtsai@study ~]$ alias 别名='指令 选项'


实例:

  1. 使用lm来简化ls -al|more
[dmtsai@study ~]$ alias lm='ls -al|more'
  1. 代替已有指令
[dmtsai@study ~]$ alias rm='rm -i'

3.查看已定义的所有另名,直接使用alias指令

[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'
  1. 取消已定义的别名,使用unalias 别名
[dmtsai@study ~]$ unalias lm



历史命令:history



查询与更新history

[dmtsai@study ~]$ history [n]
[dmtsai@study ~]$ history [-c]
[dmtsai@study ~]$ history [-raw] histfiles
选项与参数:
n   :数字,列出最近的n笔命令列表
-c  :清空目前shell中的所有history
-a  :将新增history加入histfiles,没有则默认写入 ~/.bash_history
-r  :将histfiles的内容读到目前这个shell的history
-w  :将目前的history更新到histfiles


实例


  1. 列出目前内存内的所有 history 记忆
[dmtsai@study ~]$ history
# 前面省略
1017 man bash
1018 ll
1019 history
1020 history
# 列出的信息当中,共分两栏,第一栏为该指令在这个 shell 当中的代码,
# 另一个则是指令本身的内容喔!至于会秀出几笔指令记录,则与 HISTSIZE 有关!


  1. 列出目前最近的3条history
[dmtsai@study ~]$ history 3
1019 history
1020 history
1021 history 3


  1. 立刻将目前的数据写入histfile
[dmtsai@study ~]$ history -w
# 在默认的情况下,会将历史纪录写入 ~/.bash_history 当中!


历史命令的读取与记录:

  • 登陆Linux主机,系统从~/.bash_history读取以前的hostry,HISTFILESIZE决定能记录几条history
[dmtsai@study ~]$ echo ${HISTSIZE}
1000
  • 下达过100次指令,拿出系统时系统 会将101~1100这1000格历史命令更新到~/.bash_history当中”。

  • history -w强制写入~/.bash_history,因为记录的笔数永远都是HISTFILESIZE那么多,旧的信息会被新的替代,仅保留最新的,所以也称为更新。



history执行

[dmtsai@study ~]$ !number
[dmtsai@study ~]$ !command
[dmtsai@study ~]$ !!

选项与参数:
!number  :执行第几笔指令
!command :由最近的指令向前搜寻并执行command开头的指令
!!       :就是执行上一个指令(相当于按↑按)


实例:


  1. 执行第66条history
[dmtsai@study ~]$ history
66 man rm
67 alias
68 man history
69 history
[dmtsai@study ~]$ !66 #执行第 66 笔指令


  1. 执行上一条hisoty
[dmtsai@study ~]$ !! 


  1. 执行最近以at开头的指令
[dmtsai@study ~]$ !al


history存在问题:

  • 同一帐号同时多次登陆的history写入问题
    所有的bash都有1000条history在内存中,因为登出才会更新记录文件,所以最后登出的bash覆盖其他的bash的history

  • 无法记录时间
    由于这1000条history命令是依序记录的,但并没有记录时间



Bash Shell的操作环境:

是否记得我们登陆主机的时候,屏幕上头会有一些说明文字,告知我们的Linux版本啊什么的,还有,登陆的时候我们还可以给予使用者一些信息或者欢迎文字,我们习惯的环境变量、命令别名等等的,是否可以登陆就主动的帮我设置好?这些都是需要注意的。另外,这些设置值又可以分为系统 整体设置值与各人喜欢 设置值 ,仅是一些文件放置的地点不同啦 !这我们后面也会来谈一谈的!



路径与指令搜寻顺序

系统里面其实有不少的相同名称的指令,其中包括alias与bash的内置命令


指令搜寻的顺序:

  1. 相对/绝对路径执行指令,如:/bin/ls./ls
  2. 由alias找到该指令执行
  3. 由bash内置的(builtin)指令执行
  4. 通过$PATH变量顺序搜寻到的第一个指令执行


实例


  1. 通过type -a 指令查询指令搜寻顺序
[dmtsai@study ~]$ alias echo='echo -n'
[dmtsai@study ~]$ type -a echo
echo is aliased to `echo -n'
echo is a shell builtin
echo is /usr/bin/echo



bash的进站与欢迎信息


bash进站信息/etc/issue

bash进站画面就是在终端机接口登陆的时候的几行提示字符串,这些字符串写在/etc/issue,还有/etc/issue.net,这个是提供远程登陆程序用的。

[dmtsai@study ~]$ cat /etc/issue
\S
Kernel \r on an \m


issue可以使用反斜线作为变量取用:

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


实例


  1. 在tty3的进站画面显示如下信息
CentOS Linux 7 (Core) (terminal: tty3)
Date: 2015-07-08 17:29:19 > Kernel 3.10.0-
229.el7.x86_64 on an x86_64
Welcome!
[dmtsai@study ~]$ vim /etc/issue
\S (terminal: \l)
Date: \d \t
Kernel \r on an \m
Welcome!



欢迎信息/etc/motd

想要让使用者登陆后取得的信息,可以将信息加入/etc/motd


实例


  1. 告诉用户,系统将会在某个固定时间进行维护(一定要root身份才能修改):
[root@study ~]# vim /etc/motd
Hello everyone,
Our server will be maintained at 2015/07/10 0:00 ~ 24:00.
Please don't login server at that time. ^_^

#所有帐号与root登陆主机后会显示这样的信息:
Last login: Wed Jul 8 23:22:25 2015 from 127.0.0.1
Hello everyone,
Our server will be maintained at 2015/07/10 0:00 ~ 24:00.
Please don't login server at that time. ^_^



bash的环境配置文件

一进入bash系统就取得一堆有用的变量,因为系统有一些环境配置文件的存在,让bash的启动时直接读取这些配置文件及使用者个人配置文件


login与non-login shel

  • login shell:取得bash时需要完整的登陆流程的,就称为login shell举例来说,你要由tty~tty6登陆,需要帐号与密码
  • non-login shell:取得bash接口的方法不需要重复登陆的举动,如:

    • 你以X windows登陆Linux后,再以X的图形化接口启动终端机,此时那个终端接口并没有需要再次的输入帐号与密码,那么bash的环境就称为non-login shell

    • 你在原本的bash环境下再次下达bash这个指令,同样的也没有输入帐号与密码,那第二个bash(子程序)也是non-login shell



login shell读取的配置文件

bash的login shell情况下所读取的整体环境配置文件其实只有/etc/profile,但是/etc/profile还会调用出其他的配置文件


login shell 的读取流程:

BASH_第1张图片
login shell 的配置文件读取流程


/etc/profile(login shell才会读)

这个配置文件可以利用使用者UID来决定很多重要的变量数据,这也是每个使用者登陆取得bash时一定会读取的配置文件,所以如果你想要帮所有使用者设置整体环境,那就是改这里,这个文件设置的变量主要有:

  • PATH:会依据UID决定PATH变量要不要含有sbin的系统指令目录;
  • MAIL:依据贴帐号设置好使用者的mailbox到/var/spool/mail帐号名
  • USER:根据使用者的帐号设置此一变量内容
  • HOSTNAME:历史命令记录笔数。CentOS7.x设置为1000;
  • umask:包括root默认为022而一般用户为002等


/etc/profile.d/*.sh
只要在/etc/profile.d/这个目录内且扩展名为.sh,使用者能够具有r的权限,那么该文件就会被/etc/profile调用出来。

在CentOS 7.x中,这个目录下面的文件规范了

  • bash操作接口的颜色
  • 语系
  • ls指令的命令别名、vi的命令别名、which的命令别名等等。
    如果你需要帮所有使用者设置一些共享的命令别名时,你可以在这个目录下面自行创建扩展名为.sh的文件,并将所需要的数据即可!


/usr/share/bash-completion/completions/*
记得我们上头谈过的tab的妙用吧,除了命令补齐、文件名补齐之外,还可以进行指令的选项/参数补齐功能!那就是从这个目录里面找到相应的指令来处理的!其实这个目录下面的内容是由/etc/profile.d/bash_completion.sh这个文件载入的



login shell个人偏好配置文件


~/.bash_profile(login shell 才会读)

bash读完/etc/profile并借此调用其他配置文件后,接下来则是读取使用者的个人配置文件。

在login shell的bash环境中,所读取的个人偏好配置文件其实主要:

  1. ~/.bash_profile
  2. ~/.bash_login
  3. ~/.profile

其实bash的login shell设置只会读取上面三个文件的其中一个,按照上面的顺序,前者不存在才读后者

[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

这个文件将~/.local/bin~/bin累加在PATH中了,所以用户的可执行文件可以放在~/bin下直接执行,而不用绝对/相对路径来执行了,同时这个文件还判断 了~/.bashrc是否存在,若存在则读入此文件



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

source能够在不登系统的情况下读入配置文件

[dmtsai@study ~]$ source 配置文件文件名


实例:


  1. 将主文件夹的~/.bashrc的设置读入目前的bash环境中
[dmtsai@study ~]$ source ~/.bashrc <==下面这两个指令是一样的!
[dmtsai@study ~]$ . ~/.bashrc

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


  1. 将变量设置为环境变量
source 变量文件



login shell读取的配置文件

取得non-login shell时,该bash配置文件仅会读取~/.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

这个文件有使用者的个人设置,同时调用/etc/bashrc,因为/etc/bashrc定义了下面的数据:

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



其他配置文件


/etc/man_db.conf

规定了执行man的时候,该去哪里查看数据的路径设置!

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


~/.bash_history

history就记录在这里,而这个文件能够记录几条,则与HISTFILESIZE变量有关,每次登陆bash后,bash会先读取这个文件,将所有的历史指令读入内存


~/.bash_logout

记录了登出系统后系统会做什么,默认的情况,bash只是帮我们清掉屏幕的讯息而已。不过,你也可以将一些备份或者是其他你认为重要的工作写在这个文件中 (例如清空暂存
盘)



终端机的环境设置:stty,set

终端机环境设置包括:
我们中以用倒退键来删除命令列上的字符,也可以使用ctrl+c来强制终止一个指令的运行,当输入错误时,就会有声音跑来警告。

登陆终端机的时候,会自动的取得一些终端机的输入环境的设置



stty

stty(setting tty终端机)

[dmtsai@study ~]$ stty [-a]

选项与参数:
-a :将目前所有的 stty 参数列出来;


实例

  1. 列出所有的按键与按键内容
[dmtsai@study ~]$ stty -a
speed 38400 baud; rows 20; columns 90; line = 0;
intr = ^C; 
quit = ^\; 
erase = ^?; 
kill = ^U; 
eof = ^D; 
……

^表示ctrl


几个重要的代表意义:

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



默认组合键

组合按键 执行结果
Ctrl + C 终止目前的命令
Ctrl + D 输入结束 (EOF),例如邮件结束的时候;
Ctrl + M 就是 Enter 啦!
Ctrl + S 暂停屏幕的输出
Ctrl + Q 恢复屏幕的输出
Ctrl + U 在提示字符下,将整列命令删除
Ctrl + Z “暂停”目前的命令
ctrl + p 向上翻阅历史记录
ctrl + n 向下翻阅历史记录
ctrl + h 向前删除
ctrl + d 向后删除
ctrl + u 向前删除字符串
ctrl + k 向后删除字符串
ctrl + b 向前移动光标
ctrl + f 向后移动光标
ctrl + a 移动光标到头部
ctrl + e 移动光标到尾部
shift+pgup 向上翻页
shift+pgdn 向下翻页



set

set可用来显示变量,和设置整个指令输出/输入的环境,如记寻历史命令、显示错误等

[dmtsai@study ~]$ set [-uvCHhmBx]

选项与参数:
-u  :默认不启用。若启用后,当使用未设置变量时,会显示错误讯息;
-v  :默认不启用。若启用后,在讯息被输出前,会先显示讯息的原始内容;
-x  :默认不启用。若启用后,在指令被执行前,会显示指令内容(前面有 ++ 符号)
-h  :默认启用。与历史命令有关;
-H  :默认启用。与历史命令有关;
-m  :默认启用。与工作管理有关;
-B  :默认启用。与刮号 [] 的作用有关;
-C  :默认不启用。若使用 > 等,则若文件存在时,该文件不会被覆盖。

实例


  1. 显示目前所有的set设置值
[dmtsai@study ~]$ echo $-
himBH
# 那个$-变量内容就是set的所有设置,bash默认是himBH


  1. 设置若使用未定义变量时,则显示错误讯息
[dmtsai@study ~]$ set -u
[dmtsai@study ~]$ echo $vbirding
-bash: vbirding: unbound variable
# 默认情况下,未设置/未宣告 的变量都会是“空的”,不过,若设置 -u 参数,
# 那么当使用未设置的变量时,就会有问题啦!很多的 shell 都默认启用 -u 参数。
# 若要取消这个参数,输入 set +u 即可!


  1. 执行前,显示该指令内容。
[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 '~'
# 看见否?要输出的指令都会先被打印到屏幕上喔!前面会多出 + 的符号!



万用字符情怀特殊符号

万用字符(wildcard)使用bash处理数据更方便


常用的万用字符

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


实例


  1. 利用万用字符配合 ls 找文件名
#找出/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/[^a-z]*

#将上面找到的文件复制到/tmp/upper中
[dmtsai@study ~]$ mkdir /tmp/upper; cp -a /etc/[^a-z]* /tmp/upper



数据流重导向

数据流重导向(redirect)由字面上的意思来看,好像就是将“数据给他传导到其他地方去”的样?

没错~数据流重导向就是将某个指令执行后应该要出现在屏幕上的数据,给他传输到其他的地方,例如文件或者是设备(例 如打印机之类的)!这玩意儿在Linux的文字模式下面可重要的!尤其是如果我们想要将某些数据储存下来时,就更有用了!



什么是数据流重导向


什么流重导向,是指执行一个指令的时候,这个指令可能会由文件读入数据,经过处理之后,再将数据输出到指令的位置,流程是这样的:

BASH_第2张图片
指令执行


输出方式:

  • 标准输出(STDOUT),指令执行回传的正确的信息,默认输出到屏幕
  • 标准错误输出(STDERR),指令执行失败后回传的错误信息,默认输出到屏幕


为什么要使用数据流重导向:

  • 屏幕输出的信息很重要,而且我们需要将他存下来的时候;
  • 背景执行中的程序,不希望他干扰屏幕正常的输出结果时;
  • 一些系统的例行命令 (例如写在 /etc/crontab 中的文件) 的执行结果,希望他可以存下
    来时;
  • 一些执行命令的可能已知错误讯息时,想以“ 2> /dev/null ”将他丢掉时;
  • 错误讯息与正确讯息需要分别输出时。


数据流重导向作用:

可以将stdout与stderr分别传送至其他文件或设备上,传送的特殊字符:

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


输出规则:

  • 1> :以覆盖的方法将“正确的数据”输出到指定的文件或设备上;
  • 1>>:以累加的方法将“正确的数据”输出到指定的文件或设备上;
  • 2> :以覆盖的方法将“错误的数据”输出到指定的文件或设备上;
  • 2>>:以累加的方法将“错误的数据”输出到指定的文件或设备上;


实例


  1. 将数据流输出到文件
[dmtsai@study ~]$ ll / > ~/rootfile 
#屏幕并无任何信息
[dmtsai@study ~]$ ll ~/rootfile 
#有个新文件被创建了!
-rw-rw-r--. 1 dmtsai dmtsai 1078 Jul 9 18:51 /home/dmtsai/rootfile


  1. 将stdout与stderr分存到不同的文件
[dmtsai@study ~]$ find /home -name .bashrc > list_right 2> list_error


  1. 将错误的数据丢弃,屏幕上显示正确的数据
[dmtsai@study ~]$ find /home -name .bashrc 2> /dev/null
/home/dmtsai/.bashrc #只有stdout会显示到屏幕上,stderr 被丢弃


  1. 将指令的数据全部写入名为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 #正确

写入同一个文件的特殊语法可以使用2>&1&>


standard input:<<<


<是将原本需要由键盘输入的数据,改由文件内容来取代

  1. 用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


<<代表结束的输入字符

  1. 用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 <==只有这两行,不会存在关键字那一行!


命令执行的判断依据

有些时候,需要一次输入多条指令去执行,有两个选择:

  1. 编写shell script脚本
  2. 依据命令执行的判断一次输入多指令

&&||实现原理

两个指令之间有相依性,而这个相依性主要判断的地方就在于前一个指令执行的结果是否正确,前一个指令执行正确Linux会回传一个$?=0

指令是一个接着一个去执行的,因此&&||的顺序就不能搞错。

指令依序执行的关系示意图:

BASH_第3张图片
指令依序执行的关系示意图


指令下达情况 说明
; 分号前的指令执行完后就立刻执行后面的指令
cmd1 && cmd2 1. 若cmd1执行完毕且正确执行(?≠0),则cmd2不执行
`cmd1 cmd2` 1. 若cmd1执行完毕且正确执行(?≠0),则开始执行cmd2


实例:


  1. 同步两后,再关机
[root@study ~]# sync; sync; shutdown -h now


  1. 使用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

[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


  1. 测试/tmp/abc是否存在,若不存在则予以创建,若存在就不作任何事情
[dmtsai@study ~]$ ls /tmp/abc || mkdir /tmp/abc
ls: cannot access /tmp/abc: No such file or directory 
[dmtsai@study ~]$ ll -d /tmp/abc
drwxrwxr-x. 2 dmtsai dmtsai 6 Jul 9 19:17 /tmp/abc



管线命令(pipe)

管线命令|仅能处理经由前面一个指令传来的正确信息,也就是stdandard output的信息,他的流程像这样

BASH_第4张图片
管线命令的处理示意图

管线命令的使用:
在每个管线后面接的第一个数据必定是指令,而且这个指令必要能够接受standard input的数据才行,如:less,more,head,tail等,至于如:ls,cp,mv等就不是管线命令了

  • 管线命令仅会重standard output,对于standard error output会忽略
  • 管线 命令必须要能够接受来自前一个指令的数据成为standard input继续处理才行


实例


  1. 分面查看目录
[dmtsai@study ~]$ ls -al /etc | less



撷取命令:cut,grep



cut


这个指令可以将一段信息的某一段切出来,处理信息是以行为单位,最常使用在分析一些数据或文字数据的时候!这是因为有时候我们会以某些字符当作分区的参数,然后来将数据加以切割 ,以取得我们所需要的数据

[dmtsai@study ~]$ cut -d'分隔字符' -f fields #用于有特定分隔字符
[dmtsai@study ~]$ cut -c 字符区间 #用于排列整齐的讯息

选项与参数:
-d  :后面接分隔字符。与-f一起使用;
-f  :依据-d的分隔字符将一段信息分区成为数段,-f取出第几段
-c  :以字符(characters)的单位取出固定字符区间;


实例


  1. 取出PATH变量的第五个路径。
[dmtsai@study ~]$ echo ${PATH}
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
[dmtsai@study ~]$ echo ${PATH} | cut -d ':' -f 5
/home/dmtsai/.local/bin
#那么如果想要列出第 3 与第 5 呢?,就是这样:
[dmtsai@study ~]$ echo ${PATH} | cut -d ':' -f 3,5


  1. 取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"
……


  1. 用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
root
……



grep

grep是分析一行信息,若当中有需要的信息,就将该行拿出来

[dmtsai@study ~]$ grep [-acinv] [--color=auto] '搜寻字串' filename
选项与参数:
-a :将binary文件以text文件的方式搜寻数据
-c :计算找到'搜寻字串'的次数
-i :不区分大小写
-n :同时输出行号
-v :反向选择,即显示出没有'搜寻字串'内容的那一行
--color=auto :将找到的关键字部分加上颜色的显示


实例


  1. 将last当中,有出现root的那一行就取出来;
[dmtsai@study ~]$ last | grep 'root'


  1. 只要没有 root 的就取出!
[dmtsai@study ~]$ last | grep -v 'root'


  1. 在last的输出讯息中,只要有root就取出,并且仅取第一栏
[dmtsai@study ~]$ last | grep 'root' | cut -d ' ' -f 1


  1. 取出/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

排序命令:sort,wc,uniq

很多时候,我们都会去计算一次数据里头的相同型态的数据总数,

如:使用last可查得系统 上面有登陆主机者的身份,那么我以可以针对 每个使用者查出来他们的总登陆次数吗?

此时就得要排序与计算之类的指令来辅助了!下面我们介绍几个好用的排序 与统计指令

sort

sort可以帮我们进行排序 ,而且可以依据不同的数据型态来排序!

如:数字与文字的排序就不一样。此外,排序 的字符与语系的编码有关,因此,如果您需要排序 时,建议使用LANG=C来让语系统一,数据排序 比较好一些。

[dmtsai@study ~]$ sort [-fbMnrtuk] [file or stdin]

选项与参数:
-f :不区分大小写
-b :忽略最前面的空白字符部分
-M :以月份的名字来排序,例如JAN,DEC等的排序方法
-n :使用“纯数字”进行排序(默认是以文字体态来排序的)
-r :反向排序;
-u :就是uniq,相同的数据中,仅出现一行代表
-t :分隔符号,默认是用 [tab] 键来分隔;
-k :以那个区间(field)进行排序
  1. 个人帐号都记录在/etc/passwd,将帐号进行排序。
[dmtsai@study ~]$ cat /etc/passwd | sort
abrt:x:173:173::/etc/abrt:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
  1. 以/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

uniq

将排序后重复的数据仅列出一个显示

[dmtsai@study ~]$ uniq [-ic]

选项与参数:
-i :不区分大小写
-c :进行计数
  1. 使用last将帐号列出,仅取出帐号栏,进行排序后仅取出一位
[dmtsai@study ~]$ last | cut -d ' ' -f 1 | sort | uniq
  1. 统计每个人的登陆总次数
[dmtsai@study ~]$ last | cut -d ' ' -f 1 | sort | uniq -c
1
6 (unknown
47 dmtsai
4 reboot
7 root
1 wtmp

wc

他可以帮我们计算输出信息的整体数据,如有多少行?多少字,多少个字符?

[dmtsai@study ~]$ wc [-lwm]
选项与参数:
-l :仅列出行;
-w :仅列出多少字(英文单字);
-m :多少字符;
  1. 那个/etc/man_db.conf里面到底有多少相关字、行、字符数
[dmtsai@study ~]$ cat /etc/man_db.conf | wc
131 723 5171
# 输出的三个数字中,分别代表: “行、字数、字符数”
  1. last最后两行并非帐号内容,所以取得登陆系统的总数时要除去空白行,wtmp,unknown,reboot无关帐号登陆的信息
[dmtsai@study ~]$ last | grep [a-zA-Z] | grep -v 'wtmp' | grep -v 'reboot' | grep -v 'unknown' |wc -l



双向重导向:tee

想个简单的东西,我们由前一节知道>会将数据流整个传送给文件或设备,tee会同时将数据流分送到文件去与屏幕(screen);而输出 到屏幕的,其实就是stdout那就可以让下个指令继续 处理

[dmtsai@study ~]$ tee [-a] file

选项与参数:
-a :以累加(append)的方式,将数据加入file
  1. 将 last 的输出存一份到 last.list 文件中;
[dmtsai@study ~]$ last | tee last.list | cut -d " " -f 1
  1. 将 ls 的数据存一份到~/homefile,同时屏幕也有输出讯息!
[dmtsai@study ~]$ ls -l /home | tee ~/homefile | more



字符转换命令:tr,col,joinpaste,expand

tr

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

[dmtsai@study ~]$ tr [-ds] SET1

选项与参数:
-d :删除讯息当中的 SET1 这个字串;
-s :取代掉重复的字符!
  1. 将 last 输出的讯息中,所有的小写变成大写字符
[dmtsai@study ~]$ last | tr '[a-z]' '[A-Z]'
# 没有加上单引号也是可以执行的,如:“ last | tr [a-z] [A-Z] ”
  1. 将 /etc/passwd 输出的讯息中,将冒号 (:) 删除
[dmtsai@study ~]$ cat /etc/passwd | tr -d ':'

col

[dmtsai@study ~]$ col [-xb]
选项与参数:
-x :将 tab 键转换成对等的空白键
  1. 利用 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] 按键会被取代成为空白键,输出就美观多了!

虽然 col有特殊的用途,不过很多时候,他可以用来简单的处理将[tab]按键取代成为空白键,例如上面的例子当中,如果使用cat -A[tab]会以^|来表示 。但经过col -x的处理,则会将[tab]取代成为对等的空白键!

joIn

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 在处理两个相关的数据文件时,就真的是很有帮助的啦! 例如上面的案例当中,我
的 /etc/passwd, /etc/shadow, /etc/group 都是有相关性的, 其中 /etc/passwd, /etc/shadow 以
帐号为相关性,至于 /etc/passwd, /etc/group 则以所谓的 GID (帐号的数字定义) 来作为他
的相关性。根据这个相关性, 我们可以将有关系的数据放置在一起!这在处理数据可是相当
有帮助的! 但是上面的例子有点难,希望您可以静下心好好的看一看原因喔!
此外,需要特别注意的是,在使用 join 之前,你所需要处理的文件应该要事先经过排序
(sort) 处理! 否则有些比对的项目会被略过呢!特别注意了!

paste

这个paste就要比join简单多了!相对于Join必须要比地两个文件的数据相关性,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 的话,
# 情况就又不同了!这里也不好理解~您可以多设置几个数字来查阅就晓得!

expand 也是挺好玩的~他会自动将 [tab] 转成空白键~所以,以上面的例子来说, 使用 cat -
A 就会查不到 ^I 的字符啰~此外,因为 [tab] 最大的功能就是格式排列整齐! 我们转成空白
键后,这个空白键也会依据我们自己的定义来增加大小~ 所以,并不是一个 ^I 就会换成 8 个
空白喔!这个地方要特别注意的哩! 此外,您也可以参考一下 unexpand 这个将空白转成
[tab] 的指令功能啊! _

你可能感兴趣的:(BASH)