功能
历史记录
bash 可以记录你曾经使用过的指令。你使用过的指令都被存放在 .bash_history 中。.bash_history 记录的是前一次登入以前所执行过的指令, 而至于这一次登入所执行的指令都被暂存在内存中,当你成功的注销系统后,该指令记忆才会记录到 .bash_history 当中
$ history [n] # 展示最近 n 条
$ history -c # 清除所有历史
$ history -w # 手动写入 history
$ echo ${HISTSIZE} # HISTSIZE 决定存储的历史指令条数
1000
同一用户多开 bash 的问题
同时开启多个 shell, 因此所有的 bash 都有自己的 1000 笔记录在内存中。因为等到注销时才会更新记录文件,所以, 最后注销的那个 bash 才会是最后写入的数据。如此一来其他 bash 的指令操作被最后一个 bash 所覆盖更新了。
执行以前的命令
$ !66 <==执行第 66 笔指令
$ !! <==执行上一个指令,本例中亦即 !66
$ !ec <==执行最近以 ec 为开头的指令
内置关键字
builtin, !, %, ., :, @, {, }, alias, alloc, bg, bind, bindkey, break, breaksw, builtins, case, cd, chdir, command, complete, continue, default, dirs, do, done, echo, echotc, elif, else, end, endif, endsw, esac, eval, exec, exit, export, false, fc, fg, filetest, fi, for, foreach, getopts, glob, goto, hash, hashstat, history, hup, if, jobid, jobs, kill, limit, local, log, login, logout, ls-F, nice, nohup, notify, onintr, popd, printenv, pushd, pwd, read, readonly, rehash, repeat, return, sched, set, setenv, settc, setty, setvar, shift, source, stop, suspend, switch, telltc, test, then, time, times, trap, true, type, ulimit, umask, unalias, uncomplete, unhash, unlimit, unset, unsetenv, until, wait, where, which, while -- shell built-in commands
介绍一个很有用的指令 type,他可以判断各个指令是否为内置的指令,还可以找到指令的位置。
指令串分行
[dmtsai@study ~]$ cp /var/spool/mail/root /etc/crontab \
> /etc/fstab /root
只要在反斜杠后面紧跟回车即可触发指令串分行输入(中间不能输入别的东西)。
别名
设置别名和取消别名分别为 alias
, unalias
。如果你想查看所有的 alias,请直接输入 alias
变量
查看变量
使用 export 或者 echo $<变量名 > 都是可以的
$ export # 显示所有的环境变量
declare -x HISTSIZE="3000"
declare -x HISTTIMEFORMAT="%F %T "
declare -x HOME="/root"
declare -x HOSTNAME="VM_0_3_centos"
declare -x LANG="zh_CN.UTF-8"
.....
$ echo $MAIL # 读取邮箱地址
/var/spool/mail/root
如果你想要查看所有的环境变量,请使用 export
和 env
,如果你想查看所有的变量(包括自定义变量)请使用 set
。
设定规则
- 变量的赋值使用的是等号,而且等号两边不能有空格,所以说
a = b
是错误的写法,a=b
才对 - 变量名称只能是英文字母与数字,但是开头字符不能是数字
- 变量内容若有空格符应该用单引号或者双引号括起来。单引号和双引号在没有特殊符号的时候是没有区别的,但不过在有特殊符号的时候我们需要注意:
- 双引号内的特殊字符如 \$ 等,可以保有原本的特性:
"lang is $LANG"
="lang is zh_CN.UTF-8"
- 单引号内的特殊字符则仅为纯文本
- 双引号内的特殊字符如 \$ 等,可以保有原本的特性:
- 可用转义字符 \ 将特殊符号(如 [Enter], $, \, 空格符, '等)变成一般字符
- 需要引用其它指令的结果时候使用 `指令` 和 $(指令)
-
$ echo `uname -r`
-
3.10.0-957.21.3.el7.x86_64
-
$ echo uname -r
-
uname -r
-
$ echo $(uname -r)
-
3.10.0-957.21.3.el7.x86_64
-
$ echo (uname -r)
-
-bash: 未预期的符号 `uname' 附近有语法错误
-
- 一般来说系统默认变量写为大写,而用户设定的使用小写。
- 在变量后追加东西:
-
# 在 PATH 这个变量当中『累加』:/home/dmtsai/bin 这个目录
-
$ PATH=$PATH:/home/dmtsai/bin
-
$ PATH="$PATH":/home/dmtsai/bin
-
$ 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 <==以此例较佳!
-
- 设置环境变量 export,取消变量使用的是 unset。在一般的状态下,父shell 的自定义变量是无法在子shell 内使用的。但是透过 export 将变量变成环境变量后,就能够在子shell 底下应用了
- 数组类型:直接
var[index]=content
我们通常有些目录是经常使用的,但不过每次一层一层的 cd 实在是太麻烦了。下面是一个非常实用的简化目录的方法。
➜ frp_0.21.0_darwin_amd64 export frpc=$(pwd)
➜ frp_0.21.0_darwin_amd64 frpc
zsh: command not found: frpc
➜ frp_0.21.0_darwin_amd64 $frpc
➜ frp_0.21.0_darwin_amd64 echo $frpc
/Users/bonky/OneDrive/附件/运维/外网访问服务器/frp_0.21.0_darwin_amd64
下次进入直接 cd $frpc
即可~
几个比较重要的环境变量
PS1 命令提示字符
PS1 影响的就是下面这个东西:
检查 PS1 的值如下:
[root@VM_0_3_centos ~]# echo $PS1
[\u@\h \W]\$
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 时,提示字符为 # ,否则就是 $ 啰~
然后我们只要修改 PS1 就可以修改提示符(强行魔改):
[root@VM_0_3_centos ~]# export PS1="[(\T) \W] ➜ "
[(02:39:04) ~] ➜ cd /
$RANDOM
$RANDOM 这个变量,可以随机生成 0~32767之间的整数数字。
$ 和 ?
echo $$
的结果是自己当前 shell 的 PID,所以有种很骚的方法退出 shell:
[(02:42:41) ~] ➜ kill -9 $$
连接断开
echo $?
的结果是上一条指令是否成功执行,如果成功执行为0,否则为返回错误代码,可以用这个来进行指令执行错误的判断。
OSTYPE, HOSTTYPE, MACHTYPE
$ echo $OSTYPE
linux-gnu
$ echo $HOSTTYPE
x86_64
$ echo $MACHTYPE
x86_64-redhat-linux-gnu
多语言
语言变量主要包括两个 LANG 及 LC_ALL,如果我们需要临时修改,请使用下面的方式:
$ LANG=en_US.utf8
$ export LC_ALL=en_US.utf8
注意 LC_ALL 需要 export 才能生效。使用 locale 可以检查当前使用的语言。如果需要长期修改,只要把上面两句写到配置文件或者修改 /etc/locale.conf
即可。
几个有用的函数
read 读取变量
$ read [-pt] variable
选项与参数:
-p :后面可以接提示字符
-t :后面可以接等待的秒数
下面是一个示例:
$ read -p "yourname: " myname
yourname: zhuziquan
$ echo $myname
zhuziquan
declare/typeset 宣告变量类型
declare/typeset 的功能都是宣告变量的类型。(shell 默认全部为字符串)
$ declare [-aixr] variable
选项与参数:
-a :将后面名为 variable 的变量定义成为数组 (array) 类型
-i :将后面名为 variable 的变量定义成为整数数字 (integer) 类型
-x :用法与 export 一样,就是将后面的 variable 变成环境变量;
-r :将变量设定成为 readonly 类型,该变量不可被更改内容,也不能 unset(只能注销再登录才能还原)
下面是一个不定义变量类型导致计算结果为表达式的字符串的例子:
➜ ~ s=1+2
➜ ~ echo $s
1+2
我们可以把变量定义为数字:
➜ ~ declare -i a=1+2
➜ ~ echo $a
3
PS:shell 中只支持到取到整数,所以计算结果都为整数(没有小数部分)
ulimit 限制资源
操作系统中,有些资源是有限制的,通常每个用户所用的资源都有上限的,这个往往是由 ulimit 所配置的。
$ 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)数量。
利用 ulimit -a
可以看到当前系统所设置的限制:
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7267
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 100001
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) 7267
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
可以看出,每个用户所创建单一文件的大小,能够打开文件的个数都是有限制的(尤其是打开文件个数非常重要,因为在服务器端可能有同时访问多个文件的需求,如果超过限额则无法访问)
变量内容的修改
删除
$ echo ${path}
/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
# 删除后缀为 local/bin 的
$ echo ${path#/*local/bin:}
/usr/bin:/usr/local/sbin:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
看看这个 ${path#/*local/bin:}
,path
是原变量,#
代表从头开始删除(非贪婪匹配),后面是一个正则表达式 /*local/bin:
。如果像要贪婪匹配的话应该使用 ##
。
同样地,如果我们想要从末尾开始删除的话我们应该使用 %
或者 %%
,替换掉 #
或者 ##
即可。
取代
# 将 path 的变量内容内的 sbin 取代成大写 SBIN:
$ echo ${path/sbin/SBIN}
/usr/local/bin:/usr/bin:/usr/local/SBIN:/usr/sbin:/home/dmtsai/.local/bin:/home/dmtsai/bin
# 这个部分就容易理解的多了!关键词在于那两个斜线,两斜线中间的是旧字符串
# 后面的是新字符串,所以结果就会出现如上述的特殊字体部分啰!
$ echo ${path//sbin/SBIN}
/usr/local/bin:/usr/bin:/usr/local/SBIN:/usr/SBIN:/home/dmtsai/.local/bin:/home/dmtsai/bin
# 如果是两条斜线,那么就变成所有符合的内容都会被取代喔!
变量为空或空字符串的处理
范例一:测试一下是否存在 username 这个变量,若不存在则给予 username 内容为 root
$ echo ${username}
<== username 可能不存在,也可能是空字符串
$ username=${username-root}
$ echo ${username}
root <==因为 username 没有设定,所以主动给予名为 root 的内容。
$ username="vbird tsai" <==主动设定 username 的内容
$ username=${username-root}
$ echo ${username}
vbird tsai <==因为已经设定了,所以使用旧有的设定而不以 root 取代
所有的规则如下:
可以发现,关于 str 有两种表达 str
和 str:
分别表示是否考虑 str 为空的情况,str
表示不考虑 str 为空字符串的情况,而 str:
表示考虑为空字符串的情况。
然后满足以上条件的话有四种操作:-
,+
,=
和 ?
。
-
:代表设定为后面所接的字符串 exp,不满足设定为 str。+
:表示设定为空字符串,不满足则设定为 expr。=
:满足的话str,var全部设定为 expr。?
:满足的话输出到 stderr(即报错)。
机制
指令执行顺序
- 以相对/绝对路径执行指令,例如
/bin/ls
或./ls
- 由 alias 找到该指令来执行;
- 由 bash 内建的 (builtin) 指令来执行;
- 透过 $PATH 这个变量的顺序搜寻到的第一个指令来执行。
修改登录欢迎信息
直接修改文件 /etc/motd
中的内容即可。
同样如果想要修改欢迎界面(就是还没登录前那个 tty1~tty6),直接修改 /etc/issue
文件即可。由于我使用的是 ssh 远程登录服务器,无法看到。
$ cat /etc/issue
\S
Kernel \r on an \m
配置文件读取
login 与 non-login shell
- login shell:取得 bash 时需要完整的登入流程的,就称为 login shell。举例来说,你要由 tty1 ~ tty6 登入,需要输入用户的账号与密码,此时取得的 bash 就称为 login shell
- non-login shell:取得 bash 接口的方法不需要重复登入的举动,举例来说,
- 你以 X window 登入 Linux 后,再以 X 的图形化接口启动终端机,此时那个终端接口并没有需要再次的输入账号与密码,那个 bash 的环境就称为 non-login shell 了。
- 你在原本的 bash 环境下再次下达 bash 这个指令,同样的也没有输入账号密码, 那第二个 bash (子程序) 也是 non-login shell 。
login 与 non-login shell 读取的配置文件数据并不一致,login shell 一般会读 /etc/profile
和 ~/.bash_profile
,而 non-login shell 会读 ~/.bashrc
。
/etc/profile
/etc/profile 是多用户共享的整体的配置文件。这个 /etc/profile 主要会设置以下变量:
PATH:会依据 UID 决定 PATH 变量要不要含有 sbin 的系统指令目录;
MAIL:依据账号设定好使用者的 mailbox 到 /var/spool/mail/账号名;
USER:根据用户的账号设定此一变量内容;
HOSTNAME:依据主机的 hostname 指令决定此一变量内容;
HISTSIZE:历史命令记录笔数。CentOS 7.x 设定为 1000 ;
umask:包括 root 默认为 022 而一般用户为 002 等!(755和775)
然后他还会调用外部数据:
/etc/profile.d/*.sh
:主要是一些命令的别名什么的(可能还会运行一些程序),比如我得下面就有这些东西。
$ ls /etc/profile.d/*.sh
/etc/profile.d/256term.sh /etc/profile.d/colorgrep.sh /etc/profile.d/less.sh
/etc/profile.d/abrt-console-notification.sh /etc/profile.d/colorls.sh /etc/profile.d/vim.sh
/etc/profile.d/bash_completion.sh /etc/profile.d/lang.sh /etc/profile.d/which2.sh
/etc/locale.conf
:由/etc/profile.d/lang.sh
调入,负责多语言/usr/share/bash-completion/completions/*
:由/etc/profile.d/bash_completion.sh
调入, 负责文件指令的补全等等
~/.bash_profile
一般可能有三种针对与用户的配置文件,安装顺序读取(只需要一个):
~/.bash_profile
~/.bash_login
~/.profile
~/.bashrc
注意一点,由于 /etc/profile 与 ~/.bash_profile 都是在取得 login shell 的时候才会读取的配置文件,如果我们想要不注销读取配置文件,那么我们需要用到 source 指令或者点(.)指令:
$ source ~/.bashrc <==底下这两个指令是一样的!
$ . ~/.bashrc
其它配置文件
/etc/man_db.conf
:指明 man 的目录。~/.bash_logout
:记录了注销 bash 后系统要做的操作~/.bash_history
按键设定
$ stty # 设定按键
$ stty -a # 查看所有的按键
speed 38400 baud; rows 22; columns 114; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = ; start = ^Q;
stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
......
其中:
intr : 送出一个 interrupt (中断) 的讯号给目前正在 run 的程序 (就是终止啰!);
quit : 送出一个 quit 的讯号给目前正在 run 的程序;
kill : 删除在目前指令列上的所有文字;
eof : End of file 的意思,代表『结束输入』。
start : 在某个程序停止后,重新启动他的 output
stop : 停止目前屏幕的输出; (可以拿 find / 试下)
susp : 送出一个 terminal stop 的讯号给正在 run 的程序。
基本 bash 设定
使用 set 命令对 bash 进行设定:
[dmtsai@study ~]$ set [-uvCHhmBx]
选项与参数:
-u :预设不启用。若启用后,当使用未设定变量时,会显示错误讯息;
-v :预设不启用。若启用后,在讯息被输出前,会先显示讯息的原始内容;
-x :预设不启用。若启用后,在指令被执行前,会显示指令内容(前面有 ++ 符号)
-C :预设不启用。若使用 > 等,则若文件存在时,该文件不会被覆盖。
检查效果如下:
[root@VM_0_3_centos ~]# set -x
++ history -a
++ printf '\033]0;%s@%s:%s\007' root VM_0_3_centos '~'
[root@VM_0_3_centos ~]# ls
+ ls --color=auto
test.tar.xz
++ history -a
++ printf '\033]0;%s@%s:%s\007' root VM_0_3_centos '~'
执行逻辑
逻辑 | 作用 |
---|---|
cmd1 ; cmd2 | 连续执行两条指令 |
cmd1 && cmd2 | cmd1错误执行,那么 cmd2不会执行 |
cmd1 || cmd2 | cmd1正确执行,那么 cmd2不会执行 |
特殊符号
通配符:
数据流重定向
下图是指令的数据流:
stdout 和 stderr
>
:以覆盖的方法将『正确的数据』输出到指定的文件或装置上;>>
:以累加的方法将『正确的数据』输出到指定的文件或装置上;2>
:以覆盖的方法将『错误的数据』输出到指定的文件或装置上;2>>
:以累加的方法将『错误的数据』输出到指定的文件或装置上;
这里覆盖和累加应该这么理解:加入我重定向到一个文件,并执行过一次指令后,>
会覆盖前面执行的文件,>>
会在文件的尾部追加。
下面是一个简单的使用的例子:
$ find /home -name .bashrc > list_right 2> list_error
如果我们想忽略掉错误,请重定向到 /dev/null
$ find /home -name .bashrc 2> /dev/null
假如我们打算把错误和正确结果写到一个文件:
$ find /home -name .bashrc > list 2> list <==错误 $ find /home -name .bashrc > list 2>&1 <==正确 $ find /home -name .bashrc &> list <==正确
stdin
<
的作用是本需要由键盘输入的数据,改由文件内容来取代。
$ cat > catfile < ~/.bashrc
<<
的作用是指定结束输入的字符。
$ cat > catfile << "eof" > This is a test.
> OK now stop
> eof <==输入这关键词,立刻就结束而不需要输入 [ctrl]+d
使用思路
- 屏幕输出的信息很重要,而且我们需要将他存下来的时候
- 后台执行中的程序,不希望他干扰屏幕正常的输出结果时
- 不想要显示错误信息的时候
- 错误讯息与正确讯息需要分别输出时