整理一些常见 Bash 的使用技巧,例如 Here Document、特殊字符文件处理、字符集设置等。
Here Document
使用 echo 添加到文件比较麻烦,如果存在 $
则需要做转义,或者使用 EOF
也可以,如下是示例。
$ cat << EOF >> /tmp/foobar.conf
net.core.rmem_default = 262144
net.core.rmem_max = 262144
net.core.wmem_default = 262144
net.core.wmem_max = 262144
export PATH=\$PATH:\$HOME/bin
EOF
在此使用的就是 Here Document,这是一种在 Linux Shell 中的一种特殊的重定向方式,它的基本的形式如下,通常 delimiter 使用 EOF :
cmd << delimiter
Here Document Content
delimiter
也可以在终端中输入 cat << EOF
,然后输入多行信息,最终以 EOF 结束,其中间输入的信息将会回显在屏幕上。
另外,可以通过 <<-
删除 Here Document 的内容部分每行前面的 tab (制表符) , 这种用法是为了编写 Here Document 的时候可以将内容部分进行缩进,方便阅读代码。
特殊字符文件处理
在 Linux 中,文件名最大长度可达 256 字符,可用字符有字母、数字、'.'
(点)、'_'
(下划线)、'-'
(连字符)、' '
(空格),其中开始字符不建议使用 '_'
、'-'
、' '
字符,'/'
(反斜线) 用于标示目录,不能用作文件或者文件夹名称。
另外,在 shell 中,'?'
(问号)、'*'
(星号)、'&'
字符有特殊含义,同样不建议使用。
在 shell 中,将 --
之后的内容当作文件。
$ cd .>-a # 创建一个文件,或者 >-a
$ vi -- -a # 编辑,或者 echo "">-a
$ rm -- -a # 删除,或者 rm ./-a
$ touch '><!*' # 创建
$ touch '?* $&' # 创建
对于这样的文件,可以执行如下操作。
----- 将非乱码的文件移出到某个目录下
$ find . -name "[a-z|A-Z]*" | xargs -I {} mv {} /somepath
----- 也可以通过inode删除
$ ls -i
$ find -inum XXX | xargs -I {} rm {}
$ find -inum XXX -delete
如果文件的文件名含有终端无法正确显示的字符,那么可以通过 inode 来删除,处理命令如下。
----- 查看文件innode
# ls -li
total 0
358315 -rw-r--r-- 1 root root 0 Apr 6 23:13 ???}
----- 通过inode删除文件,如下两种方式相同
# find . -inum 358315 -delete
# rm -i `find . -maxdepth 1 -inum 358315 -print`
字符集设置
程序运行时需要使用一套语言环境,包括了:字符集 (数据) 和字体 (显示),在 Linux 中通过 locale 来设置程序运行的不同语言环境,locale 由 ANSI C 提供支持,可以根据不同的国家地区设置不同的语言环境。
locale 的命名规则为 <语言>_<地区>.<字符集编码>
,例如 zh_CN.UTF-8
中 zh
代表中文,CN
代表大陆地区,UTF-8
表示字符集。另外,在 locale 会通过一组环境变量,针对不同场景配置。
LC_COLLATE
定义该环境的排序和比较规则
LC_CTYPE
用于字符分类和字符串处理,控制所有字符的处理方式,包括字符编码,字符是单字节还是
多字节,如何打印等,是最重要的一个环境变量。
LC_MONETARY
货币格式。
LC_NUMERIC
非货币的数字显示格式。
LC_TIME
时间和日期格式。
LC_MESSAGES
提示信息的语言,与之详细的还有LANGUAGE参数,当LANGUAGE设置后LC_MESSAGES将会失效,
而且可同时设置多种语言信息,如LANGUANE="zh_CN.GB18030:zh_CN.GB2312:zh_CN"。
LANG
LC_*的默认值,是最低级别的设置,如果LC_*没有设置,则使用该值。
LC_ALL
一个宏,如果该值设置了,则该值会覆盖所有LC_*的设置值。注意,LANG的值不受该宏影响。
简单介绍下常见的操作。
----- 设置成中文环境
export LANG="zh_CN.UTF-8"
export LANGUAGE="zh_CN:zh:en_US:en"
----- 设置成英文环境
export LANG="en_US.UTF-8"
export LANGUAGE="en_US:en"
----- 查看现有语言环境
$ locale
----- 所有可用语言环境
$ locale -a
注意,图形界面可能需要更多的设置,暂时先不讨论了。
获取脚本绝对路径
在使用脚本时经常会有诉求获取当前脚本的路径,如果简单使用 PWD=$(dirname $0)
命令,容易导致使用不同的命令行导致不同的值,脚本如下。
#!/bin/bash
PWD=$(dirname $0)
cd /opt
cp hello.txt ${PWD}
本意是将 /opt/hello.txt
文件复制到脚本所在路径,但是当使用不同的调用方式时,实际得到的 PWD
值是不同的,如下是不同脚本以及 pwd
命令返回的结果。
执行脚本 | ${PWD} 变量值 | pwd 命令 |
---|---|---|
sh test.sh | . | /tmp/demo |
sh ./test.sh | . | /tmp/demo |
sh demo/test.sh | demo | /tmp |
sh ./demo/test.sh | ./demo | /tmp |
sh /tmp/demo/test.sh | /tmp/demo | / |
正确的方式是 PWD=$(dirname $(readlink -f $0))
也就是先通过 readlink
命令获取绝对路径,然后再取出目录,这样获取的 $PWD
值始终为 /tmp/demo
目录了。
其它
目录合并
可以将两个目录通过 cp -r dir1/* dir2/* merged/
进行合并,由于是复制,对于较大的文件会导致速度较慢。如果通过 mv
则会报 Directory not empty
的错误。
可以通过如下命令采用硬链接的方式进行复制,通过 tree --inodes
命令查看文件的 inode 号。
$ cp -r --link dir1/* dir2/* merged/
日期转换
----- 日期转换
$ date +%s -d "04/24/2014 15:30:00" // 将日期转换成时间戳
$ date -d @1398324600 // 将时间戳转换成日期
$ date +%s // 将当前日期转换成时间戳