Skip to content

shell

CLI, Command-line interface

  • 实体控制台(Physical Console):实体电脑所在处,就是实体控制台。
  • 虚拟控制台(Virtual Console):tty1~tty6,这六个可供登录的环境,都是实体控制台模拟出来的控制台环境,因此就被成为虚拟控制台。
  • 终端机(Terminal):虚拟控制台提供一个界面供应输入和输出,这个界面称为终端机。用户在终端机登录后获取壳程序与系统交互。一般终端机多指纯文本界面(CLI)。广义上,图形界面(GUI, Graphical User Interface)也视为一种终端机。

一些快捷键

  • [tab]:命令补齐,可补齐命令、文件名、变量名
  • [ctrl] + [c]:终止命令
  • [shift] + [page up], [shift] + [page down]:实体、虚拟控制台界面上下翻页翻页。

实体终端

预设提供六个 Terminal 给用户操作。可通过快捷键切换([ctrl] + [alt] + [f1-6]),切换后会进入对应的 tty1 ~ tty6 虚拟控制台。

  1. 用户登录后,系统会提供壳程序 shell(通常为,bash)到当前终端下 。
  2. shell 启动时会加载配置文件进行环境变量设置。
  3. 用户可以在 shell 下可以通过命令与系统交互。

shell 配置文件

依次根据顺序加载:

  1. 系统配置 /etc/profile (勿修改)
  2. 系统配置 /etc/profile.d/*.sh(按需添加)
  3. 用户配置 ~/.bashrc(按需添加)

登录获取 shell:加载所有配置(登录,sudo -isu -

无登录获取 shell:仅加载用户配置(调用 shell 软件进入子环境)

伪终端

PTS(Pseudo Terminal Slave)是伪终端从设备,与 PTY(Pseudo Terminal Master)伪终端主设备配对工作,模拟物理终端的功能。

plain
┌─────────────────────────────────────────┐
│         用户空间 (User Space)            │
├─────────────────────────────────────────┤
│  Terminal Emulator  │    Shell/进程      │
│       (PTY Master)  │   (PTY Slave)     │
└───────────┬─────────┴─────────┬─────────┘
            │                   │
            └───────────────────┘
            ┌───────────────────┐ 
│           │   内核空间         │         │
│           │  TTY Driver       │         │
└───────────┴───────────────────┴─────────┘

PTS 工作流程

  1. SSH客户端连接到SSH服务器
  2. SSH服务器端创建PTY主从设备对
  3. Master端:SSH服务器进程使用
  4. Slave端(PTS):分配给用户shell
  5. 用户在PTS上输入,通过SSH传输到Maste
sh
# 物理终端设备
/dev/tty1, /dev/tty2, /dev/tty3, ...

# 伪终端设备
/dev/pts/0, /dev/pts/1, /dev/pts/2, ...

# 查看当前终端
echo $(tty)

# 查看终端类型
echo $TERM

jobs control

终端界面下同时进行多个作业管理。

  • 前景(foreground):控制与下达命令的这个环境。
  • 背景(baskground):下达命令后程序在后台中执行,前景依旧可继续任何操作。

shell 下触发的程序皆为 shell 程序的子程序。背景中程序无法等待接收(terminal/shell)的输入。

sh
# 执行命令并转入背景当中。若有输出最好使用重定向指向日志文档,方便查询亦可避免写入前景。
$ <cmd> &

# [ctrl] + [z]: 将当前前景执行的程序转入背景当中并暂停。

# 打印当前 shell 执行所有程序
$ jobs -l

# 将第n个背景当中的工作转移到前景
$ fg %n

# 将第n个背景当中的工作由 Stoped 状态变为 Running
$ bg %n

shells

系统中有效的 shell 会写入到 /etc/shells 文件当中。完成安装新 shell 软件后会写入该文档。

  • csh:设置变量需要使用 set 命令 (set var='content'),bash不需要使用set但是不允许等号两边有空格。

nologin

/usr/sbin/nologin 不可交互的、无效的 shell。

守护进程(daemon)这类程序的账户需要严格限制(restricted account),以免被攻破后照成更大伤害。

非限制的用户(unrestricted account)可以使用/etc/shells 中记录的 shell。

预设/usr/sbin/nologin不再写入/etc/shells当中。(参考:https://access.redhat.com/errata/RHSA-2018:3249)

stdin, stdout, stderr

sh
# 网络连接,sshd 
# pts/0: /dev/pts/0
# pts/1: /dev/pts/1

# <: stdin,标准输入。代码为0
# <<: stdin,标准输入。代码为0
# 1>|>: stdin,标准输出,覆盖。代码为1
# 1>>|>>: stdin,标准输出,追加。代码为1
# 2>: stderr,标准错误,覆盖。代码为2
# 2>>: stderr,标准错误,追加。代码为2
# &>: 合并标准输出、错误输出

# 分离标准输出、标准错误到不同文件
$ cmd > cmd.log 2> cmd-err.log

# 合并输出,标准错误转到标准输出管路上,同步输出
$ cmd > cmd.log 2>&1
$ cmd 1> cmd.log 2>&1
# 合并输出,标准输出转到标准错误管路上,同步输出
$ cmd 2> cmd.log 1>&2
# 简写
$ cmd &> cmd.log

# 多指令重定向
$ (date; uptime; uname -r) > ./test.txt

# eof, end of file
# 覆盖写入
$ cat > test.txt << eof
test1
eof

# 追加写入
$ cat >> test.txt << eof
test1
eof

# |: 管道,将标准输出(stdout)转给关联指令的标准输入(stdin)
$ find /etc | grep '\.conf'

变量

赋值

sh
# 获取变量值赋值
$ var=${var1}
# 结果赋值
$ var=$(CMD); var=`CMD`;
# 基础算式结果赋值
$ var=$((1*2))
# 字符串,保留 $ 符号功能
$ var="str";
# 纯字符串
$ var='str';
  • 全局变量:子进程 shell 下,可以获取父进程声明的变量。
  • 局部变量:声明范围为父进程,子进程不能获取。
sh
# 变量声明
$ VAR=VAL
# 全局声明
$ export VAR=VAL
# 取消定义
$ unset VAR

shell 环境变量

变量功能
LANGLC_ALL语系设置
PATH命令文件搜寻路径,路径间使用":"分割。
HOME用户家目录
MAIL用户邮件池目录
HISTSIZE命令记录数,记录执行过的命令。
RANDOM获取随机数,范围 0~32767(2¹⁵-1)。
PS1Prompt String 1,命令行提示内容

指令嵌套

sh
# `CMD` 和 $(CMD) 包裹的内容会被当成指令
$ ls -l $(find /usr/bin -perm /6000)
$ ls -l `find /usr/bin -perm /6000`

自动补全

sh
$ apt install bash-completion
# centos7
$ yum install bash-completion
$ yum install bash-completion-extra

登录信息

sh
# show who is logged on
$ who
# who 简写指令 w
$ w

# show a listing of last logged in users
# /var/log/wtmp,根据其内容
$ last
# reports the most recent login of all users or of a given user
# /var/log/lastlog,根据其内容
$ lastlog
# dump UTMP and WTMP files in raw format
$ utmpdump /var/log/wtmp # login records
$ utmpdump /var/log/btmp

shell script

脚本执行需要 rx 权限,使用绝对路径、相对路径,或纳入 PATH 变量。

sh
$ bash script.sh
$ ./script.sh
$ source script.sh
$ . script.sh

# read a line from the standard input and split it into fields
$ read
# 从标准输入写入变量
$ read -p 'enter a num:' nun
$ echo $num

参数

sh
# /bin/bash
# test.sh
echo "parameter num: $#"
echo "pid: $$"

for param in $@ # 参数集合
do
        echo "\$@-param: $param"
done

for param in $* # 参数集合
do
        echo "\$*-param: $param"
done

echo "param num: $0"
echo "param1: $1"
echo "param2: $2"
echo "param3: $3"
# --- end ---

# 当前 bash 下执行脚本
$ . test.sh 1 2 3
$ source test.sh 1 2 3
# 新增一个子进程 bash 执行脚本
$ bash test.sh 1 2 3
$./test.sh 1 2 3

# --- 输出结果 ---
param num: 3
pid:291762
$@-param: 1
$@-param: 2
$@-param: 3
$*-param: 1
$*-param: 2
$*-param: 3
# 调用方式不同 $0 的输出会有所不同
# . | source 调用方式
$0: -bash
# ./test.sh | bash 调用方式
$0: test.sh
param1: 1
param2: 2
param3: 3

基础算数

sh
# + - * / %
# =: 赋值
# !=
# ==

判断

sh
# check file types and compare values
$ test
# 文件是否存在
$ test -e <path>;
$ [ -e <path> ]

# --- 文件类型 ---
# 判断,普通文件
$ [ -f <path> ]
# 判断,目录
$ [ -d <path> ]
# 判断,块设备文件
$ [ -b <path> ]
# 判断,字符设备文件
$ [ -c <path> ]
# 判断, 套接字文件
$ [ -S <path> ]
# 判断,管道文件
$ [ -p <path> ]
# 判断,链接文件
$ [ -L <path> ]

# --- 权限 ---
# 判断,可读
$ [ -r <path> ]
# 判断,可写
$ [ -w <path> ]
# 判断,可执行
$ [ -x <path> ]
# 判断,suid
$ [ -u <path> ]
# 判断,sgid
$ [ -g <path> ]
# 判断,sbit
$ [ -k <path> ]
# 判断,非空白文件
$ [ -s <path> ]

# --- 文件新旧判断 ---
# newer than。判断,file1 比 file2 新
$ [ file1 -nt file2 ]
# older than。判断,file1 比 file2 旧
$ [ file1 -ot file2 ]
# equal file 判断,inode 相等
$ [ file1 -ef file2 ]

# --- 整数判断 ---
# equal
$ [ val1 -eq val2 ]
# not equal
$ [ val1 -ne val2 ]
# greater than
$ [ val1 -gt val2 ]
# less than
$ [ val1 -lt val2 ]
# greater than ot equal
$ [ val1 -ge val2 ]
# less then or equal
$ [ val1 -le val2 ]

# --- 字符串判断 ---
# 判断,字符串长度为0
$ [ -z 'String' ]
$ [ -z $var ]
# 判断,字符串长度非0
$ [ -n $var ]
# 判断,内容相同
$ [ $var1 == $var2 ]
# 判断,内容不同
$ [ $var1 != $var2 ]

# --- 逻辑关联 ---
# AND
$ [ -e <path> -a -x <path> ]
# OR
$ [ -r <path> -o -x <path> ]
# 非
$ [ ! -e <path> ]

逻辑运算

sh
# --- 指令逻辑关联 --- 
# CMD1 状态值为 0,CMD2 才会执行。
$ CMD1 && CMD2
# CMD1 状态值非 0,CMD2 才会执行。
$ CMD1 || CMD2

# 类似,三目运算
$ CMD1 && CMD2 || CMD3

循环

sh
# 遍历文件
for file in /tmp
do
	ls -l $file
done

# 遍历
for i in {1..10}
do
	echo $i
done

# for, 
for ((i=0;i<=10;i++))
do
	echo $i
done

# while, 条件为真执行do代码块
i=0
while [ $i -lt 10 ]
do
	echo $i
	i=$(($i+1))
done

# until, 条件为假执行do代码块
i=0
until [ $1 -gt 10 ]
do
	echo $i
	i=$(($i+1))
done

流程控制

sh
# 单分支
i=0
if [ $i == 0 ]; then
	echo "true"
fi

# 双分支
i=1
if [ $i == 0 ]; then
	echo "true"
else
	echo "false"
fi

# 多重分支,if

if [ $i == 0 ]; then
	echo "0"
elif [ $i == 1 ]; then
	echo "1"
else
	echo "other"
fi

# 多重分支,case
i=3
case i in 
	1)
		echo 1
		;;
	2)
		echo 2
		;;
	3)
		echo 3
		;;
	*)
		echo other
		;;
esac

函数

sh
# int∈[0,255]
[function] fun() {
	[return int;]
}

fun