idea .gitignore(git文件忽略)

idea使用git通常需要忽略一些临时文件,需要配置.gitignore插件

  1. 安装插件
    File -> Settings -> Plugins 搜索框搜索.ignore,点击安装

  2. 生成初始.ignore文件

出现如下弹框,会默认生成所选语言的常用忽略项,我这里选java,

img

生成如下文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*

idea项目一般需要自己增加如下两项

1
2
.idea/
target/

即忽略这两个文件夹即文件夹下的所有文件

现在可以使用了,提交一次测试下

发现.idea文件夹下的文件还有变更被提交,这是因为在使用gitignore之前,此文件就以及被跟踪了,这样的话需要移除跟踪,如下命令:

移除指定文件夹即文件夹下所有文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
git rm --cached --force -r .idea 
rm '.idea/artifacts/xxx_api_war.xml'
rm '.idea/artifacts/xxx_api_war_exploded.xml'
rm '.idea/artifacts/xxx_task_war.xml'
rm '.idea/artifacts/xxx_task_war_exploded.xml'
rm '.idea/libraries/Maven__ch_qos_logback_logback_classic_1_1_7.xml'
rm '.idea/libraries/Maven__ch_qos_logback_logback_core_1_1_7.xml'
rm '.idea/libraries/Maven__com_alibaba_druid_1_0_22.xml'
rm '.idea/libraries/Maven__com_alibaba_fastjson_1_2_14.xml'
rm '.idea/libraries/Maven__com_aliyun_openservices_ons_client_1_2_4.xml'
rm '.idea/libraries/Maven__com_fasterxml_aalto_xml_0_9_11.xml'
rm '.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_5_2.xml'
rm '.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_5_2.xml'
rm '.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_5_2.xml'
rm '.idea/libraries/Maven__com_fasterxml_jackson_dataformat_jackson_dataformat_xml_2_5_2.xml'
rm '.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_jaxb_annotations_2_5_2.xml'
rm '.idea/libraries/Maven__com_google_guava_guava_19_0.xml'
rm '.idea/libraries/Maven__commons_codec_commons_codec_1_9.xml'
rm '.idea/libraries/Maven__commons_httpclient_commons_httpclient_3_1.xml'
rm '.idea/libraries/Maven__commons_logging_commons_logging_1_0_4.xml'
rm '.idea/libraries/Maven__commons_logging_commons_logging_1_2.xml'
rm '.idea/libraries/Maven__javax_servlet_javax_servlet_api_3_1_0.xml'
rm '.idea/libraries/Maven__mysql_mysql_connector_java_5_1_39.xml'
rm '.idea/libraries/Maven__org_apache_commons_commons_lang3_3_3_2.xml'
rm '.idea/libraries/Maven__org_apache_commons_commons_pool2_2_0.xml'
rm '.idea/libraries/Maven__org_apache_httpcomponents_httpclient_4_4.xml'
rm '.idea/libraries/Maven__org_apache_httpcomponents_httpcore_4_4.xml'
rm '.idea/libraries/Maven__org_aspectj_aspectjweaver_1_8_9.xml'
rm '.idea/libraries/Maven__org_codehaus_woodstox_stax2_api_3_1_4.xml'
rm '.idea/libraries/Maven__org_mongodb_mongo_java_driver_2_14_0.xml'
rm '.idea/libraries/Maven__org_mybatis_mybatis_3_4_1.xml'
rm '.idea/libraries/Maven__org_mybatis_mybatis_spring_1_3_0.xml'
rm '.idea/libraries/Maven__org_quartz_scheduler_quartz_2_2_2.xml'
rm '.idea/libraries/Maven__org_slf4j_jcl_over_slf4j_1_7_21.xml'
rm '.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_21.xml'
rm '.idea/libraries/Maven__org_springframework_data_spring_data_commons_1_12_2_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_data_spring_data_mongodb_1_9_2_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_aop_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_aspects_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_beans_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_context_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_context_support_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_core_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_expression_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_jdbc_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_tx_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_web_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__org_springframework_spring_webmvc_4_3_1_RELEASE.xml'
rm '.idea/libraries/Maven__redis_clients_jedis_2_6_2.xml'

移除指定文件:

1
2
git rm --cached --force ydq-api/ydq-api.iml
rm 'ydq-api/ydq-api.iml'

这样,表示移除成功。

现在,上面的操作进行提交:

img

移交移除文件.png

以后再做一些变更,当再次提交时,只有未被忽略的(被忽略的文件的变更再也不会被提交了)修改的文件了。

现在,idea下配置.gitignore结束。

注:
.gitignore只能忽略那些原来没有被track的文件,如果某些文件已经被纳入了版本管理中,则修改.gitignore是无效的。那么解决方法就是先把本地缓存删除(改变成未track状态),然后再提交:
输入:
git rm -r –cached filePath
git commit -m “remove xx”
或者:
git rm -r –cached .
git add .
git commit -m “update .gitignore”

来解释下几个参数 -r 是删除文件夹及其子目录 –cached 是删除暂存区里的文件而不删除工作区里的文件,第一种是删除某个文件,第二种方法就把所有暂存区里的文件删了,再加一遍,相当于更新了一遍。

l

git 流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
正常上线
git clone

git checkout dev

git checkout -b feature-function

git add .
git commit -m '结束功能开发'

git checkout dev
git merge feature-function
git push origin dev

git checkout -b release-2019-09-29
git push origin release-2019-09-29

线上发现bug 紧急上线

git checkout -b hotfix-2019-09-29 origin/master
git checkout -b hotfix-2019-09-29
git add .
git commit -m 'message'
git push origin hotfix-2019-09-29

git checkout dev
git merge hotfix-2019-09-29
git push origin dev


git branch -d feature-function
l

mac快捷键

图形按键

按键或组合键 功能
Command 键
Control 键
Option 键
Shift 键
Caps Lock
fn 功能键常用快捷键

剪切、拷贝和粘贴

您可以在大多数 app 中使用这些快捷键来剪切、拷贝或粘贴选中的项目。其中包括文本、图片、音乐等等。您甚至可以在 Finder 中拷贝和粘贴文件,来将文件拷贝到新位置。

按键或组合键 功能
Command-C 将所选数据拷贝到剪贴板
Command-X 移除选中的项目,然后将副本放在剪贴板中
Command-V 将剪贴板副本放到(粘贴)到当前文稿或 app 中

拍摄屏幕快照

使用这些快捷键可拍摄一张屏幕内容的照片。您也可以使用 Grab 拍摄屏幕快照,这款 app 位于“实用工具”文件夹中。

按键或组合键 功能
Command-Shift-3 将屏幕捕捉到文件
Command-Shift-Control-3 将屏幕内容捕捉到剪贴板
Command-Shift-4 将所选屏幕内容捕捉到一个文件,或按空格键仅捕捉一个窗口
Command-Shift-Control-4 将所选屏幕内容捕捉到剪贴板,或按空格键仅捕捉一个窗口

启动快捷键

使用这些组合键可更改电脑的启动方式。启动 Mac 后立即按住相关按键或组合键,直到所需的功能出现。例如,启动时按住 Option 键,直到“启动管理器”出现。
注:如果您使用的不是 Apple 制造的键盘,则 Alt 键通常相当于 Option 键。如果此修饰键不起作用,请尝试改用 Apple 键盘。

按键或组合键 功能
Option 或 Alt 显示所有启动宗卷(启动管理器)
Shift 在安全模式下启动
C 从可引导介质(DVD、CD、USB 闪存驱动器)启动
T 在目标磁盘模式下启动
N 从 NetBoot 服务器启动
X 强制 OS X 启动(如果非 OS X 启动宗卷可用)
D 使用 Apple Hardware Test
Command-R 使用 OS X 恢复功能(OS X Lion 或更高版本)
Command-Option-R 在受支持的电脑上使用互联网恢复
Command-V 以详细模式启动
Command-S 以单用户模式启动
Command-Option-P-R 重置 NVRAM
按住介质推出 (⏏) 键、F12 键、鼠标或触控板按钮 推出活动光盘

睡眠、关闭和注销快捷键

Mac 启动后使用这些组合键可让电脑进入睡眠状态、关机、注销或重新启动。

按键或组合键 功能
电源按钮 轻按可开机。在通电后,轻按“电源”按钮可使您的 Mac 唤醒或进入睡眠状态。
按住电源按钮 1.5 秒 显示重新启动/睡眠/关闭对话框
按住电源按钮 5 秒 强制 Mac 关机
Control-电源按钮 显示重新启动/睡眠/关闭对话框
Command-Control-电源按钮 强制 Mac 重新启动
Command-Option-电源按钮 使电脑进入睡眠状态
Command-Control-电源按钮 退出所有 app(会让您先存储对已打开文稿所作的更改),然后重新启动电脑
Command-Option-Control-电源按钮 退出所有 app(会让您先存储对已打开文稿所作的更改),然后关闭电脑
Shift-Control-电源按钮 使所有显示器进入睡眠状态
Command-Shift-Q 注销
Command-Shift-Option-Q 立即注销

App 快捷键

这些键盘快捷键适用于大部分 app。

组合键 功能
Command-A 在最前面的窗口中选择所有项目或文本
Command-Z 撤消上一命令(有些 app 可让您撤消多次)
Command-Shift-Z 重做,恢复上次使用撤消进行的更改(有些 app 可让您重做多次)
Command-空格键 显示或隐藏 Spotlight 搜索栏(如果同时使用多语种,此快捷键可能会转而循环显示启用的脚本系统)
Command-Option-空格键 显示 Spotlight 搜索结果窗口(如果安装了多语种,则可能循环显示某一脚本中的键盘布局和输入法)
Command-Tab 在打开的 app 列表中,向下移动到下一个最近使用的 app
Option-介质推出键 (⏏) 从备选光盘驱动器中推出(如果已安装)
Command-调低亮度 (F1) 切换针对多显示器配置的“镜像显示器”
Command-调高亮度 (F2) 切换目标显示器模式
Command-Mission Control (F3) 显示桌面
Command-F5 切换 VoiceOver 的开关
Option-亮度 (F2) 打开“显示器”系统偏好设置
Option-Mission Control (F3) 打开“Mission Control”偏好设置
Option-音量键 (F12) 打开“声音”偏好设置
Command-减号 (–) 缩小所选项
Command-冒号 (:) 显示“拼写和语法”窗口
Command-分号 (;) 查找文稿中拼写错误的词
Command-逗号 (,) 打开最前面的 app 的偏好设置窗口
Command-问号 (?) 打开“帮助”菜单
Command-加号 (+)或 Command-Shift-等号 (=) 放大所选项
Command-Option-D 显示或隐藏 Dock
Command-Control-D 显示或隐藏选中字词的定义
Command-D 在“打开和存储”对话框中选择“桌面”文件夹
Command-Delete 选择包含“删除”或“不存储”按钮的对话框中的“不存储”
Command-E 使用所选内容进行查找
Command-F 打开“查找”窗口,或在文稿中查找文本
Command-Option-F 移到搜索栏控件
Command-G 查找所选内容出现的下一个位置
Command-Shift-G 查找所选内容出现的上一个位置
Command-H 隐藏当前正在运行的 app 的窗口
Command-Option-H 隐藏所有其他正在运行的 app 的窗口
Command-Option-I 显示检查器窗口
Command-M 将活跃窗口最小化至 Dock
Command-Option-M 将处于活动状态的 app 的所有窗口最小化至 Dock 中
Command-N 在最前面的 app 中创建新文稿
Command-O 显示一个对话框,用于选择要在最前面的 app 中打开的文稿
Command-P 打印当前文稿
Command-Shift-P 显示用于指定文稿参数(页面设置)的对话框
Command-Q 退出最前面的 app
Command-S 存储活跃文稿
Command-Shift-S 显示“存储为”对话框或复制当前文稿
Command-Option-T 显示或隐藏工具栏
Command-W 关闭最前面的窗口
Command-Option-W 关闭当前 app 中的所有窗口
Command-Z 撤消上一命令(有些 app 允许多次撤消)
Command-Shift-Z 重做,恢复上次使用撤消进行的更改(有些 app 可让您重做多次)
Command-Option-esc 选择要强制退出的 app
Command-Shift-Option-Esc(按住三秒钟) 强制退出最前面的 app

对文本进行处理

编辑某个字段或文稿中的文本时可使用这些组合键。

组合键 功能
Command-B 以粗体显示所选文本或切换文本粗体显示开/关
Command-I 以斜体显示所选文本或切换文本斜体显示开/关
Command-U 对所选文本加下划线或打开/关闭加下划线功能
Command-T 显示或隐藏“字体”窗口
fn-Delete 向前删除(适用于便携式 Mac 的内建键盘)
fn-上箭头 向上滚动一页(相当于 Page Up 键)
fn-下箭头 向下滚动一页(相当于 Page Down 键)
fn-左箭头 滚动至文稿开头(相当于 Home 键)
fn-右箭头 滚动至文稿末尾(相当于 End 键)
Command-右箭头 将文本插入点移至当前行的行尾
Command-左箭头 将文本插入点移至当前行的行首
Command-下箭头 将文本插入点移至文稿末尾
Command-上箭头 将文本插入点移至文稿开头
Option-右箭头 将文本插入点移至下一个单词的末尾
Option-左箭头 将文本插入点移至上一个单词的开头
Option-Delete 删除光标左侧的词,以及词后的任何空格或标点符号
Command-Shift-右箭头 选中插入点与当前行行尾之间的文本 (*)
Command-Shift-左箭头 选中插入点与当前行行首之间的文本 (*)
Command-Shift-上箭头 选中插入点与文稿开头之间的文本 (*)
Command-Shift-下箭头 选中插入点与文稿末尾之间的文本 (*)
Shift-左箭头 将文本选择范围向左扩展一个字符 (*)
Shift-右箭头 将文本选择范围向右扩展一个字符 (*)
Shift-上箭头 将文本选择范围扩展到上一行相同水平位置的最近字符边缘 (*)
Shift-下箭头 将文本选择范围扩展到下一行相同水平位置的最近字符边缘 (*)
Shift-Option-右箭头 将文本选择范围扩展到当前词的词尾,再按一次则扩展到后一词的词尾 (*)
Shift-Option-左箭头 将文本选择范围扩展到当前词的词首,再按一次则扩展到后一词的词首 (*)
Shift-Option-下箭头 将文本选择范围扩展到当前段落的段尾,再按一次则扩展到下一段落的结尾 (*)
Shift-Option-上箭头 将文本选择范围扩展到当前段落的段首,再按一次则扩展到下一段落的段首 (*)
Control-A 移至行或段落的开头
Control-B 向后移动一个字符
Control-D 删除光标前的字符
Control-E 移至行或段落的开头
Control-F 向前移动一个字符
Control-H 删除光标后的字符
Control-K 删除从光标前的字符到行或段落末尾的所有内容
Control-L 将光标或所选内容置于可见区域中央
Control-N 下移一行
Control-O 在光标后插入一行
Control-P 上移一行
Control-T 调换光标前后的字符
Control-V 下移
Command-{ 使所选内容左对齐
Command-} 使所选内容右对齐
Command- 使所选内容居中对齐
Command-Option-C 拷贝所选项的格式设置并存储到剪贴板
Command-Option-V 将某对象的样式应用于所选对象(粘贴样式)
Command-Shift-Option-V 将周围文本的样式应用于所插入对象(粘贴并匹配样式)
Command-Control-V 将格式设置应用于所选对象(粘贴标尺)

辅助功能

这些键盘快捷键可帮助您通过键盘或辅助设备控制 Mac。这些快捷键中,有些快捷键需要在“系统偏好设置”的“键盘”面板或“辅助功能”面板中启用。

组合键 功能
Command-Option-F5 显示辅助功能选项
Command-F5 或 fn-Command-F5 打开或关闭 VoiceOver
Option-Control-F8 或 fn-Option-Control-F8 打开 VoiceOver 实用工具(如果 VoiceOver 已打开)
Command-Option-8 打开或关闭缩放功能
Command-Option-加号 (+) 放大
Command-Option-减号 (–) 缩小
Command-Option-Control-8 反转/复原屏幕颜色
Command-Option-Control-逗号 (,) 降低对比度
Command-Option-Control-句点 (.) 增强对比度

全键盘控制

通过全键盘控制,您可以使用键盘进行导航,并与屏幕上的项目进行交互。使用这些快捷键可选择并调整文本栏和滑块等控件。按 Control-F7 或从“系统偏好设置”中“键盘”偏好设置的“快捷键”面板中选择“全键盘控制”,可以切换此设置开/关。

组合键 功能
Tab 移至下一个控件
Shift-Tab 移至上一个控件
Control-Tab 已选择文本栏时移至下一个控件
Shift-Control-Tab 将焦点移至上一组控件
箭头键 移至列表、标签组或菜单中的相邻项,或移动滑块和调节器(用于增加和减小值的垂直上下箭头)
Control-箭头键 移至与文本栏相邻的控件
空格键 选择高亮显示的菜单项
Return 或 Enter 键 点按默认按钮或执行默认操作
Esc 键 点按“取消”按钮,或关闭菜单而不选取项目
Shift-Control-F6 将焦点移至上一个面板
Control-F7 临时覆盖窗口和对话框中的当前键盘访问模式
Control-F8 移到菜单栏中的状态菜单
Command-重音符 (`) 激活最前面的 app 中下一个打开的窗口
Command-Shift-重音符 (`) 激活最前面的 app 中上一个打开的窗口
Command-Option-重音符 (`) 将焦点移至窗口抽屉

您可以在菜单栏中浏览菜单,而无需使用鼠标或触控板。若要将焦点置于菜单栏中,请按 Control-F2(便携式键盘上的 fn-Control-F2)。然后使用下列组合键。

组合键 功能
左箭头和右箭头 从一个菜单移到另一个菜单
Return 打开所选菜单
上箭头和下箭头 移至所选菜单中的菜单项
键入菜单项的名称 跳到所选菜单中的菜单项
Return 选择菜单项

辅助功能 - 鼠标键

在“辅助功能”偏好设置中打开“鼠标键后,您可以使用键盘或数字小键盘上的按键移动鼠标指针。

组合键 功能
8 或数字小键盘上的 8 上移
K 或数字小键盘上的 2 下移
U 或数字小键盘上的 4 左移
O 或数字小键盘上的 6 右移
J 或数字小键盘上的 1 沿对角线向左下角移
L 或数字小键盘上的 3 沿对角线向右下角移
7 或数字小键盘上的 7 沿对角线向左上角移
9 或数字小键盘上的 9 沿对角线向右上角移
I 或数字小键盘上的 5 按鼠标按钮
M 或数字小键盘上的 0 按住鼠标按钮
.(句点键) 释放按住的鼠标按钮

Finder 快捷键

组合键 功能
Command-A 选择最前面的窗口中的所有文件
Command-Option-A 取消选择所有项
Command-C 拷贝选中的文件,然后使用“粘贴”或“移动”来移动这些文件。
Command-D 重复选中的文件
Command-E 推出
Command-F 查找任何匹配 Spotlight 属性的内容
Command-I 显示选中的文件的“显示简介”窗口
Command-Shift-C 打开“电脑”窗口
Command-Shift-D 打开“桌面”文件夹
Command-Shift-F 显示“我的所有文件”窗口
Command-Shift-G 前往文件夹
Command-Shift-H 打开当前已登录用户帐户的个人文件夹
Command-Shift-I 打开 iCloud Drive
Command-Shift-K 打开“网络”窗口
Command-Shift-L 打开“下载”文件夹
Command-Shift-O 打开“文稿”文件夹
Command-Shift-R 打开 AirDrop 窗口
Command-Shift-U 打开“实用工具”文件夹
Command-Control-T 添加到边栏 (OS X Mavericks)
Command-Option-I 显示或隐藏“检查器”窗口
Command-Control-I 获得摘要信息
Command-Option-P 隐藏或显示路径栏
Command-Option-S 隐藏或显示边栏
Command-正斜线 (/) 隐藏或显示状态栏
Command-J 调出“显示”选项
Command-K 连接到服务器
Command-L 为所选项制作替身
Command-N 新建 Finder 窗口
Command-Shift-N 新建文件夹
Command-Option-N 新建智能文件夹
Command-O 打开所选项
Command-R 显示(替身的)原身
Command-T 在当前 Finder窗口中打开单个标签时显示或隐藏标签栏
Command-Shift-T 显示或隐藏 Finder 标签
Command-Option-T 在当前 Finder窗口中打开单个标签时显示或隐藏工具栏
Command-V 将您放在剪贴板上的文本副本粘贴到当前位置。
Command-Option-V 将您放在剪贴板上的文本从其原始位置移动到当前位置。
Command-Option-Y 查看选中文件的快速查看幻灯片。
Command-1 以图标显示
Command-2 以列表方式显示
Command-3 以分栏方式显示
Command-4 以 Cover Flow 方式显示(Mac OS X v10.5 或更高版本)
Command-逗号 (,) 打开 Finder 偏好设置
Command-左中括号 ([) 前往上一文件夹
Command-右中括号 (]) 前往下一文件夹
Command-Control-上箭头 在新窗口中打开包含当前文件夹的文件夹
Command-下箭头 打开高亮显示的项目
右箭头(以列表视图显示) 打开所选文件夹
左箭头(以列表视图显示) 关闭所选文件夹
Option-点按显示三角形 (以列表视图显示)打开所选文件夹内的所有文件夹
Option-连按 在单独窗口中打开文件夹,并关闭当前窗口
Command-连按 在单独标签或窗口中打开文件夹
Command-点按窗口标题 查看包含当前窗口的文件夹
Command-Delete 移至废纸篓
Command-Shift-Delete 清倒废纸篓
Command-Shift-Option-Delete 清倒废纸篓(不显示确认对话框)
空格键(或 Command-Y) 快速查看选中的文件
拖移时按 Command 键 将拖移的项目移至其他宗卷或位置(按住按键时指针会改变)
拖移时按 Option键 拷贝拖移的项目(按住按键时指针会改变)
拖移时按 Command-Option 组合键 对拖移的项目赋予别名(按住按键时指针会改变)
l

explain结果每个字段的含义说明

我们都知道用explain xxx分析sql语句的性能,但是具体从explain的结果怎么分析性能以及每个字段的含义你清楚吗?这里我做下总结记录,也是供自己以后参考。

  • 首先需要注意:MYSQL 5.6.3以前只能EXPLAIN SELECT; MYSQL5.6.3以后就可以EXPLAIN SELECT,UPDATE,DELETE

explain结果示例:

1
2
3
4
5
6
7
mysql> explain select * from staff;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | staff | ALL | NULL | NULL | NULL | NULL | 2 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set

先上一个官方文档表格的中文版:

Column 含义
id 查询序号
select_type 查询类型
table 表名
partitions 匹配的分区
type join类型
prossible_keys 可能会选择的索引
key 实际选择的索引
key_len 索引的长度
ref 与索引作比较的列
rows 要检索的行数(估算值)
filtered 查询条件过滤的行数的百分比
Extra 额外信息

这是explain结果的各个字段,分别解释下含义:

1. id

SQL查询中的序列号。

id列数字越大越先执行,如果说数字一样大,那么就从上往下依次执行。

2. select_type

查询的类型,可以是下表的任何一种类型:

select_type 类型说明
SIMPLE 简单SELECT(不使用UNION或子查询)
PRIMARY 最外层的SELECT
UNION UNION中第二个或之后的SELECT语句
DEPENDENT UNION UNION中第二个或之后的SELECT语句取决于外面的查询
UNION RESULT UNION的结果
SUBQUERY 子查询中的第一个SELECT
DEPENDENT SUBQUERY 子查询中的第一个SELECT, 取决于外面的查询
DERIVED 衍生表(FROM子句中的子查询)
MATERIALIZED 物化子查询
UNCACHEABLE SUBQUERY 结果集无法缓存的子查询,必须重新评估外部查询的每一行
UNCACHEABLE UNION UNION中第二个或之后的SELECT,属于无法缓存的子查询

DEPENDENT 意味着使用了关联子查询。

3. table

查询的表名。不一定是实际存在的表名。
可以为如下的值:

  • <unionM,N>: 引用id为M和N UNION后的结果。
  • : 引用id为N的结果派生出的表。派生表可以是一个结果集,例如派生自FROM中子查询的结果。
  • : 引用id为N的子查询结果物化得到的表。即生成一个临时表保存子查询的结果。

4. type(重要)

这是最重要的字段之一,显示查询使用了何种类型。从最好到最差的连接类型依次为:

system,const,eq_ref,ref,fulltext,ref_or_null,index_merge,unique_subquery,index_subquery,range,index,ALL

除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一个索引。

  • 1、system

表中只有一行数据或者是空表,这是const类型的一个特例。且只能用于myisam和memory表。如果是Innodb引擎表,type列在这个情况通常都是all或者index

  • 2、const

最多只有一行记录匹配。当联合主键或唯一索引的所有字段跟常量值比较时,join类型为const。其他数据库也叫做唯一索引扫描

  • 3、eq_ref

多表join时,对于来自前面表的每一行,在当前表中只能找到一行。这可能是除了system和const之外最好的类型。当主键或唯一非NULL索引的所有字段都被用作join联接时会使用此类型。

eq_ref可用于使用’=’操作符作比较的索引列。比较的值可以是常量,也可以是使用在此表之前读取的表的列的表达式。

相对于下面的ref区别就是它使用的唯一索引,即主键或唯一索引,而ref使用的是非唯一索引或者普通索引。
eq_ref只能找到一行,而ref能找到多行。

  • 4、ref

对于来自前面表的每一行,在此表的索引中可以匹配到多行。若联接只用到索引的最左前缀或索引不是主键或唯一索引时,使用ref类型(也就是说,此联接能够匹配多行记录)。

ref可用于使用’=’或’<=>’操作符作比较的索引列。

  • 5、 fulltext

使用全文索引的时候是这个类型。要注意,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引

  • 6、ref_or_null

跟ref类型类似,只是增加了null值的比较。实际用的不多。

1
2
3
eg.
SELECT * FROM ref_table
WHERE key_column=expr OR key_column IS NULL;
  • 7、index_merge

表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取多个索引,性能可能大部分时间都不如range

  • 8、unique_subquery

用于where中的in形式子查询,子查询返回不重复值唯一值,可以完全替换子查询,效率更高。
该类型替换了下面形式的IN子查询的ref:
value IN (SELECT primary_key FROM single_table WHERE some_expr)

  • 9、index_subquery

该联接类型类似于unique_subquery。适用于非唯一索引,可以返回重复值。

  • 10、range

索引范围查询,常见于使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。

1
2
3
4
5
SELECT * FROM tbl_name
WHERE key_column BETWEEN 10 and 20;

SELECT * FROM tbl_name
WHERE key_column IN (10,20,30);
  • 11、index

索引全表扫描,把索引从头到尾扫一遍。这里包含两种情况:
一种是查询使用了覆盖索引,那么它只需要扫描索引就可以获得数据,这个效率要比全表扫描要快,因为索引通常比数据表小,而且还能避免二次查询。在extra中显示Using index,反之,如果在索引上进行全表扫描,没有Using index的提示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 此表见有一个name列索引。
# 因为查询的列name上建有索引,所以如果这样type走的是index
mysql> explain select name from testa;
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
| 1 | SIMPLE | testa | index | NULL | idx_name | 33 | NULL | 2 | Using index |
+----+-------------+-------+-------+---------------+----------+---------+------+------+-------------+
1 row in set

# 因为查询的列cusno没有建索引,或者查询的列包含没有索引的列,这样查询就会走ALL扫描,如下:
mysql> explain select cusno from testa;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | testa | ALL | NULL | NULL | NULL | NULL | 2 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set

# 包含有未见索引的列
mysql> explain select * from testa;
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
| 1 | SIMPLE | testa | ALL | NULL | NULL | NULL | NULL | 2 | NULL |
+----+-------------+-------+------+---------------+------+---------+------+------+-------+
1 row in set
  • 12、all

全表扫描,性能最差。

5. partitions

版本5.7以前,该项是explain partitions显示的选项,5.7以后成为了默认选项。该列显示的为分区表命中的分区情况。非分区表该字段为空(null)。

6. possible_keys

查询可能使用到的索引都会在这里列出来

7. key

查询真正使用到的索引。
select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。

8. key_len

查询用到的索引长度(字节数)。
如果是单列索引,那就整个索引长度算进去,如果是多列索引,那么查询不一定都能使用到所有的列,用多少算多少。留意下这个列的值,算一下你的多列索引总长度就知道有没有使用到所有的列了。

key_len只计算where条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到key_len中。

9. ref

如果是使用的常数等值查询,这里会显示const,如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段,如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func

10. rows(重要)

rows 也是一个重要的字段。 这是mysql估算的需要扫描的行数(不是精确值)。
这个值非常直观显示 SQL 的效率好坏, 原则上 rows 越少越好.

11. filtered

这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数。这个字段不重要

12. extra(重要)

EXplain 中的很多额外的信息会在 Extra 字段显示, 常见的有以下几种内容:

  • distinct:在select部分使用了distinc关键字
  • Using filesort:当 Extra 中有 Using filesort 时, 表示 MySQL 需额外的排序操作, 不能通过索引顺序达到排序效果. 一般有 Using filesort, 都建议优化去掉, 因为这样的查询 CPU 资源消耗大.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 例如下面的例子:

mysql> EXPLAIN SELECT * FROM order_info ORDER BY product_name \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: index
possible_keys: NULL
key: user_product_detail_index
key_len: 253
ref: NULL
rows: 9
filtered: 100.00
Extra: Using index; Using filesort
1 row in set, 1 warning (0.00 sec)
我们的索引是

KEY `user_product_detail_index` (`user_id`, `product_name`, `productor`)
但是上面的查询中根据 product_name 来排序, 因此不能使用索引进行优化, 进而会产生 Using filesort.
如果我们将排序依据改为 ORDER BY user_id, product_name, 那么就不会出现 Using filesort 了. 例如:

mysql> EXPLAIN SELECT * FROM order_info ORDER BY user_id, product_name \G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: order_info
partitions: NULL
type: index
possible_keys: NULL
key: user_product_detail_index
key_len: 253
ref: NULL
rows: 9
filtered: 100.00
Extra: Using index
1 row in set, 1 warning (0.00 sec)
  • Using index
    “覆盖索引扫描”, 表示查询在索引树中就可查找所需数据, 不用扫描表数据文件, 往往说明性能不错
  • Using temporary
    查询有使用临时表, 一般出现于排序, 分组和多表 join 的情况, 查询效率不高, 建议优化.

除此之外还有其他值,这里就不一一一列举了。


通过以上这些总结,以后我们看到explain的结果,就知道该从哪些字段值去分析sql语句的执行效率了。

l

mysql 连表操作后字符集不同导致索引失效

背景

一个表的字符集为utf8mb4
一个表的字符集为utf8

连表查询后可以发现索引失效的情况,查询很慢。这时候可以通过show warnings;查询警告信息。发现了convert(testdb.t1.code using utf8mb4)之后,发现2个表的字符集不一样。

t1为utf8,t2为utf8mb4。但是为什么表字符集不一样(实际是字段字符集不一样)就会导致t1全表扫描呢?下面来做分析。

分析原因

(1)首先t2 left join t1决定了t2是驱动表,这一步相当于执行了

1
select * from t2 where t2.name = 'dddd'

取出code字段的值,这里为’8a77a32a7e0825f7c8634226105c42e5’;

(2)然后拿t2查到的code的值根据join条件去t1里面查找,这一步就相当于执行了

1
select * from t1 where t1.code = '8a77a32a7e0825f7c8634226105c42e5';

(3)但是由于第(1)步里面t2表取出的code字段是utf8mb4字符集,而t1表里面的code是utf8字符集,这里需要做字符集转换,字符集转换遵循由小到大的原则,因为utf8mb4是utf8的超集,所以这里把utf8转换成utf8mb4,即把t1.code转换成utf8mb4字符集,转换了之后,由于t1.code上面的索引仍然是utf8字符集,所以这个索引就被执行计划忽略了,然后t1表只能选择全表扫描。如果t2筛选出来的记录不止1条,那么t1就会被多次全表扫描,性能之差可想而知。

修改字符集

1
alter table t1 charset utf8mb4;

但这是错的,这只是改了表的默认字符集,即新的字段才会使用utf8mb4,已经存在的字段仍然是utf8。
show create table t1 会发现DEFAULT CHARSET=utf8mb4改变了,但是SHOW FULL COLUMNS FROM pay_log_all_from_mq;会发现字段的字符集没有改变。

1
alter table t1 convert to charset utf8mb4;

查看 SHOW FULL COLUMNS FROM t1 ;会发现字段的字符已经改变。

注意点

  • 表字符集不同时,可能导致join的SQL使用不到索引,引起严重的性能问题;
l

mysql 连表操作后字符集不同导致索引失效

背景

一个表的字符集为utf8mb4
一个表的字符集为utf8

连表查询后可以发现索引失效的情况,查询很慢。

t1为utf8,t2为utf8mb4。但是为什么表字符集不一样(实际是字段字符集不一样)就会导致t1全表扫描呢?

分析原因

(1)首先t2 left join t1决定了t2是驱动表,这一步相当于执行了

1
select * from t2 where t2.name = 'dddd'

取出code字段的值,假如为’8a77a32a7e0825f7c8634226105c42e5’;

(2)然后拿t2查到的code的值根据join条件去t1里面查找,这一步就相当于执行了

1
select * from t1 where t1.code = '8a77a32a7e0825f7c8634226105c42e5';

(3)但是由于第(1)步里面t2表取出的code字段是utf8mb4字符集,而t1表里面的code是utf8字符集,这里需要做字符集转换,字符集转换遵循由小到大的原则,因为utf8mb4是utf8的超集,所以这里把utf8转换成utf8mb4,即把t1.code转换成utf8mb4字符集,转换了之后,由于t1.code上面的索引仍然是utf8字符集,所以这个索引会被执行计划忽略了,然后t1表只能选择全表扫描。如果t2筛选出来的记录不止1条,那么t1就会被多次全表扫描,性能之差可想而知。

修改字符集

1
alter table t1 charset utf8mb4;

这是错的,这只是改了表的默认字符集,即新的字段才会使用utf8mb4,已经存在的字段仍然是utf8。

1
alter table t1 convert to charset utf8mb4;

查看 SHOW FULL COLUMNS FROM t1 ;会发现字段的字符已经改变。

其他

排序规则概念:是指对指定字符集下不同字符的比较规则。排序规则有以下特征:

  • 它和字符集(CHARSET)相关
  • 每种字符集都有多种它支持的排序规则
  • 每种字符集都会默认指定一种排序规则为默认值。
  • mysql可对相应字符集,单独设置排序规则

排序规则作用:排序规则指定后,它会影响我们使用 ORDER BY语句查询的结果顺序,会影响到 WHERE条件中大于小于号的筛选结果,会影响 DISTINCT、GROUP BY、HAVING 语句的查询结果。另外,mysql 建索引的时候,如果索引列是字符类型,也会影响索引创建,只不过这种影响我们感知不到。总之,凡是涉及到字符类型比较或排序的地方,都和排序规则有关。

注意点

  • 表字符集不同时,可能导致join的SQL使用不到索引,引起严重的性能问题;
  • 建表语句一定要设置默认字符集,需要关联的表字符集一定要统一;
  • 统一dev、fat、test、prod环境mysql对应库的字符集,可以及早发现问题;
l

§1效率

输入相似文件名太麻烦

用花括号括起来的字符串用逗号连接,可以自动扩展,非常有用,直接看例子:

1
2
3
4
$ echo {one,two,three}file
onefile twofile threefile
$ echo {one,two,three}{1,2,3}
one1 one2 one3 two1 two2 two3 three1 three2 three3

你看,花括号中的每个字符都可以和之后(或之前)的字符串进行组合拼 接,注意花括号和其中的逗号不可以用空格分隔,否则会被认为是普通的字 符串对待

这个技巧有什么实际用处呢?最简单有用的就是给 cp , mv , rm 等命令扩 展参数:

1
2
3
4
5
6
7
8
9
$ cp /very/long/path/file{,.bak}
==> cp /very/long/path/file /very/long/path/file.bak
# 给 file 复制一个叫做 file.bak 的副本

$ rm file{1,3,5}.txt
# 删除 file1.txt file3.txt file5.txt

$ mv *.{c,cpp} src/
# 将所有 .c 和 .cpp 为后缀的文件移入 src 文件夹

输入路径名称太麻烦

  • cd - 返回刚才呆的目录,直接看例子吧:
1
2
3
4
5
6
7
$ pwd
/very/long/path
$ cd # 回到家目录瞅瞅
$ pwd
/home/labuladong
$ cd - # 再返回刚才那个目录 $ pwd
/very/long/path
  • 特殊命令 !$ 会替换成上一次输入的命令最后的路径,直接看例子:
1
2
3
4
5
6
7
#没有加可执行权限
$ /usr/bin/script.sh
zsh: permission denied: /usr/bin/script.sh

$ chmod +x !$
chmod +x /usr/bin/script.sh
export lessCharset-uft8

特殊命令 !* 会替换成上一次命令输入的所有文件路径,直接看例子:

1
2
3
4
5
6
# 创建了三个脚本文件
$ file script1.sh script2.sh script3.sh

# 给它们全部加上可执行权限
$ chmod +x !*
chmod +x script1.sh script2.sh script3.sh

可以在环境变量 CDPATH 中加入你常用的工作目录,当 cd 命令在当前目 录中找不到你指定的文件/目录时,会自动到 CDPATH 中的目录中寻找。

比如说我常去 /var/log 目录找日志,可以执行如下命令:

1
2
3
4
5
6
7
8
9
$ export CDPATH='~:/var/log'
# cd 命令将会在 〜 目录和 /var/log 目录扩展搜索$ pwd
/home/labuladong/musics
$ cd mysql
cd /var/log/mysql
$ pwd
/var/log/mysql
$ cd my_pictures
cd /home/labuladong/my_pictures

这个技巧是十分好用的,这样就免了经常写完整的路径名称,节约不少时间。

需要注意的是,以上操作是 bash 支持的,其他主流 shell 解释器当然都支持 扩展 cd 命令的搜索目录,但可能不是修改 CDPATH 这个变量,具体的设 置方法可以自行搜索。

输入重复命令太麻烦

** 使用特殊命令 !! ,可以自动替换成上一次使用的命令:

1
2
3
4
5
$ apt install net-tools
E: Could not open lock file - open (13: Permission denied)

$ sudo !!
sudo apt install net-tools [sudo] password for fdl:

有的命令很⻓,一时间想不起来具体参数了怎么办?

对于 bash 终端,可以使用 Ctrl+R 快捷键反向搜索历史命令,之所以说是 反向搜索,就是搜索最近一次输入的命令。

比如按下 Ctrl+R 之后,输入 sudo ,bash 就会搜索出最近一次包含 sudo 的命令,你回⻋之后就可以运行该命令了:

1
(reverse-i-search)`sudo': sudo apt install git

但是这个方法有缺点:首先,该功能似乎只有 bash 支持,我用的 zsh 作为 shell 终端,就用不了;第二,只能查找出一个(最近的)命令,如果我想 找以前的某个命令,就没办法了。

对于这种情况,我们最常用的方法是使用 history 命令配合管道符和 grep 命令来寻找某个历史命令:

1
2
3
4
5
6
# 过滤出所有包含 config 字段的历史命令
$ history | grep 'config'
7352 ./configure
7434 git config --global --unset https.proxy 9609 ifconfig
9985 clip -o | sed -z 's/\n/,\n/g' | clip
10433 cd ~/.config

你使用的所有 shell 命令都会被记录,前面的数字就表示这是第几个命令, 找到你想重复使用的命令后,也不需要复制粘贴该命令,只要使用 ! + 你 想重用的命令编号即可运行该命令
拿上面的例子,我想重新运行 git config 那条命令,就可以这样:

1
2
$ !7434
git config --global --unset https.proxy # 运行完成

我觉得 history 加管道加 grep 这样打的字还是太多,可以在 你的 shell 配置文件中( .bashrc , .zshrc 等) 中写这样一个函数:

1
2
3
his() {
history | grep "$@"
}

这样就不需要写那么多,只需要 his ‘some_keyword’ 即可搜索历史命令。

我一般不使用 bash 作为终端,我给大家推荐一款很好用的 shell 终端叫做 zsh,这也是我自己使用的 shell。这款终端还可以扩展各种插件,非常好 用,具体配置方法可自行搜索。

其他小技巧

  • **yes 命令自动输入字符y **进行确认

    我们安装某些软件的时候,可能有交互式的提问:

1
2
3
$ sudo apt install XXX
...
XXX will use 996 MB disk space, continue? [y/n]

一般情况下我们都是一路 y 到底,但如果我们想自动化一些软件的安装就很 烦,遇到这种交互式提问就卡住了,还得手动处理。

yes 命令可以帮助我们:

1
$ yes | your_cmd

这样就会一路自动 y 下去,不会停下让我们输入了。 如果你读过前文Linux 文件描述符,就知道其原理很简单:

你单独运行一下 yes 命令,发现它就是打印出一大堆字符 y,通过管道把 输出和 your_cmd 的标准输入相连接,如果 your_cmd 又提出无聊的问题, 就会从标准输入读取数据,也就会读取到一个 y 和换行符,和你手动输入 y 确认是一个效果。

  • 特殊变量 $? 记录上一次命令的返回值

在 Linux shell 中,遵循 C 语言的习惯,返回值为 0 的话就是程序正常退 出,非 0 值就是异常退出出。读取上一次命令的返回值在平时使用命令行时 感觉没什么用,但是如果你想编写一些 shell 脚本,知道返回值非常有用。

举个实际的例子,比如我的 Github 仓库 fucking-algorithm ,我需要给其中 所有 markdown 文件最下方添加上一篇、下一篇、目录三个⻚脚链接,有的 文章已经有了⻚脚,大部分都没有。

为了防止重复添加,我必须知道一个 md 文件下方是否已添加,这时候就可 以使用 $? 变量配合 grep 命令做到:

1
2
3
4
5
6
#!/bin/bash
filename=$1
# 查看文件尾部是否包含关键词
tail | grep '下一篇' $filename
# grep 查找到匹配会返回 0,找不到则返回非 0 值
[ $? -ne 0 ] && { 添加⻚脚; }
  • 特殊变量 $$ 记录当前进程的 PID

这个功能可能在平时使用时也不怎么用,但是在写 shell 脚本时也非常有 用,比如说你要在 /tmp 创建临时文件,给文件起名字一直都是非常让人 费脑子 的,这时候可以使用 $$ 变量扩展出当前进程的 PID 作为临时文件的名字。

l

$1易混淆点

一、标准输入和参数的区别

这个问题一定是最容易让人迷惑的,具体来说,就是搞不清什么时候用管道 符| 和文件重定向> ,< ,什么时候用变量$ 。

比如说,我现在有个自动连接宽带的 shell 脚本 connect.sh ,存在我的家目录:

1
2
$ where connect.sh 
/home/fdl/bin/connect.sh

如果我想删除这个脚本,而且想少敲几次键盘,应该怎么操作呢?我曾经这
样尝试过:

1
$ where connect.sh | rm

实际上,这样操作是错误的,正确的做法应该是这样的:

1
$ rm $(where connect.sh)

前者试图将 where 的结果连接到 rm 的标准输入,后者试图将结果作为命令行参数传入。

标准输入就是编程语言中诸如 scanf 或者 readline 这种命令;而参数是指 程序的 main 函数传入的 args 字符数组

前文「Linux文件描述符」说过,管道符和重定向符是将数据作为程序的标 准输入,而 $(cmd) 是读取 cmd 命令输出的数据作为参数。

用刚才的例子说, rm 命令源代码中肯定不接受标准输入,而是接收命令行 参数,删除相应的文件。作为对比, cat 命令是既接受标准输入,又接受 命令行参数:

1
2
3
4
5
6
$ cat filename 
...file text...
$ cat < filename
...file text...
$ echo 'hello world' | cat
hello world

如果命令能够让终端阻塞,说明该命令接收标准输入,反之就是不接受,比 如你只运行 cat 命令不加任何参数,终端就会阻塞,等待你输入字符串并 回显相同的字符串。

二、后台运行程序

比如说你远程登录到服务器上,运行一个 Django web 程序:

1
$ python manager.py runserver 0.0.0.0 Listening on 0.0.0.0:8080...

现在你可以通过服务器的 IP 地址测试 Django 服务,但是终端此时就阻塞 了,你输入什么都不响应,除非输入 Ctrl-C 或者 Ctrl-/ 终止 python 进程。

可以在命令之后加一个 & 符号,这样命令行不会阻塞,可以响应你后续输 入的命令,但是如果你退出服务器的登录,就不能访问该网⻚了。

如果你想在退出服务器之后仍然能够访问web服务,应该这样写命令 (cmd &) :

1
2
3
$ (python manager.py runserver 0.0.0.0 &) Listening on 0.0.0.0:8080...

$ logout

底层原理是这样的:

每一个命令行终端都是一个 shell 进程,你在这个终端里执行的程序实际上 都是这个 shell 进程分出来的子进程。正常情况下,shell 进程会阻塞,等待 子进程退出才重新接收你输入的新的命令。加上 & 号,只是让 shell 进程不 再阻塞,可以继续响应你的新命令。但是无论如何,你如果关掉了这个 shell 命令行端口,依附于它的所有子进程都会退出。

而 (cmd &) 这样运行命令,则是将 cmd 命令挂到一个 systemd 系统守护进程名下,认 systemd 做爸爸,这样当你退出当前终端时,对于刚才的 cmd 命令就完全没有影响了。

类似的,还有一种后台运行常用的做法是这样:

1
$ nohub some_cmd &

nohub 命令也是类似的原理,不过通过我的测试,还是 (cmd &) 这种形式 更加稳定

三、单引号和双引号的区别

不同的 shell 行为会有细微区别,但有一点是确定的,对于 $ ( ) 这 几个符号,单引号包围的字符串不会做任何转义,双引号包围的字符串会转 义

shell 的行为可以测试,使用 set -x 命令,会开启 shell 的命令回显,你可 以通过回显观察 shell 到底在执行什么命令:

可⻅ echo $(cmd) 和 echo “$(cmd)” ,结果差不多,但是仍然有区别。注 意观察,双引号转义完成的结果会自动增加单引号,而前者不会。

也就是说,如果 $ 读取出的参数字符串包含空格,应该用双引号括起来, 否则就会出错

四、****sudo 找不到命令

有时候我们普通用户可以用的命令,用 sudo 加权限之后却报错 command not found:

1
2
3
4
5
$ connect.sh
network-manager: Permission denied

$ sudo connect.sh
sudo: command not found

原因在于, connect.sh 这个脚本仅存在于该用户的环境变量中:

当使用 sudo 时,系统会使用 /etc/sudoers 这个文件中规定的该用户的权 限和环境变量,而这个脚本在 /etc/sudoers 环境变量目录中当然是找不到 的。

解决方法是使用脚本文件的路径,而不是仅仅通过脚本名称:

1
$ sudo /home/fdl/bin/connect.sh
l

报告分类bash

for file in *.pdf; do mkdir -p -- "${file%%-*}" && \
    mv -- "$file" "${file%%-*}"; done
l

[toc]

§1效率

输入相似文件名太麻烦

用花括号括起来的字符串用逗号连接,可以自动扩展,非常有用,直接看例子:

1
2
3
4
$ echo {one,two,three}file
onefile twofile threefile
$ echo {one,two,three}{1,2,3}
one1 one2 one3 two1 two2 two3 three1 three2 three3

你看,花括号中的每个字符都可以和之后(或之前)的字符串进行组合拼 接,注意花括号和其中的逗号不可以用空格分隔,否则会被认为是普通的字 符串对待

这个技巧有什么实际用处呢?最简单有用的就是给 cp , mv , rm 等命令扩 展参数:

1
2
3
4
5
6
7
8
9
$ cp /very/long/path/file{,.bak}
==> cp /very/long/path/file /very/long/path/file.bak
# 给 file 复制一个叫做 file.bak 的副本

$ rm file{1,3,5}.txt
# 删除 file1.txt file3.txt file5.txt

$ mv *.{c,cpp} src/
# 将所有 .c 和 .cpp 为后缀的文件移入 src 文件夹

输入路径名称太麻烦

  • cd - 返回刚才呆的目录,直接看例子吧:
1
2
3
4
5
6
7
$ pwd
/very/long/path
$ cd # 回到家目录瞅瞅
$ pwd
/home/labuladong
$ cd - # 再返回刚才那个目录 $ pwd
/very/long/path
  • 特殊命令 !$ 会替换成上一次输入的命令最后的路径,直接看例子:
1
2
3
4
5
6
7
#没有加可执行权限
$ /usr/bin/script.sh
zsh: permission denied: /usr/bin/script.sh

$ chmod +x !$
chmod +x /usr/bin/script.sh
export lessCharset-uft8

特殊命令 !* 会替换成上一次命令输入的所有文件路径,直接看例子:

1
2
3
4
5
6
# 创建了三个脚本文件
$ file script1.sh script2.sh script3.sh

# 给它们全部加上可执行权限
$ chmod +x !*
chmod +x script1.sh script2.sh script3.sh

可以在环境变量 CDPATH 中加入你常用的工作目录,当 cd 命令在当前目 录中找不到你指定的文件/目录时,会自动到 CDPATH 中的目录中寻找。

比如说我常去 /var/log 目录找日志,可以执行如下命令:

1
2
3
4
5
6
7
8
9
$ export CDPATH='~:/var/log'
# cd 命令将会在 〜 目录和 /var/log 目录扩展搜索$ pwd
/home/labuladong/musics
$ cd mysql
cd /var/log/mysql
$ pwd
/var/log/mysql
$ cd my_pictures
cd /home/labuladong/my_pictures

这个技巧是十分好用的,这样就免了经常写完整的路径名称,节约不少时间。

需要注意的是,以上操作是 bash 支持的,其他主流 shell 解释器当然都支持 扩展 cd 命令的搜索目录,但可能不是修改 CDPATH 这个变量,具体的设 置方法可以自行搜索。

输入重复命令太麻烦

** 使用特殊命令 !! ,可以自动替换成上一次使用的命令:

1
2
3
4
5
$ apt install net-tools
E: Could not open lock file - open (13: Permission denied)

$ sudo !!
sudo apt install net-tools [sudo] password for fdl:

有的命令很⻓,一时间想不起来具体参数了怎么办?

对于 bash 终端,可以使用 Ctrl+R 快捷键反向搜索历史命令,之所以说是 反向搜索,就是搜索最近一次输入的命令。

比如按下 Ctrl+R 之后,输入 sudo ,bash 就会搜索出最近一次包含 sudo 的命令,你回⻋之后就可以运行该命令了:

1
(reverse-i-search)`sudo': sudo apt install git

但是这个方法有缺点:首先,该功能似乎只有 bash 支持,我用的 zsh 作为 shell 终端,就用不了;第二,只能查找出一个(最近的)命令,如果我想 找以前的某个命令,就没办法了。

对于这种情况,我们最常用的方法是使用 history 命令配合管道符和 grep 命令来寻找某个历史命令:

1
2
3
4
5
6
# 过滤出所有包含 config 字段的历史命令
$ history | grep 'config'
7352 ./configure
7434 git config --global --unset https.proxy 9609 ifconfig
9985 clip -o | sed -z 's/\n/,\n/g' | clip
10433 cd ~/.config

你使用的所有 shell 命令都会被记录,前面的数字就表示这是第几个命令, 找到你想重复使用的命令后,也不需要复制粘贴该命令,只要使用 ! + 你 想重用的命令编号即可运行该命令
拿上面的例子,我想重新运行 git config 那条命令,就可以这样:

1
2
$ !7434
git config --global --unset https.proxy # 运行完成

我觉得 history 加管道加 grep 这样打的字还是太多,可以在 你的 shell 配置文件中( .bashrc , .zshrc 等) 中写这样一个函数:

1
2
3
his() {
history | grep "$@"
}

这样就不需要写那么多,只需要 his ‘some_keyword’ 即可搜索历史命令。

我一般不使用 bash 作为终端,我给大家推荐一款很好用的 shell 终端叫做 zsh,这也是我自己使用的 shell。这款终端还可以扩展各种插件,非常好 用,具体配置方法可自行搜索。

其他小技巧

  • **yes 命令自动输入字符y **进行确认

    我们安装某些软件的时候,可能有交互式的提问:

1
2
3
$ sudo apt install XXX
...
XXX will use 996 MB disk space, continue? [y/n]

一般情况下我们都是一路 y 到底,但如果我们想自动化一些软件的安装就很 烦,遇到这种交互式提问就卡住了,还得手动处理。

yes 命令可以帮助我们:

1
$ yes | your_cmd

这样就会一路自动 y 下去,不会停下让我们输入了。 如果你读过前文Linux 文件描述符,就知道其原理很简单:

你单独运行一下 yes 命令,发现它就是打印出一大堆字符 y,通过管道把 输出和 your_cmd 的标准输入相连接,如果 your_cmd 又提出无聊的问题, 就会从标准输入读取数据,也就会读取到一个 y 和换行符,和你手动输入 y 确认是一个效果。

  • 特殊变量 $? 记录上一次命令的返回值

在 Linux shell 中,遵循 C 语言的习惯,返回值为 0 的话就是程序正常退 出,非 0 值就是异常退出出。读取上一次命令的返回值在平时使用命令行时 感觉没什么用,但是如果你想编写一些 shell 脚本,知道返回值非常有用。

举个实际的例子,比如我的 Github 仓库 fucking-algorithm ,我需要给其中 所有 markdown 文件最下方添加上一篇、下一篇、目录三个⻚脚链接,有的 文章已经有了⻚脚,大部分都没有。

为了防止重复添加,我必须知道一个 md 文件下方是否已添加,这时候就可 以使用 $? 变量配合 grep 命令做到:

1
2
3
4
5
6
#!/bin/bash
filename=$1
# 查看文件尾部是否包含关键词
tail | grep '下一篇' $filename
# grep 查找到匹配会返回 0,找不到则返回非 0 值
[ $? -ne 0 ] && { 添加⻚脚; }
  • 特殊变量 $$ 记录当前进程的 PID

这个功能可能在平时使用时也不怎么用,但是在写 shell 脚本时也非常有 用,比如说你要在 /tmp 创建临时文件,给文件起名字一直都是非常让人 费脑子 的,这时候可以使用 $$ 变量扩展出当前进程的 PID 作为临时文件的名字。

l