Bash One-Liners Explained 译文(五)

这是 Bash One-Liners Explained 系列的第五篇文章。在这一部分,我会教你如何快速在 Bash 命令行中使用 Emacs 风格的键盘导航快捷键。

0. 行编辑模式介绍

Bash 使用 GNU readline 库来提供行编辑特性。readline 库同时支持 Emacs 风格和 Vi 风格的快捷键绑定,也支持用户去做自定义绑定。默认情况下,readline 会使用 Emacs 风格的键绑定,不过你可以很方便的切换到 Vi 风格,或者自定义设置。

执行set -o emacs命令切换到 Emacs 风格,set -o vi则会切换到 Vi 风格。

除此之外,你仍可以通过~/.inputrc或者bind命令来自定义快捷键绑定。例如,bind '"\C-f": "ls\n"'CTRL+F绑定为执行ls命令。你可以通过查阅 Bash 手册中的 readline 一节来更多地了解 readline 的快捷键绑定语法。

1. 移动光标到行首

CTRL + a

2. 移动光标到行尾

CTRL + e

3. 光标往后(向左)移动一个单词

ESC + b 或者 ALT + b

4. 光标往前(向右)移动一个单词

ESC + f 或者 ALT + f

5. 删除上一个单词

CTRL + w

继续阅读

Bash One-Liners Explained 译文(四)

这是 Bash One-Liners Explained 系列的第四篇文章。在这一篇里,我会给大家介绍 Bash 命令行历史功相关的内容。我会选择用最合适的 Bash 方法,各种常见的语法和技巧,向各位阐明如何用 Bash 内置的命令和 Bash 编程语言来完成各式各样的任务。

1. 清除命令行历史

$ rm ~/.bash_history

Bash 将历史执行的命令都保存在文件.bash_history中,该文件位于你的家目录下。为了清除命令行历史,只要把这个文件删除即可。

注意,当你执行完退出后,最后一个rm ~/.bash_history命令依然会被记录下来。如果你想隐藏清除的操作命令,请看下一条。

2. 当前会话下停止记录命令行历史

$ unset HISTFILE

环境变量HISTFILE指向命令行执行历史保存的目标文件路径,如果你重置了该变量,Bash 就不会保存历史。

另外一种方法是将它指向/dev/null

$ HISTFILE=/dev/null

3. 不要记录当前执行的命令

很简单,只要在命令之前加空格就行:

$  command

注意,以上正常工作的前提是,HISTIGNORE变量被正确的设置,它的值是冒号分隔的匹配表达式列表,如果一个命令匹配其中的任意一个表达式则不会被保存到记录中。

继续阅读

Bash One-Liners Explained 译文(三)

最近工作,生活中有一堆事情要处理,博客好久没更新了,请见谅。

拖了好久才翻译好这篇文章,这篇翻译下来好累,主要是原文废话太多了,后来还是决定尽量省略部分内容,建议有时间的还是去看下原文。这一篇太长了,先翻译部分,我会继续不断的更新。

这是 Bash One-Liners Explained 系列的第三篇文章。在这一篇里,我会给大家介绍重定向相关的内容。我会选择用最合适的 Bash 方法,各种常见的语法和技巧,向各位阐明如何用 Bash 内置的命令和 Bash 编程语言来完成各式各样的任务。

重定向其实是通过操作文件描述符来完成的,这样会更容易理解。当 Bash 启动时,会自动创建三个标准的文件描述符,它们分别是 stdin(标准输入,文件描述符为0),stdout(标准输出,文件描述符为1)和 stderr(标准错误输出,文件描述符为2)。你也可以创建更多的文件描述符,例如3,4,5等等,或者关闭它们,又或者拷贝它们。你可以从对应的文件中读取或者写入内容。

文件描述符指向某个文件(除非它们被关闭)。通常情况下,Bash 启动的三个文件描述符 —— stdin,stdout 和 stderr 都是指向你的终端,从终端输入中读取内容,并且把标准输出和标准错误都送到终端上。

继续阅读

Bash One-Liners Explained 译文(二)

这是 Bash One-Liners Explained 系列的第二篇文章。在这一篇里,我会给你们介绍如何用 Bash 来完成各种各样的字符串操作。我会选择用最合适的 Bash 方法,各种常见的语法和技巧,向各位阐明如何用 Bash 内置的命令和 Bash 编程语言来完成各式各样的任务。

1. 生成从 a 到 z 的字母表

$ echo {a..z}

这一行命令用到了括号展开(Brace expansion)功能,它可以用于生成任意的字符串。{x..y} 是一个序列表达式,其中 x 和 y 都是单个字符,这个表达式展开后包含 x 与 y 之间的所有字符。

运行上面的命令会生成从 a 到 z 的所有字母:

$ echo {a..z}
a b c d e f g h i j k l m n o p q r s t u v w x y z

2. 生成从 a 到 z 的字母表,字母之间不包含空格

$ printf "%c" {a..z}

这是一个 99.99% 的人都不知道的非常棒的技巧。如果你在printf命令之后指定一个列表,最终它会循环依次打印每个元素,直到完成为止。

在这一行命令中,printf 的格式为"%c",代表一个字符(character),后面的参数是从 a 到 z 的字符列表,字符之间以空格分隔。所以,当printf执行时,它依次输出每个字符直到所有字符全被处理完成为止。

下面是执行的结果:

abcdefghijklmnopqrstuvwxyz

输出的结果最后不包含换行符,因为printf的输出格式是"%c",其中并没有包含\n。如果你想输出完整的一行,可以简单地在字符列表后面增加一个$'\n'

$ printf "%c" {a..z} $'\n'

继续阅读

Bash One-Liners Explained 译文(一)

Bash One-Liners Explained 是一系列介绍 Bash 命令技巧的文章,由国外牛人 Peteris Krumins 撰写。凭借扎实的功底和丰富的经验,作者总结了许多快速解决问题的技巧,并且每一条都只要用简洁的一行 Bash 命令就可以完成,同时每一行命令文中都给出了非常详尽的解释。

Peteris Krumins 是一位高产的博主,在他的博客上有很多非常精彩的文章,推荐大家有机会都可以去好好读一读。例如,大家耳熟能详的 Awk One-Liners ExplainedSed One-Liners Explained 等等。后者我也北曾经在博客上分享过一篇笔记

回到正题,虽然这一系列文章不难,但是还是可以从中学到很多细节的知识,相信这些肯定会对许多初学者有所帮助,所以我打算将这一系列翻译成中文,分享给大家。为了同原文保持一致,这一系列文章最终会分成以下五篇:

  1. Bash One-Liners Explained 译文(一): 文件处理
  2. Bash One-Liners Explained 译文(二): 操作字符串
  3. Bash One-Liners Explained 译文(三): 漫谈重定向
  4. Bash One-Liners Explained 译文(四): 历史命令
  5. Bash One-Liners Explained 译文(五): 命令行跳转

本系列的文章同其它系列一样,最终都可以在连载页面找到,有兴趣的同学可以随意翻翻,看看有没有一些对你有价值的文章,大家一起交流学习。

1. 清空文件内容

$ > file

这一行命令用到了输出重定向操作符>。输出重定向发生时,文件会被打开准备写入。如果此时文件不存在则先创建,存在则将其大小截取为0。这里我们并没有重定向写任何内容到文件中,所以文件依然保持为空。

如果你想替换文件的内容,或者创建一个包含指定内容的文件,可以运行下面的命令:

$ echo "some string" > file

2. 追加内容到文件

$ echo "foo bar baz" >> file

继续阅读

Bash Pitfalls: 编程易犯的错误(四)

上一篇文章参见Bash Pitfalls: 编程易犯的错误(三)。这一篇翻译得不是非常满意,时间比较赶,请见谅,如果有问题可以在本文后方留言,大家一起深入探讨。

36. [ -n $foo ] or [ -z $foo ]

这个例子中,$foo 没有用引号引起来,当$foo包含空格或者$foo为空时都会出问题:

$ foo="some word" && [ -n $foo ] && echo yes
-bash: [: some: binary operator expected

$ foo="" && [ -n $foo ] && echo yes
yes

正确的写法是:

[ -n "$foo" ]
[ -z "$foo" ]
[ -n "$(some command with a "$file" in it)" ]

[[ -n $foo ]]
[[ -z $foo ]]

37. [[ -e "$broken_symlink" ]] returns 1 even though $broken_symlink exists

这里-e 选项是看文件是否存在,当紧跟的文件是一个软链接时,它不看软链接是否存在,而是看实际指向的文件是否存在。所以当软链接损坏时,即实际指向的文件被删除后,-e 的结果返回1。

继续阅读

Bash Pitfalls: 编程易犯的错误(三)

上一篇文章参见Bash Pitfalls: 编程易犯的错误(二)

24. for arg in $*

和大多数 Shell 一样,Bash 支持依次读取单个命令行参数的语法。不过这并是$*或者$@,这两种写法都不正确,它们只能得到完整的参数列表,并非单独的一个个参数。

正确的语法是(没错要加上引号):

for arg in "$@"

# 或者更简单的写法
for arg

在脚本中遍历所有参数是一个再普遍不过的需求,所以 for arg 默认等价于 for arg in "$@"。$@使用双引号后就有特殊的魔力,每个参数展开后成为一个独立的单词。("$@"等价于"$1" "$2" "$3" ...)

下面是一个错误的例子:

for x in $*; do
   echo "parameter: '$x'"
done

执行的结果为:

$ ./myscript 'arg 1' arg2 arg3
parameter: 'arg'
parameter: '1'
parameter: 'arg2'
parameter: 'arg3'

正确的写法:

for x in "$@"; do
   echo "parameter: '$x'"
done

执行的结果为:

$ ./myscript 'arg 1' arg2 arg3
parameter: 'arg 1'
parameter: 'arg2'
parameter: 'arg3'

继续阅读

Bash Pitfalls: 编程易犯的错误(二)

上一篇文章参见Bash Pitfalls: 编程易犯的错误(一)

13. cat file | sed s/foo/bar/ > file

你不应该在一个管道中,从一个文件读的同时,再往相同的文件里面写,这样的后果是未知的。

你可以为此创建一个临时文件,这种做法比较安全可靠:

# sed 's/foo/bar/g' file > tmpfile && mv tmpfile file

或者,如果你用得是 GNU Sed 4.x 以上的版本,可以使用-i 选项即时修改文件的内容:

# sed -i 's/foo/bar/g' file

14. echo $foo

这种看似无害的命令往往会给初学者千万极大的困扰,他们会怀疑是不是因为 $foo 变量的值是错误的。事实却是因为,$foo 变量在这里没有使用双引号,所以在解析的时候会进行单词拆分文件名展开,最终导致执行结果与预期大相径庭:

msg="Please enter a file name of the form *.zip"
echo $msg

这里整句话会被拆分成单词,然后其中的通配符会被展开,例如*.zip。当你的用户看到如下的结果时,他们会怎样想:

Please enter a file name of the form freenfss.zip lw35nfss.zip

继续阅读