Bash
Quoting
IFS=$'\n'
文档中这么说的:
Bash introduces two additional forms of quoting. The first is $'...' which acts like single quotes except that backslash-escaped combinations are expanded as specified by the ANSI C standard. This allows a convenient way to embed nonprintable characters into strings, or to pass them as arguments.
Aliases
\rm file
跳过rm的任何alias。在Bash手册的alias节:
The first word of each simple command, if unquoted, is checked to see if it has an alias.
Aliases不被子进程继承,这一点在Bash的官方文档里面似乎没有指出。
Grouping Commands
for的do...done可以用{}来代替(Grouping Commands):
for file in /usr/sbin/sendmail /usr/bin/mailq /usr/bin/newaliases; { echo -n "$file: " && rpm -q --whatprovides ${file}; }
在Bashref和ABS里面尚未看到有相关的说明。
read
$ read -p input:
input:abc
$ echo $REPLY
abc
$ read -p input: 2>/dev/null
abc
$ echo $REPLY
abc
可以看到如果把stderr重定向到null的话,read的提示符就出不来了。没有看源代码,我猜测-p参数后的提示符是写到stderr的。如果在运行脚本的时候加上2>/dev/null,而正好脚本里又有read -p <prompt>命令,那么这个提示符是看不到的。可用echo <prompt>代替-p prompt。这个应该是有意这么设计的,不要让提示符影响stdout的内容。
Command Substitution
我用
<<< $(tail -n +3 /proc/net/dev)
来做一个输入定向,本来是多行内容,这样之后合并成了一行。经过研究,发现是$()的问题。Bash的文档中说:
Embedded newlines are not deleted, but they may be removed during word splitting.
以及:
If the substitution appears within double quotes, word splitting and filename expansion are not performed on the results.
就是说如果没有用双引号把Command Substitution的内容保护起来,会做word splitting,然后中间的换行符会删除掉,所以就连成一行了。改成
<<< "$(tail -n +3 /proc/net/dev)"
就好了,输出还是多行。
匹配所有.开头的文件
ls -A | grep '^\.'
或者
shopt -s extglob
shopt -s dotglob
ls -ad *(.*)
ls -ad .!(.|)
但是在打开dotglob的情况下,*(.*)不匹配.和..,而.*是匹配的:
$ diff <(ls -d *(.*)) <(ls -d .*)
0a1,2
> .
> ..
这个需要进行澄清,应该也是bug。还有,在不打开dotglob的情况下,*(.*)什么都不匹配:
$ shopt -u dotglob
$ ls -d *(.*)
ls: cannot access *(.*): No such file or directory
已汇报bug。
文本部分输出
输出前20个字符:
$ cut -b -20 pi
3.141592653589793238
或者:
$ grep -oE '^.{20}' pi
3.141592653589793238
或者:
$ read -n 20 <pi && echo $REPLY
3.141592653589793238
输出第30~40个字符呢:
$ cut -b 30-40 pi
27950288419
复制文件并覆盖链接
如果当前目录下有一个符号链接,想把这个链接改成被链文件的副本,一般是删除链接再把原文件复制过来。其实可以用cp的--remove-destination选项一次复制的:
$ ls -l foo
lrwxrwxrwx 1 tux tux 6 2011-12-31 20:02 foo -> ../foo
$ cp ../foo .
cp: `../foo' and `./foo' are the same file
$ ls -l foo
lrwxrwxrwx 1 tux tux 6 2011-12-31 20:02 foo -> ../foo
$ cp --remove-destination ../foo .
$ ls -l foo
-rwxr-x--x 1 tux tux 0 2011-12-31 20:03 foo
时间
手动设置系统时间(来自info date):
date -d "$(LC_TIME=C date)"
后面的date运行结果从一个正确的机器的来,例如:
Sun Apr 1 22:40:43 CST 2012
>
重定向的时候,如果内容是整数,那么整数和>之间要有空格,否则整数会被Bash理解为文件描述符。
$ echo 2>a
$ cat a
$ echo 2 >a
$ cat a
2
用find .是不行的,要如下:
find */* -type l
!
echo "haha!"这个语句在交互方式下执行是不行的,因为!是History expansion character,只能用\或者单引号来保护。但是这个语句在脚本里面是正确的,因为和历史相关的东西在脚本下都失效的。
Here Document
最后的delimiter必须前后没有空白和其它字符,并且独占一行。
并行执行命令
例如,把当前目录下的图片都旋转90度:
while read -r img; do (mogrify -rotate 180 "$img" &) ; done < <(ls)
do后面的语句要加括后才行,否则语法错误。
还可以用xargs,parallel等工具。