https://missing.csail.mit.edu/
https://missing-semester-cn.g...
https://www.bilibili.com/vide...
shell 会使用 UNIX 提供的信号机制执行进程间通讯。当一个进程接收到信号时,它会中止执行、处理该信号并基于信号传递的信息来改变其执行。就这一点而言,信号是一种软件中断。php
man signal
for referencekill
: sends signals to a process; default is TERMSIGINT
: ^C
; interrupt program; terminate processSIGQUIT
: ^\
; quit programSIGKILL
: terminate process; kill program; cannot be captured by process and will always terminate immediatelyhtml
SIGSTOP
: pause a processpython
SIGTSTP
: ^Z
; terminal stopSIGHUP
: terminal line hangup; terminate process; will be sent when terminal is closedlinux
nohup
to avoidSIGTERM
: signal requesting graceful process exitgit
kill -TERM <pid>
下面这个 Python 程序向您展现了捕获信号 SIGINT
并忽略它的基本操做,它并不会让程序中止。为了中止这个程序,咱们须要使用 SIGQUIT
信号。程序员
#!/usr/bin/env python import signal, time def handler(signum, time): print("nI got a SIGINT, but I am not stopping") signal.signal(signal.SIGINT, handler) i = 0 while True: time.sleep(.1) print("r{}".format(i), end="") i += 1
$ python sigint.py 24^C I got a SIGINT, but I am not stopping 26^C I got a SIGINT, but I am not stopping 30^\[1] 39913 quit python sigint.py
使用 fg
或 bg
命令恢复暂停的工做。它们分别表示在前台继续或在后台继续。github
jobs
命令会列出当前终端会话中还没有完成的所有任务。可使用 pid 引用这些任务(也能够用 pgrep
找出 pid)。也可使用百分号 + 任务编号(jobs
会打印任务编号)来选取该任务。若是要选择最近的一个任务,可使用 $!
这一特殊参数。shell
命令中的 &
后缀可让命令在直接在后台运行,不过它此时仍是会使用 shell 的标准输出。vim
使用 Ctrl-Z
放入后台的进程仍然是终端进程的子进程,一旦关闭终端(会发送另一个信号 SIGHUP
),这些后台的进程也会终止。为了防止这种状况发生,可使用 nohup
(一个用来忽略 SIGHUP
的封装) 来运行程序。针对已经运行的程序,可使用 disown
。浏览器
$ sleep 1000 ^Z [1] + 18653 suspended sleep 1000 $ nohup sleep 2000 & [2] 18745 appending output to nohup.out $ jobs [1] + suspended sleep 1000 [2] - running nohup sleep 2000 $ bg %1 [1] - 18653 continued sleep 1000 $ jobs [1] - running sleep 1000 [2] + running nohup sleep 2000 $ kill -STOP %1 [1] + 18653 suspended (signal) sleep 1000 $ jobs [1] + suspended (signal) sleep 1000 [2] - running nohup sleep 2000 $ kill -SIGHUP %1 [1] + 18653 hangup sleep 1000 $ jobs [2] + running nohup sleep 2000 $ kill -SIGHUP %2 $ jobs [2] + running nohup sleep 2000 $ kill %2 [2] + 18745 terminated nohup sleep 2000 $ jobs
终端多路复用使咱们能够分离当前终端会话并在未来从新链接。这让您操做远端设备时的工做流大大改善,避免了 nohup
和其余相似技巧的使用。
如今最流行的终端多路器是 tmux
。
会话 - 每一个会话都是一个独立的工做区,其中包含一个或多个窗口
tmux
开始一个新的会话tmux new -s NAME
以指定名称开始一个新的会话tmux ls
列出当前全部会话tmux
中输入 <C-b> d
(detach),将当前会话分离tmux a
(attach)从新链接最后一个会话。您也能够经过 -t
来指定具体的会话窗口 - 至关于编辑器或是浏览器中的标签页,从视觉上将一个会话分割为多个部分
<C-b> c
建立一个新的窗口,使用 <C-d>
关闭<C-b> N
跳转到第 N 个窗口,注意每一个窗口都是有编号的<C-b> p
(previous)切换到前一个窗口<C-b> n
(next)切换到下一个窗口<C-b> ,
重命名当前窗口<C-b> w
列出当前全部窗口面板 - 像 vim 中的分屏同样,面板使咱们能够在一个屏幕里显示多个 shell
<C-b> "
水平分割<C-b> %
垂直分割<C-b> <方向>
切换到指定方向的面板,<方向> 指的是键盘上的方向键<C-b> z
(zoom)切换当前面板的缩放<C-b> [
开始往回卷动屏幕。您能够按下空格键来开始选择,回车键复制选中的部分<C-b> <空格>
在不一样的面板排布间切换扩展阅读: 这里 是一份 tmux
快速入门教程, 而这一篇 文章则更加详细,它包含了 screen
命令。您也许想要掌握 screen
命令,由于在大多数 UNIX 系统中都默认安装有该程序。
# colorls source $(dirname $(gem which colorls))/tab_complete.sh alias ls=colorls alias l="ls -lh" alias ll="ls -lAh" alias la="ls -lah" alias hz="history | fzf" alias mv="mv -i" alias cp="cp -i" alias mkdir="mkdir -p" # To ignore an alias run it prepended with \ls # Or disable an alias altogether with unalias unalias la # To get an alias definition just call it with alias alias l # Will print l='ls -lh'
管理配置文件的一个方法是,把它们集中放在一个文件夹中,例如 ~/.dotfiles/
,并使用版本控制系统进行管理,而后经过脚本将其 符号连接 到须要的地方。这么作有以下好处:
一些技巧:
if [[ "$(uname)" == "Linux" ]]; then {do_something}; fi # 使用和 shell 相关的配置时先检查当前 shell 类型 if [[ "$SHELL" == "zsh" ]]; then {do_something}; fi # 您也能够针对特定的设备进行配置 if [[ "$(hostname)" == "myServer" ]]; then {do_something}; fi # Test if ~/.aliases exists and source it if [ -f ~/.aliases ]; then source ~/.aliases fi
# 链接设备 ssh foo@bar.mit.edu ssh foobar@192.168.1.42 # 若是存在配置文件,能够简写 ssh bar # 执行命令 # 在本地查询远端 ls 的输出 ssh foobar@server ls | grep PATTERN # 在远端对本地 ls 输出的结果进行查询 ls | ssh foobar@server grep PATTERN
基于密钥的验证机制使用了密码学中的公钥,咱们只须要向服务器证实客户端持有对应的私钥,而不须要公开其私钥。这样您就能够避免每次登陆都输入密码的麻烦了秘密就能够登陆。
ssh-keygen -t ed25519 -C "_your_email@example.com_" # If you are using a legacy system that doesn't support the Ed25519 algorithm, use: ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
生成的 id_rsa 和 id_rsa.pub 两个文件(或者 id_ed25519 和 id_ed25519.pub),分别为的私钥和公钥。私钥等效于你的密码,因此必定要好好保存它。要检查您是否持有某个密钥对的密码并验证它,您能够运行 ssh-keygen -y -f /path/to/key
。
ssh
会查询 .ssh/authorized_keys
来确认那些用户能够被容许登陆。您能够经过下面的命令将一个公钥拷贝到这里:
cat .ssh/id_ed25519.pub | ssh foobar@remote 'cat >> ~/.ssh/authorized_keys'
若是支持 ssh-copy-id
的话,可使用下面这种更简单的解决方案:
ssh-copy-id -i .ssh/id_ed25519.pub foobar@remote
使用 ssh 复制文件有不少方法:
ssh+tee
, 最简单的方法是执行 ssh
命令,而后经过这样的方法利用标准输入实现 cat localfile | ssh remote_server tee serverfile
。回忆一下,tee
命令会将标准输出写入到一个文件;scp
:当须要拷贝大量的文件或目录时,使用scp
命令则更加方便,由于它能够方便的遍历相关路径。语法以下:scp path/to/local_file remote_host:path/to/remote_file
;rsync
对 scp
进行来改进,它能够检测本地和远端的文件以防止重复拷贝。它还能够提供一些诸如符号链接、权限管理等精心打磨的功能。甚至还能够基于 --partial
标记实现断点续传。rsync
的语法和scp
相似。本地端口转发
远程端口转发
常见的情景是使用本地端口转发,即远端设备上的服务监听一个端口,而您但愿在本地设备上的一个端口创建链接并转发到远程端口上。例如,咱们在远端服务器上运行 Jupyter notebook 并监听 8888
端口。 而后,创建从本地端口 9999
的转发,使用 ssh -L 9999:localhost:8888 foobar@remote_server
。这样只须要访问本地的 localhost:9999
便可。
使用 ~/.ssh/config
文件来建立别名,相似 scp
、rsync
和mosh
的这些命令均可以读取这个配置并将设置转换为对应的命令行选项。
Host vm User foobar HostName 172.16.174.141 Port 2222 IdentityFile ~/.ssh/id_ed25519 LocalForward 9999 localhost:8888 # 在配置文件中也可使用通配符 Host *.mit.edu User foobaz
服务器侧的配置一般放在 /etc/ssh/sshd_config
。您能够在这里配置免密认证、修改 shh 端口、开启 X11 转发等等。也能够为每一个用户单独指定配置。
链接远程服务器的一个常见痛点是遇到由关机、休眠或网络环境变化致使的掉线。若是链接的延迟很高也很让人讨厌。Mosh(即 mobile shell )对 ssh 进行了改进,它容许链接漫游、间歇链接及智能本地回显。
有时将一个远端文件夹挂载到本地会比较方便, sshfs 能够将远端服务器上的一个文件夹挂载到本地,而后您就可使用本地的编辑器了。
常见的 Shell:
常见的 Shell 框架:
一些经典的模拟器:
一些新兴的模拟器(一般具备更好的性能,例以下面两个具备 GPU 加速):
$ sleep 1000 ^Z [1] + 689 suspended sleep 1000 $ sleep 2000 ^Z [2] + 697 suspended sleep 2000 $ jobs [1] - suspended sleep 1000 [2] + suspended sleep 2000 $ bg %1 [1] - 689 continued sleep 1000 $ jobs [1] - running sleep 1000 [2] + suspended sleep 2000 $ pgrep -af "sleep 1" 689 sleep 1000 $ pkill -f "sleep 1" [1] - 689 terminated sleep 1000 $ jobs [2] + suspended sleep 2000 $ pkill -f "sleep 2" $ jobs [2] + suspended sleep 2000 $ pkill -9 -f "sleep 2" [2] + 697 killed sleep 2000 $ jobs
参见 man kill
,默认发送的信号是 TERM。-9
等价于 -SIGKILL
或者 -KILL
$ sleep 10 & [1] 1121 $ pgrep sleep | wait ; ls [1] + 1121 done sleep 10 Nothing to show here
$ pidwait() { wait $1 echo "done" eval $2 } $ sleep 10 & [1] 1420 $ pidwait 1420 "ls" [1] + 1420 done sleep 10 done Nothing to show here