命令行技巧

使用 ssh 反向隧道登录没有 IP 的服务器

假设我们家里的服务器叫做 homeserver,没有公网 IP。然后我们有一台服务器叫做 relayserver,拥有公网 IP。

在家里执行

homeserver~$ ssh -fN -R 10022:localhost:22 relayserver_user@1.1.1.1

在服务器上就可以登陆啦

relayserver~$ ssh -p 10022 homeserver_user@localhost

然而这样链接还是很不稳定的,我们还是需要一个稳定的链接,这时候就可以使用 autossh 了,它会保持链接的稳定,自动重新连接。

autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C

参考

  1. http://xmodulo.com/access-linux-server-behind-nat-reverse-ssh-tunnel.html
  2. https://superuser.com/questions/37738/how-to-reliably-keep-an-ssh-tunnel-open

strace

尽管在线下会做充足的测试,但是线上出问题是难免的。当我们的程序在线上运行中遇到问题的时候,而我们又没有日志可以观察到底哪里出了问题,这时候可以使用 strace 命令。strace 可以直接根据 pid 附着到进程上,打印出进程的一些统计信息,为排查 bug 提供有意义的参考。

Strace 的选项

  1. c – See what time is spend and where (combine with -S for sorting)
  2. f – Track process including forked child processes
  3. o my-process-trace.txt – Log strace output to a file
  4. p 1234 – Track a process by PID
  5. P /tmp – Track a process when interacting with a path
  6. T – Display syscall duration in the output

Track by specific system call group

  1. e trace=ipc – Track communication between processes (IPC)
  2. e trace=memory – Track memory syscalls
  3. e trace=network – Track memory syscalls
  4. e trace=process – Track process calls (like fork, exec)
  5. e trace=signal – Track process signal handling (like HUP, exit)
  6. e trace=file – Track file related syscalls

Trace multiple syscalls

strace -e open,close

统计进程系统调用花费时间

strace -c -f -p 11084

一般来说要加上 -f 选项,这样才能跟踪多进程程序,也就是 fork 之后的进程。

strace -o output.txt -T -tt -e trace=all -p 28979

使用和这个命令可以统计每一个系统调用的时间

  1. 使用 struss 查看系统问题 https://www.ibm.com/developerworks/cn/linux/l-tsl/index.html
  2. strace 的详细用法 https://blog.csdn.net/uisoul/article/details/83143290
  3. https://blog.csdn.net/budong282712018/article/details/83151953
  4. https://blog.csdn.net/mapleleavesfor_me/article/details/42391979
  5. https://blog.51cto.com/5iwww/771031
  6. https://blog.csdn.net/lotluck/article/details/77943152
  7. https://linux-audit.com/the-ultimate-strace-cheat-sheet/

几个新一代命令行工具

新一代命令行工具的特点是语法简单,符合直觉。他们大多使用 rust 或者 go 编写。

duf

显示磁盘信息的工具 https://github.com/muesli/duf

sd

sd 可以替代 sed。sd 是使用 rust 编写的,所以使用的正则引擎和你在 JS 和 Python 中熟悉的正则引擎是一致的,也就不需要各种奇奇怪怪的转义了。sd 还具有字符串模式,也就是关闭正则表达式,这也避免了一些转义的工作量。

安装

# 首先安装 rust,如果没有安装的话
~$ curl https://sh.rustup.rs -sSf | sh
~$ cargo install sd

使用

# 和 sed 的对比:
sd: sd before after
sed: sed s/before/after/g

# 字符串模式,-s 开启,可以看到括号就是括号
> echo 'lots((([]))) of special chars' | sd -s '((([])))' ''
lots of special chars

# 默认是正则模式
> echo 'lorem ipsum 23   ' | sd '\s+$' ''
lorem ipsum 23

# 使用正则分组
> echo 'cargo +nightly w

choose

用于替代 cut 和 awk(一部分)
https://github.com/theryangeary/choose

procs

用于替代 ps

https://github.com/dalance/procs

Linux 下分区并挂载磁盘

分区

parted -s -a optimal /dev/sda mklabel gpt -- mkpart primary ext4 1 -1s

创建文件系统

mkfs.ext4 /dev/sda1

查看分区结果

parted -l

复制数据

首先挂载到临时分区

mount /dev/sdb1 /mnt

把之前的数据考进去

# rsync -av /home/* /mnt/
OR
# cp -aR /home/* /mnt/

校验数据

diff -r /home /mnt

删除老数据

rm -rf /home/*
umount /mnt

挂载

mount /dev/sdb1 /home

写入到 fstab 中

blkid /dev/sdb1

/dev/sdb1: UUID="e087e709-20f9-42a4-a4dc-d74544c490a6" TYPE="ext4" PARTLABEL="primary" PARTUUID="52d77e5c-0b20-4a68-ada4-881851b2ca99"

在 /etc/fstab 中增加

UUID=e087e709-20f9-42a4-a4dc-d74544c490a6   /home   ext4   defaults   0   2

每一列的含义如下


    UUID – specifies the block device, you can alternatively use the device file /dev/sdb1.
    /home – this is the mount point.
    etx4 – describes the filesystem type on the device/partition.
    defaults – mount options, (here this value means rw, suid, dev, exec, auto, nouser, and async).
    0 – used by dump tool, 0 meaning don’t dump if filesystem is not present.
    2 – used by fsck tool for discovering filesystem check order, this value means check this device after root filesystem.

调整分区大小

首先使用 parted 打开对应的磁盘

tiger@iZ8vbe91kz7sqlvkjdu8p6Z:~$ sudo parted
GNU Parted 3.2
Using /dev/vda
Welcome to GNU Parted! Type "help" to view a list of commands.
(parted) select /dev/vdc
Using /dev/vdc
(parted) resizepart
Partition number? 1
Warning: Partition /dev/vdc1 is being used. Are you sure you want to continue?
Yes/No? yes
End?  [107GB]? 1100G
(parted) print
Model: Virtio Block Device (virtblk)
Disk /dev/vdc: 1100GB
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:

Number  Start   End     Size    Type     File system  Flags
 1      1049kB  1100GB  1100GB  primary  ext4

然后使用 resize2fs 重新调整分区大小

resize2fs /dev/vdb1

参考

  1. https://www.tecmint.com/move-home-directory-to-new-partition-disk-in-linux/
  2. https://www.tecmint.com/parted-command-to-create-resize-rescue-linux-disk-partitions/

tmux cheatsheet

if you use set mouse off, then you could use system mark and copy, or if you are in Mac, you could use Option + Mouse Select

按键绑定

C-b         发送 Ctrl-b 按键
C-z         暂停(suspend) tmux 客户端

窗口创建与管理

!           把当前分区独立出来作为单独的窗口
"           横向分隔,变成上线两个分区
%           纵向分隔,变成左右两个分区
&           关闭当前窗口(window),也就是所有的分区
.           Prompt for an index to move the current window.
c           创建一个新窗口
x           关闭当前分区(pane)
{           Swap the current pane with the previous pane.
}           Swap the current pane with the next pane.
C-o         Rotate the panes in the current window forwards.
M-o         Rotate the panes in the current window backwards.
C-Up, C-Down
C-Left, C-Right
            Resize the current pane in steps of one cell.
M-Up, M-Down
M-Left, M-Right
            Resize the current pane in steps of five cells.
M-1 to M-5  Arrange panes in one of the five preset layouts: even-horizontal, even-vertical, main-horizontal, main-vertical, or tiled.
Space       Arrange the current window in the next preset layout.
M-n         Move to the next window with a bell or activity marker.
M-p         Move to the previous window with a bell or activity marker.

copy and paste

#           List all paste buffers.
-           Delete the most recently copied buffer of text.
=           Choose which buffer to paste interactively from a list.
[           Enter copy mode to copy text or view the history.
]           Paste the most recently copied buffer of text.
Page Up     Enter copy mode and scroll one page up.

rename

$           Rename the current session.
,           Rename the current window.

选择窗口

"           Prompt for a window index to select.
(           Switch the attached client to the previous session.
)           Switch the attached client to the next session.
0 to 9      Select windows 0 to 9.
l           Move to the previously selected window. remapped to \
n           Change to the next window.
o           Select the next pane in the current window.
p           Change to the previous window.
s           Select a new session for the attached client interactively.
w           Choose the current window interactively.
;           Move to the previously active pane.

其他

:           Enter the tmux command prompt.
?           List all key bindings.
D           Choose a client to detach.
L           Switch the attached client back to the last session.
d           Detach the current client.
f           Prompt to search for text in open windows.
i           Display some information about the current window.
q           Briefly display pane indexes.
r           Force redraw of the attached client.
m           Mark the current pane (see select-pane -m).
M           Clear the marked pane.
t           Show the time
z           Toggle zoom state of the current pane.
           ~           Show previous messages from tmux, if any.

           Up, Down
           Left, Right
                       Change to the pane above, below, to the left, or to the right of the current pane.

dtach

还有一个类似 tmux 的命令 dtach,不过后来没怎么用了,笔记还是留在这里。

dtach is used to detach and attach to a session.

dtach works with a session file, and can detach and attach to a session, but it has no functionality of terminal multiplexing.

Formula
dtach [mode] [session_file] [options] [command]

dtach modes are -a attach, -A attach or create, -c creates a new session and attach, -n creates without attach.

pv – 在 Linux 下查看命令执行进度

pv 是 Pipe Viewer 的缩写,也就是管道查看器。挡在命令行执行命令的时候,可以通过使用 pv 来指导当前的进度。

使用

替换 cat

比如你要把一个日志打包好下载:

% gzip -c access.log > access.log.gz

可以改成

% cat access.log | gzip > access.log.gz

使用 pv

% pv access.log | gzip > access.log.gz
611MB 0:00:11 [58.3MB/s] [=>      ] 15% ETA 0:00:59

使用多个 pv

可以使用多个 pv 来查看在不同阶段的速率

$ pv -cN source access.log | gzip | pv -cN gzip > access.log.gz
source:  760MB 0:00:15 [37.4MB/s] [=>     ] 19% ETA 0:01:02
  gzip: 34.5MB 0:00:15 [1.74MB/s] [  <=>  ]

在上面的命令中,-c 是为了防止两个 pv 的显示混在一起。-N 表示名字。可以看到读取 access.log 的速率是 37.4 MB/s,而写入 gzip 文件的速率大概是 1.74 MB/s,我们大概也可以得出压缩率大概是 21 倍。

指定文件的大小

可以用下面这个命令压缩一个文件夹。

$ tar -czf - . | pv > out.tgz
 117MB 0:00:55 [2.7MB/s] [>         ]

在上面的例子中我们可以看到下面 gzip 一行的输出中没有百分比,因为 pv 没法知道 gzip 之后的最终大小,所以没有办法计算进度。可以使用 -s 指定大小。

$ tar -cf - . | pv -s $(du -sb . | awk "{print $1}") | gzip > out.tgz
 253MB 0:00:05 [46.7MB/s] [>     ]  1% ETA 0:04:49

Linux 磁盘管理命令

挂载磁盘

mount 命令,用于挂载磁盘以及显示相关信息

mount 显示挂载信息
mount -t TYPE OPTIONS DEVICE DIRECTORY
比如:
     mount -t vfat /dev/sdb1 /mnt/media

常用的参数

-a 挂载 /etc/fstab 中的所有文件
-f 模拟挂载
-r 只读挂载
-w 读写挂载(默认)
-L 指定 lebel
-u 指定 uuid
-o 选项
    ro 只读
    rw 读写
    user 允许普通用户挂载
    check=none 不检查错误
    loop 挂载文件,比如 iso
    nofail 失败了也不要汇报
    remount
--bind 选择新的挂载点作为 alias &#x60;mount --rbind olddir newdir&#x60;
--move 移动到新的挂载点 &#x60;mount --move olddir newdir&#x60;

使用 mount 命令的最佳实践是在 /etc/fstab 中先输入需要挂载的磁盘对应的配置,然后使用 mount -a 挂载。这样避免在 /etc/fstab 中挂载的命令是错的导致无法开机。

卸载

umount DIRECTORY/DEVICE 卸载设备

磁盘使用

  • 查看分区的 uuid:ll /dev/disk/by-uuid。在 etc/fstab 中挂载磁盘最好使用 uuid
  • 查看分区的类型:parted -l
  • df show free disk spaces
  • du show disk usage infomation。du -sh ls # great command
  • dd disk dump

分区与格式化

fdisk DEVICE

p   print partition table
n   new partition
w   write back to table
d   删除分区

创建文件系统

mkfs.ext4 PARTITION

fsck check a file system

逻辑卷

硬盘称作物理卷,多个物理卷构成一个卷组,一个卷组可以分成多个逻辑卷

fzf – 命令行模糊查找工具

安装

在 mac 上直接 brew install fzf 就好了

使用

调用 fzf 命令

直接在命令行输入 fzf 开始模糊查找。

查找命令 | 匹配类型 | 说明
——|————|—————
sbtrkt|模糊匹配 | 匹配 sbtrkt
^music|前缀精确匹配 | 以 music 开头
.mp3^|后缀精确匹配 | 以.mp3 结尾
‘wild |精确匹配 (quoted) | 精确包含 wild
!fire | inverse-exact-match | 不包含 fire
!.mp3$ | inverse-suffix-exact-match | 不以.mp3 结尾

|可以做 or 匹配, 比如 ^core go$|rb$|py$ 表示以 core 开头,以 go 或 rb 或 py 结尾的

按键

ctrl-j/k 或者 ctrl-n/p 或者箭头来上下选择
ctrl-c 或者 Esc 退出
Enter 选择
在多行模式,tab 和 Shift-tab 来标记文件

除了这些按键之外,还可以使用 --bind 绑定自己的按键,见下文

选中之后,fzf 的默认操作是打印这个文件名,这样我们还得打开,所以可以直接 vim $(fzf) 也就是使用 vim 打开我们选中的文件。

使用快捷键

Ctrl-T 快速选择当前目录文件,并把文件名打印出来
Ctrl-R 使用 fzf 来过滤 history 命令
ALT-C cd 进入选中的目录

自动补全

fzf 支持不少命令的自动补全功能,通过 ** 来触发。如果没有特殊支持某个命令的话,fzf 会用文件来补全。

vim **<tab>
cd **<tab>
ssh **<tab>  从 /etc/hosts 中读取主机列表
unset **<tab>
export **<tab>
unalias **<tab>

kill -s TERM <tab>

这里我把触发按键设置成了 Ctrl-Y 比原生的触发更方便一点,如何配置见下文。

选项

--height xx% 默认情况下 fzf 占据了 100% 的屏幕
--reverse 提示符在上面
--bind 绑定命令
--preview 指定预览命令

默认情况下,在 fzf 中选中文件之后知识打印出这个文件名,可以使用 bind 来指定一些快捷键,来对文件的一些操作。

比如:

# Press F1 to open the file with less without leaving fzf
# Press CTRL-Y to copy the line to clipboard and aborts fzf (requires pbcopy)
fzf --bind "f1:execute(less -f {}),ctrl-y:execute-silent(echo {} | pbcopy)+abort"

默认情况下,fzf 不会预览文件的内容,可以使用 –preview 指定,

# Use head instead of cat so that the command doesn"t take too long to finish
fzf --preview "head -100 {}"

语法高亮

fzf --preview "[[ $(file --mime {}) =~ binary ]] &amp;&amp;
                 echo {} is a binary file ||
                 (highlight -O ansi -l {} ||
                  coderay {} ||
                  rougify {} ||
                  cat {}) 2> /dev/null"

相关的环境变量

默认情况下,fzf 从 find * -type f 中读取文件列表,可以使用更好用的 fd 来替换。

export FZF_DEFAULT_OPTS="--height 40% --reverse --border" 这个变量来指定默认选项。
export FZF_DEFAULT_COMMAND="fd --type f" 来指定
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"

我的最终配置

[ -f ~/.fzf.zsh ] &amp;&amp; source ~/.fzf.zsh

export FZF_DEFAULT_COMMAND="fd --type f"
export FZF_CTRL_T_COMMAND="fd --type f"
export FZF_ALT_C_COMMAND="fd --type d"
export FZF_COMPLETION_TRIGGER=""
export FZF_DEFAULT_OPTS="--height 40% --reverse --border --prompt ">>>" \
    --bind "alt-j:preview-down,alt-k:preview-up,alt-v:execute(vi {})+abort,ctrl-y:execute-silent(cat {} | pbcopy)+abort,?:toggle-preview" \
    --header "A-j/k: preview down/up, A-v: open in vim, C-y: copy, ?: toggle preview" \
    --preview "(highlight -O ansi -l {} 2> /dev/null || cat {} || tree -C {}) 2> /dev/null""
export FZF_CTRL_T_OPTS=$FZF_DEFAULT_OPTS
export FZF_CTRL_R_OPTS="--preview "echo {}" --preview-window hidden:wrap --bind "?:toggle-preview""
export FZF_ALT_C_OPTS="--height 40% --reverse --border --prompt ">>>" \
    --bind "alt-j:preview-down,alt-k:preview-up,?:toggle-preview" \
    --header "A-j/k: preview down/up, A-v: open in vim, C-y: copy, ?: toggle preview" \
    --preview "tree -C {}""
bindkey "^Y" fzf-completion
bindkey "^I" $fzf_default_completion
[ -f ~/.dotfiles/lib/fzf-extras.sh ] &amp;&amp; source ~/.dotfiles/lib/fzf-extras.sh