工具类 - 终端

TOC

我今天发现Mac自带的应用程序(提示.app)(Tips.app)非常好用。有关Mac的使用都可以通过这个程序得到一些了解,配合Google 和 deepseek 也就差不多了。

我学习终端的使用也是按照Tips.app的说明进行的,结合deepseek的帮助,可以帮助我完成一些我所想要的东西,这里记录一下。

终端是什么

“终端” App 可让高级用户和开发者通过命令行界面 (CLI) 与 Mac 操作系统进行通信。你可以输入命令和脚本(称为 shell 脚本)在 Mac 上执行任务。

你也可以配合“终端”使用简单脚本来节省时间或自动执行重复的任务。当需要以快速底层编程来自动执行或处理脚本时,“终端”可让你很好地访问 UNIX 操作 shell 的输入和输出,包括 BASH 和 ZSH。

上边的内容来自tips.app, 我使用终端(terminal.app)也是出于这一点:节省时间或自动执行重复的任务。

具体到我现在的实际需求就是:

  • 将生成的简体中文歌词文件自动转成其他繁体中文的歌词文件;
  • 提取歌词文件中的纯文字部分;

这两点都已经实现了,具体在Tools - 工具类 - 文字这篇里我写了,但是并不完善,因为这两点还是需要两个命令来实现,为啥不用一个命令就都搞定呢。所以,打开终端,开始行动。

打开/退出 终端App

打开终端

  • 点按程序坞中的“启动台”图标,在搜索栏中键入“终端”,然后点按“终端”。
  • 在“访达” 中,打开“/应用程序/实用工具”文件夹,然后连按“终端”。
  • 聚焦搜索 (command ⌘ + 空格)- 输入"终端(或 terminal.app)"

弹出来的应用就是终端,类似这样的文字。

Last login: Wed Mar 19 21:22:55 on ttys000
xdl@MacBook-Air ~ % 
  • 这里的xdl是我的用户名,
  • MacBook-Air是我的主机名,
  • ~符号代表已登录用户的个人文件夹,比如我这里就是/Users/xdl;
  • %符号是提示符(Prompt),我们就是在这个%符号后边输入命令,输入完成之后就按 Return 键()。

你可以试试输入ls这两个字符,然后按

xdl@MacBook-Air ~ % ls
Applications	Documents	Library		Music		Public		my music
Desktop		Downloads	Movies		Pictures	my		python

lslist的缩写,表示列出当前文件夹下的内容。

退出终端

  • 在 Mac 上前往“终端” App ,选取“终端”>“退出终端”。
  • ⌘ Q

执行命令和运行工具

你可以交互使用命令行环境,方法是键入命令并等待结果,你也可以使用 shell 编写无需直接交互即可运行的脚本。

上边这句话的意思是:你可以自己一个个的输入命令执行,也可以是将命令(不管是一个,还是很多个)放在一个文件里,然后执行这个文件就可以了。

执行命令的时候需要加上这个命令的路径(所在文件夹的位置),除非这个命令所在的路径已经添加到了 shell 的 PATH 环境变量中,除了自己编写的,系统提供的都只需要提供命令本身的名字就可以了。自己编写的,只要把命令也放到比如/usr/local/bin/这种已经添加到PATH中的路径下,那么也可以使用的时候直接输入命令就可以了。

一个例子就是上边的ls。另外一个我觉得很有用的就是用终端打开图形界面的app,比如,我要打开文本编辑这个应用:

open -a TextEdit

就可以打开这个应用,如果要关闭它,可以:

killall TextEdit

获取命令相关的帮助

每个有关的命令,比如这里的openkillall,以及open后边的-a,都可以通过man(manual,手册,说明书的缩写)来查看详细信息,比如

man open

就会显示有关open这个命令的说明,通过鼠标上下滑动或者键盘的上下键可以查看更多,如果要退出就按一个英文状体啊下的q(表示quit)就可以了。


获取应用的英文名称

比如上边的文本编辑.app,你在终端里输入的时候这个应用名称必须是英文,中文的不行(我试过)

xdl@MacBook-Air ~ % open -a 文本编辑
Unable to find application named '文本编辑'
xdl@MacBook-Air ~ % open -a '文本编辑'
Unable to find application named '文本编辑'
xdl@MacBook-Air ~ % open -a '文本编辑.app'
Unable to find application named '文本编辑.app'

所以只能输入准确的应用英文名称:TextEdit。 这里再说一下,最好大小写清楚的写上,我试过,open命令不管大小写都可以打开,但是关闭killall是必须要准确的名称。

xdl@MacBook-Air ~ % open -a textedit
xdl@MacBook-Air ~ % open -a TextEdit
xdl@MacBook-Air ~ % killall textedit
No matching processes belonging to you were found
xdl@MacBook-Air ~ % killall TextEdit
xdl@MacBook-Air ~ % 

获取应用的英文名称的方法,就是在 应用程序 - 应用名字.app 选中该应用,然后 ⌘ I查看该应用的简介,在名称与扩展名这个项目下就是该应用准确的英文名字,比如谷歌浏览器的名字就是Google Chrome.app,微信就是WeChat.app。我们试试:

xdl@MacBook-Air ~ % open Google Chrome
The files /Users/xdl/Google and /Users/xdl/Chrome do not exist.
xdl@MacBook-Air ~ % open -a Google Chrome
The file /Users/xdl/Chrome does not exist.
xdl@MacBook-Air ~ % open -a 'Google Chrome'
xdl@MacBook-Air ~ % kill 'Google Chrome'
kill: illegal pid: Google Chrome
xdl@MacBook-Air ~ % killall 'Google Chrome'

上边的代码例子很好的展示了几点容易出错的地方:

  • 应用名字中间如果有空行,那么要用引号给围起来以便命令识别;

  • 命令输入要完整,这里的-a表示application的意思,如果没有这个参数(–flag),那么命令就会是另一种执行结果;

  • 命令要输入正确,比如 kill这个命令也可以结束应用,但是它接收的是一个应用在系统里的进程数字,我们可以这样做来看看。

    xdl@MacBook-Air ~ % open -a 'Google Chrome'                        
    xdl@MacBook-Air ~ % top
    

    注意,要退出上边的这个top也是按q就行了。

    输入top命令会打开命令行的活动监视器(当然你也可以打开图形化的这个应用),然后你会看到每个应用有自己的ID, 比如这里的Google Chrome就对应如7643,7650…之类的数字,这就表示这个应用有这些进程。

    我们也可以准确的查看:

    xdl@MacBook-Air ~ % pgrep 'Google Chrome'  
    7643
    7650
    7651
    7653
    7675
    7712
    7722
    

    返回这些数字就是这个应用对应的全部进程,所以,如果用kill这个命令,后边就跟这些数字中的一个就行(我试过,kill 一个就全没了)。

    不过这种方法需要你先知道这个应用对应的进程ID,所以如果要通过名字,就用killall,具体有啥区别可以用man去看。


重定向终端输入和输出

我们看看这个例子:

xdl@MacBook-Air ~ % pgrep 'Google Chrome' > record.txt
xdl@MacBook-Air ~ % ls
Applications	Documents	Library		Music		Public		my music	record.txt
Desktop		Downloads	Movies		Pictures	my		python
xdl@MacBook-Air ~ % cat record.txt
7806
7812
7813
7814
7833
7839
7873
xdl@MacBook-Air ~ % pgrep 'Google Chrome'             
7806
7812
7813
7814
7833
7839
7878

先说明一个,cat这个命令用来查看文件内容。

上边的>, 具体的说明是:

重定向描述
>使用一个右尖括号将命令输出重定向至文件。
<使用一个左尖括号将文件内容用作命令输入。
»使用两个右尖括号将命令中的输出附加到文件。

现在我们再来理解下上边的代码示例,一个命令的输出结果应该直接展示在屏幕上,就像pgrep 'Google Chrome' 这段,但是我们也可以把结果储存到一个文件中去 pgrep 'Google Chrome' > record.txt。当然按照上边的表格说明,因为是>, 所以这个文件的内容只会有这次的结果(意思是如果这个文件原本有内容,会被覆盖掉),如果要把结果增加到这个文件的末尾就用>>,我们可以试试:

xdl@MacBook-Air ~ % pgrep 'Google Chrome' > record.txt
xdl@MacBook-Air ~ % cat record.txt       
7806
7812
7813
7814
7833
7839
7873
xdl@MacBook-Air ~ % pgrep 'Google Chrome' >> record.txt
xdl@MacBook-Air ~ % cat record.txt                     
7806
7812
7813
7814
7833
7839
7873
7806
7812
7813
7814
7833
7839
8001

可以看到第一次是覆盖了全部内容,第二次是在第一次的基础上新增的。


错误记录

关于<, 我一开始还理解错了,我详细记录下整个过程:

  1. 首先我理解的是一个文件的内容比如:pgrep 'Google Chrome',然后我只需要把整个文件给传递进去就行了,所以我是这样做的:
xdl@MacBook-Air ~ % touch test.txt  
xdl@MacBook-Air ~ % nano test.txt
xdl@MacBook-Air ~ % < test.txt
pgrep 'Google Chrome'
xdl@MacBook-Air ~ % test.txt <
zsh: parse error near `\n'
xdl@MacBook-Air ~ % touch test.sh
xdl@MacBook-Air ~ % nano test.sh
xdl@MacBook-Air ~ % < test.sh
pgrep 'Google Chrome'
xdl@MacBook-Air ~ % test.sh <
zsh: parse error near `\n'
xdl@MacBook-Air ~ % nano test.sh
xdl@MacBook-Air ~ % < test.sh   
pgrep 'Google Chrome'
xdl@MacBook-Air ~ % test.sh <   
zsh: parse error near `\n'
xdl@MacBook-Air ~ % nano test.txt
xdl@MacBook-Air ~ % pgrep < test.txt
usage: pgrep [-Lfilnoqvx] [-d delim] [-F pidfile] [-G gid]
             [-P ppid] [-U uid] [-g pgrp] [-t tty] [-u euid]
             pattern ...
  • 我先使用touch在当前文件夹下创建了一个名为test.txt的文件;
  • 接着使用nano编辑这个文件,文件内容就是pgrep 'Google Chrome'。然后是control + X 保存,y确认,Return 键()结束。
  • 接着就是输入命令:< test.txt,可以看到只是原样输出结果(其实这里就能看出这个的用法以及输出的结果,但我没意识到),所以我改成了test.txt <,可以看到提示我解析错误,\n表示换行
  • 我以为是txt文件的原因导致了不可执行,所以将文件类型改成了sh,其他内容都一样,然后试了一遍,得到了完全一样的错误结果;
  • 所以我按照刚才说的\n换行错误又把文件的结尾空格全部删除后又都试了一遍,还是不行;
  • 所以我最后把文件内容改成:Google Chrome,然后输入命令pgrep < test.txt,结果就是终端告诉我pgrep这个命令的用法是什么,也就是说我弄的还是不对。

我把整个过程给deepseek,它告诉我:

你遇到的问题是因为对重定向符号 < 和 > 的使用方式有误解。重定向符号的作用是将文件内容作为命令的输入或将命令的输出写入文件,而不是直接执行文件中的命令。

重点就是它说的文件内容作为命令的输入,而不是直接执行文件中的命令,这一点就是我上边说的

< test.txt,可以看到只是原样输出结果(其实这里就能看出这个的用法以及输出的结果,但我没意识到)

deepseek给了很多种处理办法,有很简单的直接执行脚本之类的,不过这里我是为了学习<,所以记录下最后用<的解决办法。

  • test.txt的内容是Google Chrome

  • 终端输入:

    xargs pgrep < test.txt
    

xargs是表示带参数,如果直接pgrep < test.txt还是会告诉我用法是什么,也就是说那样写不对。

最后的结果如下:

xdl@MacBook-Air ~ % pgrep "Google Chrome"
7806
7812
7813
7814
7833
8029
8030
8044
xdl@MacBook-Air ~ % pgrep < test.txt
usage: pgrep [-Lfilnoqvx] [-d delim] [-F pidfile] [-G gid]
             [-P ppid] [-U uid] [-g pgrp] [-t tty] [-u euid]
             pattern ...
xdl@MacBook-Air ~ % xargs pgrep < test.txt
7806
7812
7813
7814
7833
8029
8030
8056

通过这个错误对<命令的输入有了一个正确的认识,其实再延伸一下,>不也就只是把输出的结果保存到文件么,那么反过来<就该是命令需要处理的内容,如果一开始就这样想也许就不会有这个错误过程了,不过解决了总是好的。

自定义终端

终端的标题,颜色,大小,字体等都可以自己进行设置。

在 Mac 上前往“终端” App 。

选取“终端”>“设置”(⌘ ,),然后点按一个设置面板:

  • 通用:设定启动窗口以及新窗口和标签页的描述文件,和选取要使用的 shell。
  • 描述文件:创建更改“终端”窗口外观的描述文件。
  • 窗口组:管理“终端”窗口组。
  • 编码:选择想要在文本编码菜单中显示的编码。

我自定义了一个描述文件,命名为my,主要就是

  • 白色背景
  • 黑色字体
  • 字体大小:15
  • 字体:Manaco(这是一个等宽字体)

另外创建了一个my-dark,主要变化就是:白色背景,黑色字体。

另外,zsh这个终端的命令输入行和输出都是同一个颜色,让我很难分辨自己输入的命令在哪里,所以我稍微改动了一下,当然网络上有很多插件,或者别的已经定义好的,不过我不想,我就想试试原生的。


自定义提示符颜色

首先创建.zshrc这个文件。

touch .zshrc

nano .zshrc

输入内容:

# 设置提示符
PROMPT='%F{green}%n@%m%f %F{green}%~%f %# '

保存并退出(在 nano 编辑器中,按 Ctrl + X,然后按 Y, 确认保存)。

使配置生效:

source ~/.zshrc

screenshot of terminal

顺带一提,这里的图片格式转换:

  • 原图 png, 642kb;
  • 新图 avif, 48kb;

用的就是之前提到的imagemagic转换的。

这样就把提示符都给变成绿色了,你当然也可以改动,这里说明一下:

%F{green} 设置颜色。

%n 是用户名,%m 是主机名,%~ 是当前目录,%# 是提示符(普通用户为 %,root 用户为 #)。


Zsh的配置文件

Zsh 在启动时会加载多个配置文件,它们的加载顺序和作用如下:

  1. /etc/zshrc:系统级别的 Zsh 配置文件,适用于所有用户。

  2. ~/.zshenv:用户级别的环境变量配置文件,每次 Zsh 启动时都会加载。

  3. ~/.zprofile:用户级别的登录配置文件,仅在登录时加载一次。

  4. ~/.zshrc:用户级别的交互式 Shell 配置文件,每次打开终端时都会加载。

  5. ~/.zlogin:用户级别的登录配置文件,在登录时加载(在 ~/.zprofile 之后)。

~/.zprofile 和 ~/.zshrc 的区别

~/.zprofile:适合放置登录时需要执行的命令,例如设置环境变量(如 PATH)。

~/.zshrc:适合放置每次打开终端时需要执行的命令,例如别名、提示符设置、插件加载等。


自定义主机名称

原本的主机名称一直显示"192.168.0.136",我不喜欢数字,想改成"MacBook-Air"。

以下是deepseek给的说明:

hostname 命令显示的内容

hostname 命令默认显示的是 本地主机名(Local HostName) 或 网络主机名(HostName),具体取决于系统的网络配置。

如果系统通过 DHCP 获取了一个网络主机名(例如 192.168.0.136),hostname 命令可能会显示这个网络主机名。

如果系统没有网络主机名,hostname 命令会显示本地主机名(Local HostName)。

现在,我决定更改这个主机名称:

sudo scutil --set HostName MacBook-Air

输入秘密啊就完成了变更,然后检查一下:

xdl@MacBook-Air ~ % scutil --get HostName
MacBook-Air

至此就改完了主机名,后期如果出现问题或者想要改回去

sudo scutil --set HostName ""

这将允许 DHCP 重新分配主机名。

自动化和脚本

你可以不用输入命令和等待响应,而是创建无需直接交互即可运行的 shell 脚本。

shell 脚本 是包含一个或多个 UNIX 命令的文本文件。运行 shell 脚本可以执行可能需要在命令行中输入的命令。

shell 脚本的好处在于你可以将多个常见任务合并在一个脚本中,从而节省时间并避免重复执行类似任务时可能产生的错误。你还可以使用 launchd 或 Apple 远程桌面等工具实现 shell 脚本自动化。

shell 脚本以表明其是 shell 脚本的字符组合 # 和 !(合称为 shebang)开头,后跟指向脚本应当随之运行的 shell 引用。例如,以下是将会随着 sh 运行的 shell 脚本的首行:

#!/bin/sh

你应当在 shell 脚本中附上注释。若要添加注释,请使用数字符号 (#) 作为行开头。每行注释均需以该数字符号开头:

#This program returns the

使用 chmod 工具来表明文本文件的可执行性(即其内容可以作为程序运行).

结合我的实际需要歌词的语言转换和提取,我就创建一个process_srt文件:

xdl@MacBook-Air ~ % cd /usr/local/bin
xdl@MacBook-Air /usr/local/bin % ls
code		github		process_srt
xdl@MacBook-Air /usr/local/bin % cat process_srt 
#!/bin/bash

# 转换简繁体
opencc -i s.srt -o t.srt -c s2t.json
opencc -i s.srt -o tw.srt -c s2tw.json
opencc -i s.srt -o hk.srt -c s2hk.json

echo "简繁体转换完成!"

# 输入文件
input_file="s.srt"
# 输出文件
output_file="s.txt"

# 清空输出文件(如果已存在)
> "$output_file"

# 逐行读取 .srt 文件
while IFS= read -r line; do
  # 跳过空行和时间戳行(时间戳行包含 -->)
  if [[ -z "$line" || "$line" == *"-->"* ]]; then
    continue
  fi
  # 跳过序号行(纯数字行)
  if [[ "$line" =~ ^[0-9]+$ ]]; then
    continue
  fi
  # 将歌词行写入输出文件
  echo "$line" >> "$output_file"
done < "$input_file"

echo "歌词已提取到 $output_file"%   

我这里是已经创建好了,所以是去查看,如果是新建,那么就和上边的一样,touch,nano.

保存好后就是赋予执行权限。

chomod +x process_srt

之后就可以直接使用了(cd到相应的文件夹下输入process_srt命令就搞定了)。

键盘快捷键

太多了,最好自己查看。

终端窗口和标签页操作快捷键

操作快捷键
新建窗口Command-N
使用相同命令新建窗口Control-Command-N
新建标签页Command-T
使用相同命令新建标签页Control-Command-T
显示或隐藏标签页栏Shift-Command-T
显示所有标签页或退出标签页概览Shift-Command-反斜杠 (\)
新建命令Shift-Command-N
新建远程连接Shift-Command-K
显示或隐藏检查器Command-I
编辑标题Shift-Command-I
编辑背景颜色Option-Command-I
放大字体Command-加号键 (+)
缩小字体Command-减号键 (–)
下一个窗口Command-重音符键 (`)
上一个窗口Command-Shift-波浪符号 (~)
下一个标签页Control-Tab
上一个标签页Control-Shift-Tab
将窗口拆分为两个面板Command-D
关闭拆分面板Shift-Command-D
关闭标签页Command-W
关闭窗口Shift-Command-W
关闭其他标签页Option-Command-W
全部关闭Option-Shift-Command-W
滚动到顶部Command-Home
滚动到底部Command-End
上一页Command-Page Up
下一页Command-Page Down
上一行Option-Command-Page Up
下一行Option-Command-Page Down

编辑命令行操作快捷键

操作快捷键
重新定位插入点在按住 Option 键的同时将指针移到新的插入点
将插入点移到行的开头Control-A
将插入点移到行的结尾Control-E
将插入点前移一个字符右箭头键
将插入点后移一个字符左箭头键
将插入点前移一个字词Option-右箭头键
将插入点后移一个字词Option-左箭头键
删除行Control-U
删除到行的结尾Control-K
向前删除到字词的结尾Option-D(选中将 Option 键用作 Meta 键后可用)
向后删除到字词的开头Control-W
删除一个字符Delete
向前删除一个字符向前删除(或使用 Fn-Delete)
转置两个字符Control-T

在“终端”窗口中选择和查找文本操作快捷键

操作快捷键
选择完整文件路径按住 Shift-Command 键并连按路径
选择整行文本点按该行三下
选择一个词连按该词
选择 URL按住 Shift-Command 键并连按 URL
选择矩形块按住 Option 键并拖移来选择文本
剪切Command-X
拷贝Command-C
不带背景颜色拷贝Control-Shift-Command-C
拷贝纯文本Option-Shift-Command-C
粘贴Command-V
粘贴所选内容Shift-Command-V
粘贴转义文本Control-Command-V
粘贴转义的所选内容Control-Shift-Command-V
查找Command-F
查找下一个Command-G
查找上一个Command-Shift-G
使用选定的文本查找Command-E
跳到选定的文本Command-J
全选Command-A
打开字符检视器Control-Command-Space

使用标记和书签操作快捷键

操作快捷键
标记Command-U
标记为书签Option-Command-U
取消标记Shift-Command-U
标记命令行并发送返回结果Command-Return
发送返回结果但不标记Shift-Command-Return
插入书签Shift-Command-M
插入包含名称的书签Option-Shift-Command-M
跳到上一个标记Command-上箭头键
跳到下一个标记Command-下箭头键
跳到上一个书签Option-Command-上箭头键
跳到下一个书签Option-Command-下箭头键
清除到上一个标记Command-L
清除到上一个书签Option-Command-L
清除到开头Command-K
在标记之间选择Shift-Command-A

其他快捷键

操作快捷键
进入或退出全屏幕Control-Command-F
显示或隐藏颜色Shift-Command-C
打开“终端”设置Command-逗号键 (,)
中断键入 Command-句点键 (.) 等于在命令行上输入 Control-C
打印Command-P
软重置终端仿真器状态Option-Command-R
硬重置终端仿真器状态Control-Option-Command-R
打开 URL按住 Command 键并连按 URL
添加至文件的完整路径从“访达”将文件拖移到“终端”窗口中
将文本导出为Command-S
将选定的文本导出为Shift-Command-S
反向搜索命令历史Control-R
开关“允许鼠标报告”选项Command-R
开关“将 Option 键用作 Meta 键”选项Command-Option-O
显示备用屏幕Shift-Command-下箭头键
隐藏备用屏幕Shift-Command-上箭头键
打开所选内容的 man 页面Control-Shift-Command-问号键 (?)
搜索所选内容的 man 页面索引Control-Option-Command-斜杠 (/)
完整的目录或文件名称在命令行上,键入一个或多个字符,然后按下 Tab 键
显示可能的目录或文件名称补全列表在命令行上,键入一个或多个字符,然后按下 Tab 键两次