命令行的艺术 笔记
基础
man bash学习 Bash 基础知识man阅读文档apropos查找文档有些命令并不对应可执行文件,而是在 Bash 内置好的,此时可以使用
help和help -d命令获取帮助信息type 命令来判断这个命令到底是可执行文件、shell 内置命令还是别名>和<来重定向输出和输入|来重定向管道>会覆盖输出文件而>>是在文件末添加。了解标准输出 stdout 和标准错误 stderr通配符
*代表任意数量的字符?字符代表单个字符[...]匹配方括号之中的任意一个字符[start-end]表示一个连续的范围[^...]和[!...]表示匹配不在方括号里面的字符,也可以是连续范围[!start-end]{...}表示匹配大括号里面的所有模式,模式之间使用逗号分隔{start..end}会匹配连续范围的字符[]需要匹配的字符集合。{}代表一组表达式的与关系{...}与[...]有一个很重要的区别。如果匹配的文件不存在,[...]会失去模式的功能,变成一个单纯的字符串,而{...}依然可以展开$ ls [ab].txt ls: [ab].txt: No such file or directory $ ls {a,b}.txt ls: a.txt: No such file or directory ls: b.txt: No such file or directory通配符是先解释,再执行
通配符不匹配,会原样输出,这条规则对
{...}不适用上面所有通配符只匹配单层路径,不能跨目录匹配,如果要匹配子目录里面的文件
ls */*.txt
引用意味着保护在引号中的字符串
""部分引用,允许解释变量名前缀 ($)、后引符 (`) 和 转义符 (\)''全局引用,不允许解释变量引用,不会展开通配符变量引用会保留空白字符
$ hello="A B C D" $ echo $hello A B C D $ echo "$hello" A B C D''保护被引起字符串中的特殊字符$ ls first a.txt b.txt $ grep '[Ff]irst' *.txt a.txt:This is the first line of file1.txt. b.txt:This is the First line of file1.txt. # [Ff]irst 解释匹配到文件 first,因此等效于 grep first *.txt $ grep [Ff]irst *.txt a.txt:This is the first line of file1.txt. # 删除之后未匹配到,原样输出 $ rm first $ grep [Ff]irst *.txt a.txt:This is the first line of file1.txt. b.txt:This is the First line of file1.txt.
Bash 中的任务管理工具:
&后台执行- ctrl-z 挂起当前任务
- ctrl-c 中断前台任务
jobs列出所有后台任务fg %指定后台任务转到前台bg当前任务转到后台kill %杀掉指定后台任务
基本网络管理工具:
ifconfig是net-tools套件提供的网络管理命令ip是iproute2套件提供的网络管理命令,功能更强大,并旨在取代后$ sudo ip addr add 192.168.0.193/24 dev wlan0 $ ip addr show wlan0 $ ip route show $ ip neighbour # mac addr $ sudo ip link set ppp0 down $ sudo ip link set ppp0 updig和nslookup用来解析域名,dig可以得到更多的域名信息
grep/egrep正则表达式egrep=grep -E-i–ignore-case-o–only-matchin-v–invert-match-A–after-context-B–before-context-C–context
学会基本的文件管理工具:
ls和ls -l(了解ls -l中每一列代表的意义),less,head,tail和tail -f(甚至less +F),ln和ln -s(了解硬链接与软链接的区别),chown,chmod,du(硬盘使用情况概述:du -hs \*)。 关于文件系统的管理,学习df,mount,fdisk,mkfs,lsblk。知道 inode 是什么(与ls -i和df -i等命令相关)。
日常使用
Bash 命令行
- Tab 自动补全参数
- ctrl-r 搜索命令行历史记录
- 重复按下 ctrl-r 会向后查找匹配项
- Enter 键会执行当前匹配的命令
- 右方向键会将匹配项放入当前行中,不会直接执行,以便做出修改
Bash 命令行移动
- ctrl-w 删除最后一个单词
- ctrl-u 删除行内光标所在位置之前的内容
- alt-b 和 alt-f 以单词为单位移动光标
- ctrl-a 光标移至行首
- ctrl-e 光标移至行尾
- ctrl-k 删除光标至行尾的所有内容
- ctrl-l 清屏
man readline可以查看 Bash 中的默认快捷键。内容有很多,例如 alt-. 循环地移向前一个参数,而 alt-* 可以展开通配符
按下 alt-# 在行首添加
#把它当做注释再按下回车执行。之后借助命令行历史记录,你可以很方便恢复你刚才输入到一半的命令xargs将标准输入转为命令行参数-L指定多少行作为一个命令行参数-P最大并行数-i或-I将命令行参数传给多个命令$ find . -name '*.py' | xargs grep some_function $ cat hosts | xargs -I{} ssh root@{} hostname $ find . -name "*.c" | xargs -i cp {} ~ $ echo -e "a\nb\nc" | xargs -L 1 echo a b c $ echo -e "a\nb\nc" | xargs -L 2 echo a b c
parallel在一台或多台计算机上并行的执行计算任务pstree -p以一种优雅的方式展示进程树pgrep和pkill根据名字查找进程或发送信号(-f参数通常有用)kill发送信号给进程kill -l所有信号种类man 7 signal查看详细列表kill -STOP [pid]发送STOP信号到进程以停止进程
使一个后台进程持续运行
disown是bash的内置命令,将指定任务从jobs列表之中移除# 移出最近一个正在执行的后台任务 $ disown # 移出所有正在执行的后台任务 $ disown -r # 移出所有后台任务 $ disown -a # 不移出后台任务,但是让它们不会收到SIGHUP信号 $ disown -h # 根据jobId,移出指定的后台任务 $ disown %2 $ disown -h %2nohup命令实际上将子进程与它所在的 session 分离,不会自动把进程变为后台任务,所以必须加上&符号$ nohup node server.js &
检查端口
netstat -lntp或ss -plat检查哪些进程在监听端口(默认是检查 TCP 端口; 添加参数-u则检查 UDP 端口)lsof -iTCP -sTCP:LISTEN -P -n
lsof来查看开启的套接字和文件uptime或w来查看系统已经运行多长时间alias来创建常用命令的快捷形式。例如:alias ll='ls -latr'创建了一个新的命令别名ll可以把别名、shell 选项和常用函数保存在
~/.bashrc,具体看下这篇文章。这样做的话你就可以在所有 shell 会话中使用你的设定把环境变量的设定以及登陆时要执行的命令保存在
~/.bash_profile。而对于从图形界面启动的 shell 和cron启动的 shell,则需要单独配置文件要想在几台电脑中同步你的配置文件(例如
.bashrc和.bash_profile),可以借助 Git 建立 repository变量和文件名中包含空格处理时要格外小心
- Bash 变量要用引号括起来,比如
"$FOO"部分引用 - 使用
-0或-print0选项以便用NULL代替来分隔文件名$ locate -0 pattern | xargs -0 ls -al $ find / -print0 -type d | xargs -0 ls -al - for 循环中循环访问的文件名含有空字符(空格、tab 等字符),只需用
IFS=$'\n'把内部字段分隔符设为换行符
- Bash 变量要用引号括起来,比如
Bash
set使用set -x输出执行的那行命令set -vset -e发生错误时退出而不是继续运行set -u来检查是否使用了未赋值的变量set -o pipefail,它可以监测管道中的错误- 当牵扯到很多脚本时,使用
trap来检测 ERR 和 EXIT。一个好的习惯是在脚本文件开头这样写,这会使它能够检测一些错误,并在错误发生时中断程序并输出信息:set -euxo pipefail trap "echo 'error: Script failed: see failed command above'" ERR
Bash subshell
- subshell 调用方式
- 运行另一个 shell 脚本
- 嵌在
()里的一列命令在一个子 shell 里运行 - 管道产生子shell
- 子 shell 中的目录更改不会影响到父 shell
- 子 shell 里的变量实际为局部变量
- 子 shell 可用于为一组命令设定临时的环境变量
- 子 shell(使用括号
(...))是一种组织参数的便捷方式。一个常见的例子是临时地移动工作路径# do something in current dir (cd /some/other/dir && other-command) # continue in original dir
- subshell 调用方式
Bash 变量扩展
$#参数个数$0-$9位置参数${name:?error message}用于检查变量是否存在input_file=${1:?usage: $0 input_file}只需要一个参数${name:-default}变量为空时使用默认值output_file=${2:-logfile}再加一个(可选的)参数,默认值为logfile- 数学运算符
((...)),例如i=$(( (i + 1) % 5 )) - 序列
{1..10},例如for i in {1..5}; do echo $i; done - 模式匹配截断字符串:
${var%suffix}和${var#prefix}。例如,假设var=foo.pdf,那么echo ${var%.pdf}.txt将输出foo.txt ${PARAMETER/PATTERN/STRING}替换字符串
括号扩展(
{…})来减少输入相似文本,并自动化文本组合# mv foo.txt foo.pdf ../ $ mv foo.{txt.pdf} ../ # cp foo foo.bak $ cp foo{,.bak} # mkdir -p test-a/subtest-{1,2,3} test-b/subtest-{1,2,3} test-c/subtest-{1,2,3} $ mkdir -p test-{a,b,c}/subtest-{1,2,3}<(some command)可以将输出视为文件。例如,对比本地文件/etc/hosts和一个远程文件:diff /etc/hosts <(ssh somehost cat /etc/hosts)编写脚本时,把代码都放在大括号里可以防止下载不完全代码被执行
{ # 在这里写代码 }Here Documents 是一种定义字符串的方法,可以保存文字里面的换行或是缩排等空白字元
cat <<End-of-message ------------------------------------- This is line 1 of the message. This is line 2 of the message. This is the last line of the message. ------------------------------------- End-of-message同时重定向标准输出和标准错误
>重定向标准输出<重定向标准输入/dev/null特殊空设备> /dev/null丢弃输出< /dev/null发送 EOF 给程序并立刻断开输入- 为了保证命令不会在标准输入里残留一个未关闭的文件句柄捆绑在你当前所在的终端上,在命令后添加
< /dev/null
- There are two formats for redirecting standard output and standard error:
&>word推荐使用>&word- 等效于
>word 2>&1$ ls -l &>logfile </dev/null $ ls -l >logfile 2>&1 </dev/null
查看编码信息
man ascii查看具有十六进制和十进制值的ASCII表man unicode,man utf-8,以及man latin1通用编码信息
使用
screen或tmux来使用多份屏幕,当你在使用 ssh 时(保存 session 信息)将尤为有用。而byobu可以为它们提供更多的信息和易用的管理工具。另一个轻量级的 session 持久化解决方案是dtach。ssh 中,了解如何使用
-L或-D(偶尔需要用-R)开启隧道是非常有用的,比如当你需要从一台远程服务器上访问 web 页面。对 ssh 设置做一些小优化可能是很有用的,例如这个
~/.ssh/config文件包含了防止特定网络环境下连接断开、压缩数据、多通道等选项:TCPKeepAlive=yes ServerAliveInterval=15 ServerAliveCountMax=6 Compression=yes ControlMaster auto ControlPath /tmp/%r@%h:%p ControlPersist yes一些其他的关于 ssh 的选项是与安全相关的,应当小心翼翼的使用。例如你应当只能在可信任的网络中启用
StrictHostKeyChecking=no,ForwardAgent=yes。考虑使用
mosh作为 ssh 的替代品,它使用 UDP 协议。它可以避免连接被中断并且对带宽需求更小,但它需要在服务端做相应的配置。获取八进制形式的文件访问权限(修改系统设置时通常需要,但
ls的功能不那么好用并且通常会搞砸),可以使用类似如下的代码:stat -c '%A %a %n' /etc/timezone使用
fpp(PathPicker)可以与基于另一个命令(例如git)输出的文件交互。将 web 服务器上当前目录下所有的文件(以及子目录)暴露给你所处网络的所有用户,使用:
python -m SimpleHTTPServer 7777(使用端口 7777 和 Python 2)或python -m http.server 7777(使用端口 7777 和 Python 3)。以其他用户的身份执行命令,使用
sudo。默认以 root 用户的身份执行;使用-u来指定其他用户。使用-i来以该用户登录(需要输入_你自己的_密码)。将 shell 切换为其他用户,使用
su username或者sudo - username。加入-会使得切换后的环境与使用该用户登录后的环境相同。省略用户名则默认为 root。切换到哪个用户,就需要输入_哪个用户的_密码。了解命令行的 128K 限制。使用通配符匹配大量文件名时,常会遇到“Argument list too long”的错误信息。(这种情况下换用
find或xargs通常可以解决。)当你需要一个基本的计算器时,可以使用
python解释器(当然你要用 python 的时候也是这样)。例如:>>> 2+3 5
文件及数据处理
通过文件名查找文件
- 当前目录
find . -iname '*something*' - 所有路径
locate something(但注意到updatedb可能没有对最近新建的文件建立索引,所以你可能无法定位到这些未被索引的文件)
- 当前目录
检索源码
agrgMarkdown,HTML,以及所有文档格式之间的转换,
pandoc使用
jq处理 JSON将 HTML 转为文本:
lynx -dump -stdin。当你要处理棘手的 XML 时候,
xmlstarlet算是上古时代流传下来的神器。使用
shyaml处理 YAML。要处理 Excel 或 CSV 文件的话,csvkit 提供了
in2csv,csvcut,csvjoin,csvgrep等方便易用的工具。当你要处理 Amazon S3 相关的工作的时候,
s3cmd是一个很方便的工具而s4cmd的效率更高。Amazon 官方提供的aws以及saws是其他 AWS 相关工作的基础,值得学习。了解如何使用
sort和uniq,包括 uniq 的-u参数和-d参数,具体内容在后文单行脚本节中。另外可以了解一下comm。了解如何使用
cut,paste和join来更改文件。很多人都会使用cut,但遗忘了join。了解如何运用
wc去计算新行数(-l),字符数(-m),单词数(-w)以及字节数(-c)。了解如何使用
tee将标准输入复制到文件甚至标准输出,例如ls -al | tee file.txt。要进行一些复杂的计算,比如分组、逆序和一些其他的统计分析,可以考虑使用
datamash。注意到语言设置(中文或英文等)对许多命令行工具有一些微妙的影响,比如排序的顺序和性能。大多数 Linux 的安装过程会将
LANG或其他有关的变量设置为符合本地的设置。要意识到当你改变语言设置时,排序的结果可能会改变。明白国际化可能会使 sort 或其他命令运行效率下降许多倍。某些情况下(例如集合运算)你可以放心的使用export LC_ALL=C来忽略掉国际化并按照字节来判断顺序。你可以单独指定某一条命令的环境,只需在调用时把环境变量设定放在命令的前面,例如
TZ=Pacific/Fiji date可以获取斐济的时间。了解如何使用
awk和sed来进行简单的数据处理。 参阅 One-liners 获取示例。替换一个或多个文件中出现的字符串:
perl -pi.bak -e 's/old-string/new-string/g' my-files-*.txt使用
repren来批量重命名文件,或是在多个文件中搜索替换内容。(有些时候rename命令也可以批量重命名,但要注意,它在不同 Linux 发行版中的功能并不完全一样。)# 将文件、目录和内容全部重命名 foo -> bar: repren --full --preserve-case --from foo --to bar . # 还原所有备份文件 whatever.bak -> whatever: repren --renames --from '(.*)\.bak' --to '\1' *.bak # 用 rename 实现上述功能(若可用): rename 's/\.bak$//' *.bak根据 man 页面的描述,
rsync是一个快速且非常灵活的文件复制工具。它闻名于设备之间的文件同步,但其实它在本地情况下也同样有用。在安全设置允许下,用rsync代替scp可以实现文件续传,而不用重新从头开始。它同时也是删除大量文件的最快方法之一:mkdir empty && rsync -r --delete empty/ some-dir && rmdir some-dir若要在复制文件时获取当前进度,可使用
pv,pycp,progress,rsync --progress。若所执行的复制为block块拷贝,可以使用dd status=progress。使用
shuf可以以行为单位来打乱文件的内容或从一个文件中随机选取多行。了解
sort的参数。显示数字时,使用-n或者-h来显示更易读的数(例如du -h的输出)。明白排序时关键字的工作原理(-t和-k)。例如,注意到你需要-k1,1来仅按第一个域来排序,而-k1意味着按整行排序。稳定排序(sort -s)在某些情况下很有用。例如,以第二个域为主关键字,第一个域为次关键字进行排序,你可以使用sort -k1,1 | sort -s -k2,2。如果你想在 Bash 命令行中写 tab 制表符,按下 ctrl-v [Tab] 或键入
$'\t'(后者可能更好,因为你可以复制粘贴它)。标准的源代码对比及合并工具是
diff和patch。使用diffstat查看变更总览数据。注意到diff -r对整个文件夹有效。使用diff -r tree1 tree2 | diffstat查看变更的统计数据。vimdiff用于比对并编辑文件。对于二进制文件,使用
hd,hexdump或者xxd使其以十六进制显示,使用bvi,hexedit或者biew来进行二进制编辑。同样对于二进制文件,
strings(包括grep等工具)可以帮助在二进制文件中查找特定比特。制作二进制差分文件(Delta 压缩),使用
xdelta3。使用
iconv更改文本编码。需要更高级的功能,可以使用uconv,它支持一些高级的 Unicode 功能。例如,这条命令移除了所有重音符号:uconv -f utf-8 -t utf-8 -x '::Any-Lower; ::Any-NFD; [:Nonspacing Mark:] >; ::Any-NFC; ' < input.txt > output.txt拆分文件可以使用
split(按大小拆分)和csplit(按模式拆分)。操作日期和时间表达式,可以用
dateutils中的dateadd、datediff、strptime等工具。使用
zless、zmore、zcat和zgrep对压缩过的文件进行操作。文件属性可以通过
chattr进行设置,它比文件权限更加底层。例如,为了保护文件不被意外删除,可以使用不可修改标记:sudo chattr +i /critical/directory/or/file使用
getfacl和setfacl以保存和恢复文件权限。例如:getfacl -R /some/path > permissions.txt setfacl --restore=permissions.txt为了高效地创建空文件,请使用
truncate(创建稀疏文件),fallocate(用于 ext4,xfs,btrf 和 ocfs2 文件系统),xfs_mkfile(适用于几乎所有的文件系统,包含在 xfsprogs 包中),mkfile(用于类 Unix 操作系统,比如 Solaris 和 Mac OS)。
## 系统调试
单行脚本
冷门但有用
更多资源
expr:计算表达式或正则匹配m4:简单的宏处理器yes:多次打印字符串cal:漂亮的日历env:执行一个命令(脚本文件中很有用)printenv:打印环境变量(调试时或在写脚本文件时很有用)look:查找以特定字符串开头的单词或行cut,paste和join:数据修改fmt:格式化文本段落pr:将文本格式化成页/列形式fold:包裹文本中的几行column:将文本格式化成多个对齐、定宽的列或表格expand和unexpand:制表符与空格之间转换nl:添加行号seq:打印数字bc:计算器factor:分解因数gpg:加密并签名文件toe:terminfo 入口列表nc:网络调试及数据传输socat:套接字代理,与netcat类似slurm:网络流量可视化dd:文件或设备间传输数据file:确定文件类型tree:以树的形式显示路径和文件,类似于递归的lsstat:文件信息time:执行命令,并计算执行时间timeout:在指定时长范围内执行命令,并在规定时间结束后停止进程lockfile:使文件只能通过rm -f移除logrotate: 切换、压缩以及发送日志文件watch:重复运行同一个命令,展示结果并/或高亮有更改的部分when-changed:当检测到文件更改时执行指定命令。参阅inotifywait和entr。tac:反向输出文件shuf:文件中随机选取几行comm:一行一行的比较排序过的文件strings:从二进制文件中抽取文本tr:转换字母iconv或uconv:文本编码转换split和csplit:分割文件sponge:在写入前读取所有输入,在读取文件后再向同一文件写入时比较有用,例如grep -v something some-file | sponge some-fileunits:将一种计量单位转换为另一种等效的计量单位(参阅/usr/share/units/definitions.units)apg:随机生成密码xz:高比例的文件压缩ldd:动态库信息nm:提取 obj 文件中的符号ab或wrk:web 服务器性能分析strace:调试系统调用mtr:更好的网络调试跟踪工具cssh:可视化的并发 shellrsync:通过 ssh 或本地文件系统同步文件和文件夹ngrep:网络层的 grephost和dig:DNS 查找lsof:列出当前系统打开文件的工具以及查看端口信息dstat:系统状态查看glances:高层次的多子系统总览iostat:硬盘使用状态mpstat: CPU 使用状态vmstat: 内存使用状态htop:top 的加强版last:登入记录w:查看处于登录状态的用户id:用户/组 ID 信息sar:系统历史数据ss:套接字数据dmesg:引导及系统错误信息sysctl: 在内核运行时动态地查看和修改内核的运行参数hdparm:SATA/ATA 磁盘更改及性能分析lsblk:列出块设备信息:以树形展示你的磁盘以及磁盘分区信息lshw,lscpu,lspci,lsusb和dmidecode:查看硬件信息,包括 CPU、BIOS、RAID、显卡、USB设备等lsmod和modinfo:列出内核模块,并显示其细节fortune,ddate和sl:额,这主要取决于你是否认为蒸汽火车和莫名其妙的名人名言是否“有用”