在前文中我阐释了命令行(Command Line, 简称 CLI)对比用户图形界面(Graphic User Interface, 简称 GUI)的优势,在 v2ex 等渠道收到了一些有益的反馈,在文章开头简短的回应。
首先针对 CLI 对比 GUI 是否具有我说的优势,这是个见仁见智的问题,但从我的经历和体会来讲,以下几个场景 CLI 具有优势。
(1)需要经常切换开发环境,办公电脑,个人电脑,他人电脑。这种场景下使用现成的 CLI 能够避免下载安装应用带来时间上和空间上的消耗。
(2)一些简单但重复的操作,CLI 可以覆盖 Automator 无法实现的一些功能。
其次针对前两篇尤其第二篇内容比较基础的问题。写开发心得体会的直接目的是对我的工作和学习做个总结,方便回顾和反思;也额外考虑到对于很多不常接触 CLI 的用户而言,陌生的命令像一个黑匣子,不少小白都被 sudo rm -rf / 这个恶作剧搞崩溃过,为了大家尽快熟悉 CLI ,我针对之前发布的命令都附带了简要的描述。
本文接下来主要涉及三个内容,1) 前两篇文章遗漏的同类命令 2) 命令行相关基础 3) 针对一个综合的命令逐行解释。CLI 操作覆盖了 macOS 系统的方方面面,包含此篇在内的前三篇介绍的都是比较基础的命令,lldb, make, lipo, awk, sed, grep 等等开发息息相关的命令由于功能比较庞大,后续会展开篇幅通过专门的文章介绍,请各位读者耐心等候。
一、遗漏的命令
(1)系统相关命令
之前的文章连系统内核版本和状态的命令都列举了,居然遗漏了当前硬件架构的命令,实在很不应该,这个命令在交叉编译等场景下非常有用,获取当前系统准确的硬件架构是交叉编译重要的步骤。
1
2
# 显示当前硬件架构
machine
1
2
# 显示命令记录
history
1
2
# 设置命令别名
alias ll="ls -l"
(2)文件操作命令
由于文件操作相关命令很多,之前的文章只列举了最基础的增删改查等,本文再做一些补充。
1
2
3
# 创建文件, Make File 的缩写
# 可以跟 -n 参数设置空文件大小, 单位可选 b/k/m/g, 默认为 b
mkfile FILENAME
1
2
3
4
# 查明文件类型, 如
# 输入: file 226.pdf
# 输出: 226.pdf: PDF document, version 1.4
file FILENAME
1
2
3
# 对输入的行去重, Unique 的缩写
# 通常用于管道操作, 追加 -i 忽略大小写
uniq
1
2
3
# 对输入的行排序
# 通常用于管道操作, 追加 -r 为逆序排列
sort
1
2
3
# 对输入的行裁剪
# 通常用于管道操作, 追加 -d 指定分隔符
cut
1
2
3
# 统计文件的字符/单词/行/字节 数, 默认统计除字符数外三项
# -c 字节数, -l 行数, -m 字符数, -w 单词数
wc FILENAME
1
2
3
4
# 递归查找文件
# 用法比较多, 详情 man find 查看, 本文不做展开
# 如, 查找所有文件并打印: find / -type f -exec echo {} \;
find DIRECTORY EXPRESSION
1
2
3
# 快速查找文件
# 需要建立索引数据库, 使用后会提示如何创建
locate PATERN
二、命令行基础
这部分是对我所学内容的总结,可能不够系统和准确,希望各位读者发现问题或者存在疑惑后可以通过留言等方式反馈。
在前面的文章中我引入了一个问题,Terminal, Shell, TTY 和 Console 有什么区别,理解了 Shell 是我们的命令行解释程序之后,我们来学习更多相关的基础知识。
(1)显示当前 Shell 和更新配置文件
我们可以通过 echo $SHELL 打印出当前使用的 Shell, 以 $ 符号开头的内容表示这是一个变量,这个变量之所以能够被访问到,是因为它被设置为当前的环境变量,我们可以通过 env | grep SHELL 印证。
前面提到过,source FILENAME 可以执行文件以更新配置文件,它和 sh FILENAME 的区别在于 source 之后的变量会暴露给当前命令行。通常对于 macOS, 环境变量的配置文件位于当前用户家目录,ls -a ~/ 可以查看家目录下的文件,加上 -a 可以显示包括隐藏文件在内的全部文件,Bash 的配置文件为 .bashrc ,Zsh 的配置文件为 .zshrc 。
(2)重定向
在 Shell 编程中,我们有时需要将前面处理的结果作为输入或输出传递给下一个程序,这时就要用到重定向。
前面的例子中 ls > FILENAME 是指列举文件作为输入,创建 FILENAME 或覆盖 FILENAME, 如果将 > 替换为 » 则将输出追加到文件。反之 < 则是作为输入,«EOF 是指将后续输入结束符之前的内容作为输入。
常见的 >/dev/null 是指将输出重定向到 /dev/null,通常用于避免命令行输出结果。还有一种常见使用方式 2>&1,这里的 2 是文件描述符之一,2 表示标准错误输出,1 表示标准输出,0 表示标准输入,2>&1 表示将标准错误输出重定向到标准输出,即错误和正常输出一视同仁。
(3)组合命令
在组合命令中,主要有四个连接符。
1)管道|
1
2
# 将命令1的输出作为命令2的输入
COMMAND1 | COMMAND2
2)分号;
1
2
# 执行完命令1之后执行命令2,即使命令1失败也会执行命令2
COMMAND1; COMMAND2
3)逻辑与&&
1
2
# 执行完命令1之后执行命令2,命令1失败则不会执行命令2
COMMAND1 && COMMAND2
4)逻辑或||
1
2
# 如果命令1执行失败,则执行命令2
COMMAND1 || COMMAND2
需要注意,可以通过()或者{}包围的方式组合命令使用以上连接符。除了将输出作为输入之外,前一命令的输入也可以作为参数传递给下一命令,这里可以通过 xargs 传递,默认情况下 xargs 会在空格处中断输入,而且得到的每个标记变成一个参数。但是,在 xargs 构建命令时,它会一次传递尽可能多的参数。您可以使用 -n或 –max-args 参数覆盖此行为。
三、命令逐行解释
我选取了在本系列文章第一篇出现过的查询系统 DNS 设置的命令,搜索引擎显示的方案运行结果不符合预期,以下为我编写的 Shell 脚本。
1
2
3
4
5
6
7
8
9
#! /bin/bash
# list all network services
IN=$(networksetup -listallnetworkservices | awk '{if (NR>1) print $0 ";"}');
while IFS=";" read -ra SERVICES; do
for i in "${SERVICES[@]}"; do
echo "${i} DNS Servers:"
networksetup -getdnsservers "${i}"
done
done <<< "$IN"
第一行表示如果该文件有执行权限,直接执行时采用的打开方式,Shell 脚本通常使用 /bin/bash 。
第二行的内容表明这是一段注释。
第三行,IN=是一个赋值语句,语句后半部分$()包围的内容表明这是一段赋值的内容为括号内的执行结果。括号内,管道连接符之前的部分表明列举所有网络连接服务,输出的第一行并非网络连接服务所以添加 NR>1 的条件表明跳过第一行,print $0 表明打印所有参数,后面追加了分号后续使用。
第四行和第九行表明这是一个循环,将第三行的 IN 作为输入,以分号作为分隔符,读取输入作为 SERVICES 变量。之所以以分号为分隔符,是因为网络连接服务中有 iPhone USB 这种包含空格的,而默认分隔符为空格会将其分为两个输入,单独查询 iPhone 这个不存在的网络连接服务 DNS 会报错。
第五六七八行内容比较简单,遍历 SERVICES,打印服务名称和提示语句,然后获取它的 DNS 设置,以下附图用于验证结果。
转载请注明原始出处 macOS 好用的命令行(3) © 晨晓 | Chinsyo