git流程
git 流程
1 | 正常上线 |
1 | 正常上线 |
idea使用git通常需要忽略一些临时文件,需要配置.gitignore插件
安装插件
File -> Settings -> Plugins 搜索框搜索.ignore,点击安装
生成初始.ignore文件

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

生成如下文件
1 | # Compiled class file |
idea项目一般需要自己增加如下两项
1 | .idea/ |
即忽略这两个文件夹即文件夹下的所有文件
现在可以使用了,提交一次测试下
发现.idea文件夹下的文件还有变更被提交,这是因为在使用gitignore之前,此文件就以及被跟踪了,这样的话需要移除跟踪,如下命令:
移除指定文件夹即文件夹下所有文件:
1 | git rm --cached --force -r .idea |
移除指定文件:
1 | git rm --cached --force ydq-api/ydq-api.iml |
这样,表示移除成功。
现在,上面的操作进行提交:

移交移除文件.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 是删除暂存区里的文件而不删除工作区里的文件,第一种是删除某个文件,第二种方法就把所有暂存区里的文件删了,再加一遍,相当于更新了一遍。
1 | ❯ docker run --restart=always -d --name holer-client -e PARAMS="www.swimminghao.top 6060 51c45156bf1c4a82b4e6ffff2150b65e" swimminghao/holer-client:latest |
1 | dockerfile1 |
1 | dockerfile2 |
dockerfile和jar包放同一文件夹,再执行下面指令
1 | ❯ docker build -t swimminghao/holer-client:latest . |
1 | FROM openjdk:8-jdk-alpine |
dockerfile和jar包放同一文件夹,再执行下面指令
1 | ❯ docker build -t swimminghao/holer-server:latest . |

1 | ❯ docker ps -a |
1 | ❯ docker history <83d576a828a8> --format "{{.CreatedBy}}" --no-trunc |tac | awk '{if($3~/nop/){for(i=1;i<=3;i++){$i=""};print substr($0,4)}else{print "RUN",$0}}' |
1 | ❯ bash decompile.sh seanhongxing/holer-server |
1 | #!/bin/bash |
1 | //镜像导出 |
注:分层,jar包在某一层
| 按键或组合键 | 功能 |
|---|---|
| ⌘ | 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。
| 组合键 | 功能 |
|---|---|
| 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-重音符 (`) | 将焦点移至窗口抽屉 |
| 组合键 | 功能 |
|---|---|
| 左箭头和右箭头 | 从一个菜单移到另一个菜单 |
| Return | 打开所选菜单 |
| 上箭头和下箭头 | 移至所选菜单中的菜单项 |
| 键入菜单项的名称 | 跳到所选菜单中的菜单项 |
| Return | 选择菜单项 |
在“辅助功能”偏好设置中打开“鼠标键后,您可以使用键盘或数字小键盘上的按键移动鼠标指针。
| 组合键 | 功能 |
|---|---|
| 8 或数字小键盘上的 8 | 上移 |
| K 或数字小键盘上的 2 | 下移 |
| U 或数字小键盘上的 4 | 左移 |
| O 或数字小键盘上的 6 | 右移 |
| J 或数字小键盘上的 1 | 沿对角线向左下角移 |
| L 或数字小键盘上的 3 | 沿对角线向右下角移 |
| 7 或数字小键盘上的 7 | 沿对角线向左上角移 |
| 9 或数字小键盘上的 9 | 沿对角线向右上角移 |
| I 或数字小键盘上的 5 | 按鼠标按钮 |
| M 或数字小键盘上的 0 | 按住鼠标按钮 |
| .(句点键) | 释放按住的鼠标按钮 |
| 组合键 | 功能 |
|---|---|
| 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 组合键 | 对拖移的项目赋予别名(按住按键时指针会改变) |
用花括号括起来的字符串用逗号连接,可以自动扩展,非常有用,直接看例子:
1 | $ echo {one,two,three}file |
你看,花括号中的每个字符都可以和之后(或之前)的字符串进行组合拼 接,注意花括号和其中的逗号不可以用空格分隔,否则会被认为是普通的字 符串对待。
这个技巧有什么实际用处呢?最简单有用的就是给 cp , mv , rm 等命令扩 展参数:
1 | $ cp /very/long/path/file{,.bak} |
1 | $ pwd |
1 | #没有加可执行权限 |
特殊命令 !* 会替换成上一次命令输入的所有文件路径,直接看例子:
1 | # 创建了三个脚本文件 |
可以在环境变量 CDPATH 中加入你常用的工作目录,当 cd 命令在当前目 录中找不到你指定的文件/目录时,会自动到 CDPATH 中的目录中寻找。
比如说我常去 /var/log 目录找日志,可以执行如下命令:
1 | $ export CDPATH='~:/var/log' |
这个技巧是十分好用的,这样就免了经常写完整的路径名称,节约不少时间。
需要注意的是,以上操作是 bash 支持的,其他主流 shell 解释器当然都支持 扩展 cd 命令的搜索目录,但可能不是修改 CDPATH 这个变量,具体的设 置方法可以自行搜索。
** 使用特殊命令 !! ,可以自动替换成上一次使用的命令:
1 | $ apt install net-tools |
有的命令很⻓,一时间想不起来具体参数了怎么办?
对于 bash 终端,可以使用 Ctrl+R 快捷键反向搜索历史命令,之所以说是 反向搜索,就是搜索最近一次输入的命令。
比如按下 Ctrl+R 之后,输入 sudo ,bash 就会搜索出最近一次包含 sudo 的命令,你回⻋之后就可以运行该命令了:
1 | (reverse-i-search)`sudo': sudo apt install git |
但是这个方法有缺点:首先,该功能似乎只有 bash 支持,我用的 zsh 作为 shell 终端,就用不了;第二,只能查找出一个(最近的)命令,如果我想 找以前的某个命令,就没办法了。
对于这种情况,我们最常用的方法是使用 history 命令配合管道符和 grep 命令来寻找某个历史命令:
1 | # 过滤出所有包含 config 字段的历史命令 |
你使用的所有 shell 命令都会被记录,前面的数字就表示这是第几个命令, 找到你想重复使用的命令后,也不需要复制粘贴该命令,只要使用 ! + 你 想重用的命令编号即可运行该命令。
拿上面的例子,我想重新运行 git config 那条命令,就可以这样:
1 | $ !7434 |
我觉得 history 加管道加 grep 这样打的字还是太多,可以在 你的 shell 配置文件中( .bashrc , .zshrc 等) 中写这样一个函数:
1 | his() { |
这样就不需要写那么多,只需要 his ‘some_keyword’ 即可搜索历史命令。
我一般不使用 bash 作为终端,我给大家推荐一款很好用的 shell 终端叫做 zsh,这也是我自己使用的 shell。这款终端还可以扩展各种插件,非常好 用,具体配置方法可自行搜索。
**yes 命令自动输入字符y **进行确认
我们安装某些软件的时候,可能有交互式的提问:
1 | $ sudo apt install XXX |
一般情况下我们都是一路 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 | #!/bin/bash |
这个功能可能在平时使用时也不怎么用,但是在写 shell 脚本时也非常有 用,比如说你要在 /tmp 创建临时文件,给文件起名字一直都是非常让人 费脑子 的,这时候可以使用 $$ 变量扩展出当前进程的 PID 作为临时文件的名字。
这个问题一定是最容易让人迷惑的,具体来说,就是搞不清什么时候用管道 符| 和文件重定向> ,< ,什么时候用变量$ 。
比如说,我现在有个自动连接宽带的 shell 脚本 connect.sh ,存在我的家目录:
1 | $ where connect.sh |
如果我想删除这个脚本,而且想少敲几次键盘,应该怎么操作呢?我曾经这
样尝试过:
1 | $ where connect.sh | rm |
实际上,这样操作是错误的,正确的做法应该是这样的:
1 | $ rm $(where connect.sh) |
前者试图将 where 的结果连接到 rm 的标准输入,后者试图将结果作为命令行参数传入。
标准输入就是编程语言中诸如 scanf 或者 readline 这种命令;而参数是指 程序的 main 函数传入的 args 字符数组。
前文「Linux文件描述符」说过,管道符和重定向符是将数据作为程序的标 准输入,而 $(cmd) 是读取 cmd 命令输出的数据作为参数。
用刚才的例子说, rm 命令源代码中肯定不接受标准输入,而是接收命令行 参数,删除相应的文件。作为对比, cat 命令是既接受标准输入,又接受 命令行参数:
1 | $ cat filename |
如果命令能够让终端阻塞,说明该命令接收标准输入,反之就是不接受,比 如你只运行 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 | $ (python manager.py runserver 0.0.0.0 &) Listening on 0.0.0.0:8080... |
底层原理是这样的:
每一个命令行终端都是一个 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 加权限之后却报错 command not found:
1 | $ connect.sh |
原因在于, connect.sh 这个脚本仅存在于该用户的环境变量中:
当使用 sudo 时,系统会使用 /etc/sudoers 这个文件中规定的该用户的权 限和环境变量,而这个脚本在 /etc/sudoers 环境变量目录中当然是找不到 的。
解决方法是使用脚本文件的路径,而不是仅仅通过脚本名称:
1 | $ sudo /home/fdl/bin/connect.sh |
[toc]
用花括号括起来的字符串用逗号连接,可以自动扩展,非常有用,直接看例子:
1 | $ echo {one,two,three}file |
你看,花括号中的每个字符都可以和之后(或之前)的字符串进行组合拼 接,注意花括号和其中的逗号不可以用空格分隔,否则会被认为是普通的字 符串对待。
这个技巧有什么实际用处呢?最简单有用的就是给 cp , mv , rm 等命令扩 展参数:
1 | $ cp /very/long/path/file{,.bak} |
1 | $ pwd |
1 | #没有加可执行权限 |
特殊命令 !* 会替换成上一次命令输入的所有文件路径,直接看例子:
1 | # 创建了三个脚本文件 |
可以在环境变量 CDPATH 中加入你常用的工作目录,当 cd 命令在当前目 录中找不到你指定的文件/目录时,会自动到 CDPATH 中的目录中寻找。
比如说我常去 /var/log 目录找日志,可以执行如下命令:
1 | $ export CDPATH='~:/var/log' |
这个技巧是十分好用的,这样就免了经常写完整的路径名称,节约不少时间。
需要注意的是,以上操作是 bash 支持的,其他主流 shell 解释器当然都支持 扩展 cd 命令的搜索目录,但可能不是修改 CDPATH 这个变量,具体的设 置方法可以自行搜索。
** 使用特殊命令 !! ,可以自动替换成上一次使用的命令:
1 | $ apt install net-tools |
有的命令很⻓,一时间想不起来具体参数了怎么办?
对于 bash 终端,可以使用 Ctrl+R 快捷键反向搜索历史命令,之所以说是 反向搜索,就是搜索最近一次输入的命令。
比如按下 Ctrl+R 之后,输入 sudo ,bash 就会搜索出最近一次包含 sudo 的命令,你回⻋之后就可以运行该命令了:
1 | (reverse-i-search)`sudo': sudo apt install git |
但是这个方法有缺点:首先,该功能似乎只有 bash 支持,我用的 zsh 作为 shell 终端,就用不了;第二,只能查找出一个(最近的)命令,如果我想 找以前的某个命令,就没办法了。
对于这种情况,我们最常用的方法是使用 history 命令配合管道符和 grep 命令来寻找某个历史命令:
1 | # 过滤出所有包含 config 字段的历史命令 |
你使用的所有 shell 命令都会被记录,前面的数字就表示这是第几个命令, 找到你想重复使用的命令后,也不需要复制粘贴该命令,只要使用 ! + 你 想重用的命令编号即可运行该命令。
拿上面的例子,我想重新运行 git config 那条命令,就可以这样:
1 | $ !7434 |
我觉得 history 加管道加 grep 这样打的字还是太多,可以在 你的 shell 配置文件中( .bashrc , .zshrc 等) 中写这样一个函数:
1 | his() { |
这样就不需要写那么多,只需要 his ‘some_keyword’ 即可搜索历史命令。
我一般不使用 bash 作为终端,我给大家推荐一款很好用的 shell 终端叫做 zsh,这也是我自己使用的 shell。这款终端还可以扩展各种插件,非常好 用,具体配置方法可自行搜索。
**yes 命令自动输入字符y **进行确认
我们安装某些软件的时候,可能有交互式的提问:
1 | $ sudo apt install XXX |
一般情况下我们都是一路 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 | #!/bin/bash |
这个功能可能在平时使用时也不怎么用,但是在写 shell 脚本时也非常有 用,比如说你要在 /tmp 创建临时文件,给文件起名字一直都是非常让人 费脑子 的,这时候可以使用 $$ 变量扩展出当前进程的 PID 作为临时文件的名字。
我们都知道用explain xxx分析sql语句的性能,但是具体从explain的结果怎么分析性能以及每个字段的含义你清楚吗?这里我做下总结记录,也是供自己以后参考。
- 首先需要注意:MYSQL 5.6.3以前只能
EXPLAIN SELECT; MYSQL5.6.3以后就可以EXPLAIN SELECT,UPDATE,DELETE
explain结果示例:
1 | mysql> explain select * from staff; |
先上一个官方文档表格的中文版:
| Column | 含义 |
|---|---|
| id | 查询序号 |
| select_type | 查询类型 |
| table | 表名 |
| partitions | 匹配的分区 |
| type | join类型 |
| prossible_keys | 可能会选择的索引 |
| key | 实际选择的索引 |
| key_len | 索引的长度 |
| ref | 与索引作比较的列 |
| rows | 要检索的行数(估算值) |
| filtered | 查询条件过滤的行数的百分比 |
| Extra | 额外信息 |
这是explain结果的各个字段,分别解释下含义:
SQL查询中的序列号。
id列数字越大越先执行,如果说数字一样大,那么就从上往下依次执行。
查询的类型,可以是下表的任何一种类型:
| 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 意味着使用了关联子查询。
查询的表名。不一定是实际存在的表名。
可以为如下的值:
这是最重要的字段之一,显示查询使用了何种类型。从最好到最差的连接类型依次为:
system,const,eq_ref,ref,fulltext,ref_or_null,index_merge,unique_subquery,index_subquery,range,index,ALL
除了all之外,其他的type都可以使用到索引,除了index_merge之外,其他的type只可以用到一个索引。
表中只有一行数据或者是空表,这是const类型的一个特例。且只能用于myisam和memory表。如果是Innodb引擎表,type列在这个情况通常都是all或者index
最多只有一行记录匹配。当联合主键或唯一索引的所有字段跟常量值比较时,join类型为const。其他数据库也叫做唯一索引扫描
多表join时,对于来自前面表的每一行,在当前表中只能找到一行。这可能是除了system和const之外最好的类型。当主键或唯一非NULL索引的所有字段都被用作join联接时会使用此类型。
eq_ref可用于使用’=’操作符作比较的索引列。比较的值可以是常量,也可以是使用在此表之前读取的表的列的表达式。
相对于下面的ref区别就是它使用的唯一索引,即主键或唯一索引,而ref使用的是非唯一索引或者普通索引。
eq_ref只能找到一行,而ref能找到多行。
对于来自前面表的每一行,在此表的索引中可以匹配到多行。若联接只用到索引的最左前缀或索引不是主键或唯一索引时,使用ref类型(也就是说,此联接能够匹配多行记录)。
ref可用于使用’=’或’<=>’操作符作比较的索引列。
使用全文索引的时候是这个类型。要注意,全文索引的优先级很高,若全文索引和普通索引同时存在时,mysql不管代价,优先选择使用全文索引
跟ref类型类似,只是增加了null值的比较。实际用的不多。
1 | eg. |
表示查询使用了两个以上的索引,最后取交集或者并集,常见and ,or的条件使用了不同的索引,官方排序这个在ref_or_null之后,但是实际上由于要读取多个索引,性能可能大部分时间都不如range
用于where中的in形式子查询,子查询返回不重复值唯一值,可以完全替换子查询,效率更高。
该类型替换了下面形式的IN子查询的ref:value IN (SELECT primary_key FROM single_table WHERE some_expr)
该联接类型类似于unique_subquery。适用于非唯一索引,可以返回重复值。
索引范围查询,常见于使用 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN()或者like等运算符的查询中。
1 | SELECT * FROM tbl_name |
索引全表扫描,把索引从头到尾扫一遍。这里包含两种情况:
一种是查询使用了覆盖索引,那么它只需要扫描索引就可以获得数据,这个效率要比全表扫描要快,因为索引通常比数据表小,而且还能避免二次查询。在extra中显示Using index,反之,如果在索引上进行全表扫描,没有Using index的提示。
1 | # 此表见有一个name列索引。 |
全表扫描,性能最差。
版本5.7以前,该项是explain partitions显示的选项,5.7以后成为了默认选项。该列显示的为分区表命中的分区情况。非分区表该字段为空(null)。
查询可能使用到的索引都会在这里列出来
查询真正使用到的索引。
select_type为index_merge时,这里可能出现两个以上的索引,其他的select_type这里只会出现一个。
查询用到的索引长度(字节数)。
如果是单列索引,那就整个索引长度算进去,如果是多列索引,那么查询不一定都能使用到所有的列,用多少算多少。留意下这个列的值,算一下你的多列索引总长度就知道有没有使用到所有的列了。
key_len只计算where条件用到的索引长度,而排序和分组就算用到了索引,也不会计算到key_len中。
如果是使用的常数等值查询,这里会显示const,如果是连接查询,被驱动表的执行计划这里会显示驱动表的关联字段,如果是条件使用了表达式或者函数,或者条件列发生了内部隐式转换,这里可能显示为func
rows 也是一个重要的字段。 这是mysql估算的需要扫描的行数(不是精确值)。
这个值非常直观显示 SQL 的效率好坏, 原则上 rows 越少越好.
这个字段表示存储引擎返回的数据在server层过滤后,剩下多少满足查询的记录数量的比例,注意是百分比,不是具体记录数。这个字段不重要
EXplain 中的很多额外的信息会在 Extra 字段显示, 常见的有以下几种内容:
1 | # 例如下面的例子: |
除此之外还有其他值,这里就不一一一列举了。
通过以上这些总结,以后我们看到explain的结果,就知道该从哪些字段值去分析sql语句的执行效率了。
一个表的字符集为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 ;会发现字段的字符已经改变。