跳转至

Shell

基本概念

计算机最根本的交互接口。常用Bourne Again SHell, 简称 “bash” 。本质是一个编程环境

命令界面

missing:~$ echo My\ Photos
My Photos

missing: 主机名, ~:主shell工作目录, #/$:是/否root echo:命令(由PATH中搜索) My\ Photos: 参数(shell通过空格分割命令, \为转义符)

变量与通配符

foo=bar  #注意不要空格
echo "$foo" # 打印 bar
echo '$foo' # 打印 $foo

shell中的变量赋值后,可以采用$foo的形式调用;

注意Bash中的字符串通过' 和 "分隔符来定义,但以'定义的字符串为原义字符串,其中的变量不会被转义,而 "定义的字符串会将变量值进行替换。

mv *{.py,.sh} folder

执行脚本可以分别使用 ? 和 * 来匹配一个或任意个字符。

同时也可以用花括号{} 来自动展开这些命令。

配置文件

很多程序的配置都是通过纯文本格式的被称作点文件的配置文件来完成的

对于 bash来说,在大多数系统下,您可以通过编辑 .bashrc 或 .bash_profile 来进行配置别名,或者是环境变量。

最好能够通过版本控制系统对这些配置文件进行管理。

同时也可以去参考别人的配置文件,见dotfiles 仓库

常用快捷键

快捷键 解释 快捷键 解释
Ctrl + A 移动光标到行首 Ctrl + E 移动光标到行尾
Ctrl + U 删除光标到行首的所有字符 Ctrl + K 删除光标到行尾的所有字符
Ctrl + W 删除光标前的一个单词 Ctrl + Y 粘贴最后一次删除的文本
Ctrl + L 清屏,相当于执行 clear 命令 Ctrl + R 搜索命令历史
Ctrl + C 终止当前命令 Ctrl + D 退出当前 Shell 会话
Alt + F 向前移动一个单词 Alt + B 向后移动一个单词

常见指令

指令 指令含义 常用参数1 常用参数2 常用参数3
ls 列出目录内容 -l(长格式) -a(显示隐藏文件) -h(人类可读格式)
cd 切换目录 -(返回上个目录) ..(上一级目录) /(根目录)
cp 复制文件或目录 -r(递归复制) -v(显示过程) -i(交互模式)
mv 移动或重命名文件 -i(交互模式) -v(显示过程) -n(不覆盖)
rm 删除文件或目录 -r(递归删除) -f(强制删除) -i(交互模式)
chmod 修改文件权限 -R(递归修改) +x(添加执行权限) -w(移除写权限)
tee 从标准输入读取数据并写入标准输出和文件 -a(追加到文件末尾) -i(忽略中断信号) 多个文件参数(同时写入)
grep 搜索文本内容 -i(忽略大小写) -r(递归搜索) -v(反转匹配)
find 查找文件或目录 -name(按名称) -type(按类型) -size(按大小)
tar 压缩或解压文件 -c(创建归档) -x(解压归档) -z(使用 gzip)
xargs 将输入转为参数 -0(以空字符分隔) -d(将指定的字符作为定界符) -a file(从指定的文件读取输入)

更多指令的帮助,可加入参数--help或者使用指令man看查手册

也可以安装社区手册tldr进行查询

常见指令模式

  • 会造成不可撤回操作的工具一般会提供“空运行”(dry run)标志参数以确认操作,或者“交互式”(interactive)标志参数以确认每个操作。

  • 基本所有的工具支持使用 --verbose 或者 -v 标志参数来输出详细的运行信息。多次使用这个标志参数,比如 -vvv,可以让工具输出更详细的信息(经常用于调试)。同样,很多工具支持 --quiet 标志参数来抑制除错误提示之外的其他输出。

  • --version 或者 -V 标志参数可以让工具显示它的版本信息(对于提交软件问题报告非常重要)。

  • 会造成破坏性结果的工具一般默认进行非递归的操作,但是支持使用“递归”(recursive)标志函数(通常是 -r)。

  • 大多数工具中,使用 - 代替输入或者输出文件名意味着工具将从标准输入(standard input)获取所需内容,或者向标准输出(standard output)输出结果。

  • 可以使用特殊参数 -- 让某个程序 停止处理 -- 后面出现的标志参数以及选项(以 - 开头的内容)(视为正常参数),如

ssh machine --for-ssh -- foo --for-foo

其中 -- 会让 ssh 知道 --for-foo 不是 ssh 的标志参数,而是foo的。

Shell编程初步

程序间的链接

shell中有三个流:标准输入流,标准输出流,标准错误流(对应编号0,1,2)

>与<:

最简单的重定向,可以将输出流定向到文件。

可使用>>来向文件内追加内容。

mising:~$ cat null >output 2>error

如上,2>可以重定向标准错误流;此外,>&2可以输入到错误流;&>则可以同时捕捉输出流和错误流

|管道:

使用管道( pipes ),允许我们将一个程序的输出和另外一个程序的输入连接起来

此外还可以用xargs将输入转为参数

missing:~$ curl --head --silent google.com | grep last-modified

特殊变量

符号 解释
$0 脚本名
$1 到 $9 脚本的参数。 $1 是第一个参数,依此类推
$@ 所有参数
$# 参数个数
$? 前一个命令的返回值
$$ 当前脚本的进程识别码
!! 完整的上一条命令,包括参数
$( CMD ) 以变量的形式获取一个命令的输出
<( CMD ) 执行 CMD 并将结果输出到临时文件中,并将 <( CMD ) 替换成临时文件名

控制语句

条件判断

if [[ $num -gt 5 ]]; then   #建议采用双方格
  echo "num 大于 5"
else
  echo "num 小于或等于 5"
  >&2 echo "erro"
  exit 1
fi

循环语句

for i in 1 2 3 4 5; do
  echo "数字: $i"
done
while [[ $counter -le 5 ]]; do
  echo "计数器: $counter"
  counter=$((counter + 1))
done                            #break和continue使用正常

函数与脚本

函数示例:(可以被source写入)

mcd () {
    mkdir -p "$1"
    cd "$1"}

脚本示例:

 #!/bin/sh         #shebang
 curl --head --silent https://missing.csail.mit.edu

认识与区别

  • 函数只能与shell使用相同的语言,脚本可以使用任意语言。因此在脚本中包含 shebang 是很重要的。
  • 函数仅在定义时被加载,脚本会在每次被执行时加载。这让函数的加载比脚本略快一些,但每次修改函数定义,都要重新加载一次。
  • 函数会在当前的shell环境中执行,脚本会在单独的进程中执行。因此,函数可以对环境变量进行更改。脚本需要使用 export 将环境变量导出,并将值传递给环境变量。

任务控制

常用信号

Shell 会使用 UNIX 提供的信号机制执行进程间通信。

当一个进程接收到信号时,它会停止执行、处理该信号并基于信号传递的信息来改变其执行。

信号名称 编号 描述 常见快捷键
SIGHUP 1 挂起信号,通常在终端关闭时发送。
SIGINT 2 中断信号,通常在按下 Ctrl+C 时发送。 Ctrl+C
SIGQUIT 3 退出信号,通常在按下 Ctrl+\ 时发送。 Ctrl+\
SIGKILL 9 杀死信号,强制终止进程。
SIGTERM 15 终止信号,通常用于请求程序正常终止。 kill默认
SIGSTOP 19 停止信号,暂停进程的执行。 Ctrl+Z
SIGTSTP 20 终端停止信号,通常在按下 Ctrl+Z 时发送。 Ctrl+Z
SIGCONT 18 继续信号,恢复一个被 SIGSTOP 暂停的进程。

进程管理

  • jobs: 列出当前会话中未完成的任务,-l可以输出完整的pid号
  • ps: 用于显示当前正在运行的进程信息,-ef 显示详细的格式化进程列表,pstree 命令以树状结构显示所有进程
  • kill/pkill: 发送信号给进程,默认为SIGTERM。输入分别为进程号/进程内容
  • fg/bg: 回复暂停的工作,分别在前台/后台继续工作
  • pgrep: 寻找相关内容的进程号

终端拓展

多路复用

终端多路复用器(如tmux)可以允许我们基于面板和标签分割出多个终端窗口,这样您便可以同时与多个 shell 会话进行交互。 tmux 的快捷键需要我们掌握,它们都是类似 <C-b> x 这样的组合,即需要先按下Ctrl+b,松开后再按下 x。

常用指令如下:

会话

每个会话都是一个独立的工作区,其中包含一个或多个窗口

  • tmux 开始一个新的会话

  • tmux new -s NAME 以指定名称开始一个新的会话

  • tmux ls 列出当前所有会话

  • tmux a 重新连接最后一个会话。您也可以通过 -t 来指定具体的会话

窗口

相当于编辑器或是浏览器中的标签页,从视觉上将一个会话分割为多个部分

  • <C-b> c 创建一个新的窗口,使用 <C-d>关闭
  • <C-b> N 跳转到第 N 个窗口,注意每个窗口都是有编号的
  • <C-b> p/n 切换到前/后一个窗口
  • <C-b> w 列出当前所有窗口

更多后续学习可以参考 tmux入门教程

远程设备

登陆

使用远程服务器来部署后端软件,可以使用安全 shell(SSH),如

ssh foo@bar.mit.edu ls

即以用户名 foo 登录服务器 bar.mit.edu, 执行ls并返回内容。

ssh可以通过密钥的方式登入,需要分别持有公钥和私钥

ssh-keygen -o -a 100 -t ed25519 -f ~/.ssh/id_ed25519 #生成密钥
cat .ssh/id_ed25519.pub | ssh foobar@remote 'cat >> ~/.ssh/authorized_keys' #加入公钥

文件拷贝

  • ssh+tee, 利用标准输入实现,如 cat localfile | ssh remote_server tee serverfile。
  • scp :当需要拷贝大量的文件或目录时,使用scp 命令则更加方便,因为它可以方便的遍历相关路径。如:scp path/to/local_file remote_host:path/to/remote_file;

端口转发

通过本地端口监听远程端口,可以通过端口转发形式,如:

ssh -L 9999:localhost:8888 foobar@remote_server

就可以通过本地的 localhost:9999监听远程服务器的8888端口

远端文件管理

FUSE(用户空间文件系统)允许运行在用户空间上的程序实现文件系统调用,并将这些调用与内核接口联系起来。

FUSE经常被用于实现通过ssh进行远端文件管理,使得文件虽然存储在远程主机,对于本地计算机上的软件而言和存储在本地别无二致。

常见的FUSE文件系统有:

  • sshfs:使用 SSH 连接在本地打开远程主机上的文件
  • rclone:将 Dropbox、Google Drive、Amazon S3、或者 Google Cloud Storage 一类的云存储服务挂载为本地文件系统
  • kbfs:分布式端到端加密文件系统。在这个文件系统里有私密(private),共享(shared),以及公开(public)三种类型的文件夹

SSH 配置

在~/.ssh/config可以方便地进行文件配置,记录下链接信息。

服务器侧的配置通常放在 /etc/ssh/sshd_config

配置举例如下:

Host vm
    User foobar
    HostName 172.16.174.141
    Port 2222
    IdentityFile ~/.ssh/id_ed25519
    LocalForward 9999 localhost:8888

# 在配置文件中也可以使用通配符
Host *.mit.edu
    User foobaz

内容拓展

SHELL工具

当你需要实现更丰富的功能,最好的方式是寻找替代方案——找到合适的替代程序即可

ripgrep (rg)

丰富grep的功能,并通过多线程提高速度

# 查找所有使用了 requests 库的文件
rg -t py 'import requests'
# 查找所有没有写 shebang 的文件(包含隐藏文件)
rg -u --files-without-match "^#!"
# 查找所有的foo字符串,并打印其之后的5行
rg foo -A 5
# 打印匹配的统计信息(匹配的行和文件的数量)
rg --stats PATTERN

fasd

能通过频率和时效排序,自动找到最常访问的目录和文件

v def conf       =>     vim /some/awkward/path/to/type/default.conf
j abc            =>     cd /hell/of/a/awkward/path/to/get/to/abcdef
f exa            =>     ls /home/bony/example.tar

优先注意点*

  • 多去使用键盘,少使用鼠标。这一目标可以通过多加利用快捷键,更换界面等来实现。
  • 学好编辑器。作为程序员你大部分时间都是在编辑文件,因此值得学好这些技能。
  • 学习怎样去自动化或简化工作流程中的重复任务。因为这会节省大量的时间。
  • 学习像 Git 之类的版本控制工具并且知道如何与 GitHub 结合,以便在现代的软件项目中协同工作。