|
17 | 17 |
|
18 | 18 | 我个人很喜欢使用 Linux 系统,虽然说 Windows 的图形化界面做的确实比 Linux 好,但是对脚本的支持太差了。一开始有点不习惯命令行操作,但是熟悉了之后反而发现移动鼠标点点点才是浪费时间的罪魁祸首。。。
|
19 | 19 |
|
20 | | -**那么对于 Linux 命令行,本文不是介绍某些命令的用法,而是说明一些简单却特别容易让人迷惑的细节问题**。 |
| 20 | +**那么对于 Linux 命令行,本文不是介绍某些命令的具体用法,而是结合使用场景说明一些容易让人迷惑的细节问题和能够提升效率的小技巧**。 |
21 | 21 |
|
22 | 22 | 1、标准输入和命令参数的区别。
|
23 | 23 |
|
|
27 | 27 |
|
28 | 28 | 4、有的命令和`sudo`一起用就 command not found。
|
29 | 29 |
|
| 30 | +5、避免输入重复的文件名、重复的路径、重复的命令的方法,以及一些其他小技巧。 |
| 31 | + |
30 | 32 | ### 一、标准输入和参数的区别
|
31 | 33 |
|
32 | 34 | 这个问题一定是最容易让人迷惑的,具体来说,就是搞不清什么时候用管道符`|`和文件重定向`>`,`<`,什么时候用变量`$`。
|
@@ -146,13 +148,209 @@ $ where connect.sh
|
146 | 148 | $ sudo /home/fdl/bin/connect.sh
|
147 | 149 | ```
|
148 | 150 |
|
| 151 | +### 五、输入相似文件名太麻烦 |
| 152 | + |
| 153 | +用花括号括起来的字符串用逗号连接,可以自动扩展,非常有用,直接看例子: |
| 154 | + |
| 155 | +```shell |
| 156 | +$ echo {one,two,three}file |
| 157 | +onefile twofile threefile |
| 158 | + |
| 159 | +$ echo {one,two,three}{1,2,3} |
| 160 | +one1 one2 one3 two1 two2 two3 three1 three2 three3 |
| 161 | +``` |
| 162 | + |
| 163 | +你看,花括号中的每个字符都可以和之后(或之前)的字符串进行组合拼接,**注意花括号和其中的逗号不可以用空格分隔,否则会被认为是普通的字符串对待**。 |
| 164 | + |
| 165 | +这个技巧有什么实际用处呢?最简单有用的就是给 `cp`, `mv`, `rm` 等命令扩展参数: |
| 166 | + |
| 167 | +```shell |
| 168 | +$ cp /very/long/path/file{,.bak} |
| 169 | +# 给 file 复制一个叫做 file.bak 的副本 |
| 170 | + |
| 171 | +$ rm file{1,3,5}.txt |
| 172 | +# 删除 file1.txt file3.txt file5.txt |
| 173 | + |
| 174 | +$ mv *.{c,cpp} src/ |
| 175 | +# 将所有 .c 和 .cpp 为后缀的文件移入 src 文件夹 |
| 176 | +``` |
| 177 | + |
| 178 | +### 六、输入路径名称太麻烦 |
| 179 | + |
| 180 | +**用 `cd -` 返回刚才呆的目录**,直接看例子吧: |
| 181 | + |
| 182 | +```shell |
| 183 | +$ pwd |
| 184 | +/very/long/path |
| 185 | +$ cd # 回到家目录瞅瞅 |
| 186 | +$ pwd |
| 187 | +/home/labuladong |
| 188 | +$ cd - # 再返回刚才那个目录 |
| 189 | +$ pwd |
| 190 | +/very/long/path |
| 191 | +``` |
| 192 | + |
| 193 | +**特殊命令 `!$` 会替换成上一次命令最后的路径**,直接看例子: |
| 194 | + |
| 195 | +```shell |
| 196 | +# 没有加可执行权限 |
| 197 | +$ /usr/bin/script.sh |
| 198 | +zsh: permission denied: /usr/bin/script.sh |
| 199 | + |
| 200 | +$ chmod +x !$ |
| 201 | +chmod +x /usr/bin/script.sh |
| 202 | +``` |
| 203 | + |
| 204 | +**特殊命令 `!*` 会替换成上一次命令输入的所有文件路径**,直接看例子: |
| 205 | + |
| 206 | +```shell |
| 207 | +# 创建了三个脚本文件 |
| 208 | +$ file script1.sh script2.sh script3.sh |
| 209 | + |
| 210 | +# 给它们全部加上可执行权限 |
| 211 | +$ chmod +x !* |
| 212 | +chmod +x script1.sh script2.sh script3.sh |
| 213 | +``` |
| 214 | + |
| 215 | +**可以在环境变量 `CDPATH` 中加入你常用的工作目录**,当 `cd` 命令在当前目录中找不到你指定的文件/目录时,会自动到 `CDPATH` 中的目录中寻找。 |
| 216 | + |
| 217 | +比如说我常去 `/var/log` 目录找日志,可以执行如下命令: |
| 218 | + |
| 219 | +```shell |
| 220 | +$ export CDPATH='~:/var/log' |
| 221 | +# cd 命令将会在 〜 目录和 /var/log 目录扩展搜索 |
| 222 | + |
| 223 | +$ pwd |
| 224 | +/home/labuladong/musics |
| 225 | +$ cd mysql |
| 226 | +cd /var/log/mysql |
| 227 | +$ pwd |
| 228 | +/var/log/mysql |
| 229 | +$ cd my_pictures |
| 230 | +cd /home/labuladong/my_pictures |
| 231 | +``` |
| 232 | + |
| 233 | +这个技巧是十分好用的,这样就免了经常写完整的路径名称,节约不少时间。 |
| 234 | + |
| 235 | +需要注意的是,以上操作是 bash 支持的,其他主流 shell 解释器当然都支持扩展 `cd` 命令的搜索目录,但可能不是修改 `CDPATH` 这个变量,具体的设置方法可以自行搜索。 |
| 236 | + |
| 237 | +### 七、输入重复命令太麻烦 |
| 238 | + |
| 239 | +**使用特殊命令 `!!`,可以自动替换成上一次使用的命令**: |
| 240 | + |
| 241 | +```shell |
| 242 | +$ apt install net-tools |
| 243 | +E: Could not open lock file - open (13: Permission denied) |
| 244 | + |
| 245 | +$ sudo !! |
| 246 | +sudo apt install net-tools |
| 247 | +[sudo] password for fdl: |
| 248 | +``` |
| 249 | + |
| 250 | +有的命令很长,一时间想不起来具体参数了怎么办? |
| 251 | + |
| 252 | +**对于 bash 终端,可以使用 `Ctrl+R` 快捷键反向搜索历史命令**,之所以说是反向搜索,就是搜索最近一次输入的命令。 |
| 253 | + |
| 254 | +比如按下 `Ctrl+R` 之后,输入 `sudo`,bash 就会搜索出最近一次包含 `sudo` 的命令,你回车之后就可以运行该命令了: |
| 255 | + |
| 256 | +```shell |
| 257 | +(reverse-i-search)`sudo': sudo apt install git |
| 258 | +``` |
| 259 | +
|
| 260 | +但是这个方法有缺点:首先,该功能似乎只有 bash 支持,我用的 zsh 作为 shell 终端,就用不了;第二,只能查找出一个(最近的)命令,如果我想找以前的某个命令,就没办法了。 |
| 261 | +
|
| 262 | +对于这种情况,**我们最常用的方法是使用 `history` 命令配合管道符和 `grep` 命令来寻找某个历史命令**: |
| 263 | +
|
| 264 | +```shell |
| 265 | +# 过滤出所有包含 config 字段的历史命令 |
| 266 | +$ history | grep 'config' |
| 267 | + 7352 ./configure |
| 268 | + 7434 git config --global --unset https.proxy |
| 269 | + 9609 ifconfig |
| 270 | + 9985 clip -o | sed -z 's/ |
| 271 | +/, |
| 272 | +/g' | clip |
| 273 | +10433 cd ~/.config |
| 274 | +``` |
| 275 | +你使用的所有 shell 命令都会被记录,前面的数字就表示这是第几个命令,找到你想重复使用的命令后,也不需要复制粘贴该命令,**只要使用 `!` + 你想重用的命令编号即可运行该命令**。 |
| 276 | +
|
| 277 | +拿上面的例子,我想重新运行 `git config` 那条命令,就可以这样: |
| 278 | +
|
| 279 | +```shell |
| 280 | +$ !7434 |
| 281 | +git config --global --unset https.proxy |
| 282 | +# 运行完成 |
| 283 | +``` |
| 284 | +
|
| 285 | +我觉得 `history` 加管道加 `grep` 这样打的字还是太多,可以在 你的 shell 配置文件中(`.bashrc`,`.zshrc` 等) 中写这样一个函数: |
| 286 | +
|
| 287 | +```shell |
| 288 | +his() |
| 289 | +{ |
| 290 | + history | grep "$@" |
| 291 | +} |
| 292 | +``` |
| 293 | +
|
| 294 | +这样就不需要写那么多,只需要 `his 'some_keyword'` 即可搜索历史命令。 |
| 295 | +
|
| 296 | +我一般不使用 bash 作为终端,我给大家推荐一款很好用的 shell 终端叫做 zsh,这也是我自己使用的 shell。这款终端还可以扩展各种插件,非常好用,具体配置方法可自行搜索。 |
| 297 | +
|
| 298 | +### 其他小技巧 |
| 299 | +
|
| 300 | +**1、`yes` 命令自动输入字符 `y` 进行确认**: |
| 301 | +
|
| 302 | +我们安装某些软件的时候,可能有交互式的提问: |
| 303 | +
|
| 304 | +```shell |
| 305 | +$ sudo apt install XXX |
| 306 | +... |
| 307 | +XXX will use 996 MB disk space, continue? [y/n] |
| 308 | +``` |
| 309 | +
|
| 310 | +一般情况下我们都是一路 y 到底,但如果我们想自动化一些软件的安装就很烦,遇到这种交互式提问就卡住了,还得手动处理。 |
| 311 | +
|
| 312 | +`yes` 命令可以帮助我们: |
| 313 | +
|
| 314 | +```shell |
| 315 | +$ yes | your_cmd |
| 316 | +``` |
| 317 | +
|
| 318 | +这样就会一路自动 `y` 下去,不会停下让我们输入了。 |
| 319 | +
|
| 320 | +如果你读过前文 [Linux 文件描述符](https://labuladong.github.io/article/fname.html?fname=linux进程),就知道其原理很简单: |
| 321 | +
|
| 322 | +你单独运行一下 `yes` 命令,发现它就是打印出一大堆字符 y,通过管道把输出和 `your_cmd` 的标准输入相连接,如果 `your_cmd` 又提出无聊的问题,就会从标准输入读取数据,也就会读取到一个 y 和换行符,和你手动输入 y 确认是一个效果。 |
| 323 | +
|
| 324 | +**2、特殊变量 `$?` 记录上一次命令的返回值**。 |
| 325 | +
|
| 326 | +在 Linux shell 中,遵循 C 语言的习惯,返回值为 0 的话就是程序正常退出,非 0 值就是异常退出出。读取上一次命令的返回值在平时使用命令行时感觉没什么用,但是如果你想编写一些 shell 脚本,知道返回值非常有用。 |
| 327 | +
|
| 328 | +**举个实际的例子**,比如我的 Github 仓库 fucking-algorithm ,我需要给其中所有 markdown 文件最下方添加上一篇、下一篇、目录三个页脚链接,有的文章已经有了页脚,大部分都没有。 |
| 329 | +
|
| 330 | +为了防止重复添加,我必须知道一个 md 文件下方是否已添加,这时候就可以使用 `$?` 变量配合 `grep` 命令做到: |
| 331 | +
|
| 332 | +```shell |
| 333 | +#!/bin/bash |
| 334 | +filename=1ドル |
| 335 | +# 查看文件尾部是否包含关键词 |
| 336 | +tail | grep '下一篇' $filename |
| 337 | +# grep 查找到匹配会返回 0,找不到则返回非 0 值 |
| 338 | +[ $? -ne 0 ] && { 添加页脚; } |
| 339 | +``` |
| 340 | +
|
| 341 | +**3、特殊变量 `$$` 记录当前进程的 PID**。 |
| 342 | +
|
| 343 | +这个功能可能在平时使用时也不怎么用,但是在写 shell 脚本时也非常有用,比如说你要在 `/tmp` 创建临时文件,给文件起名字一直都是非常让人费脑子的,这时候可以使用 `$$` 变量扩展出当前进程的 PID 作为临时文件名,PID 在计算机中都是唯一的,所以绝不会重复,也不需要你记住临时文件的名字。 |
| 344 | +
|
| 345 | +好了,今天就分享这些技巧吧,如果大家对 Linux 有兴趣,可以点在看分享,数据不错的话下次再写点。 |
| 346 | +
|
149 | 347 |
|
150 | 348 |
|
151 | 349 | <hr>
|
152 | 350 | <details>
|
153 | 351 | <summary><strong>引用本文的文章</strong></summary>
|
154 | 352 |
|
155 | | - - [Linux 管道符原理大揭秘](https://labuladong.github.io/article/fname.html?fname=linux技巧3) |
| 353 | + - [Linux 管道和重定向的坑](https://labuladong.github.io/article/fname.html?fname=linux技巧3) |
156 | 354 |
|
157 | 355 | </details><hr>
|
158 | 356 |
|
|
0 commit comments