友链
导航
These are the good times in your life,
so put on a smile and it'll be alright
友链
导航
stty -ixon
. Unable to forward search Bash history similarly as with CTRL-r - Stack Overflow
github 上有 bash-it, 用来增强 bash 功能, 有时间看看. 这个是模仿了 oh-my-zsh — Xiaopei 2013/08/07 22:47
任何语言的应用中实现 bash completion 可参考 https://github.com/iamfat/gini/blob/master/data/gini-completion.bash:
#!/bin/bash _gini() { COMPREPLY=() local cur cur=$(_get_cword) unset COMP_WORDS[0] case "$cur" in @) COMPREPLY=($( compgen -W "$(gini -- ${COMP_WORDS[@]})" -- "$cur" )) ;; *) COMPREPLY=( $( compgen -W "$(gini -- ${COMP_WORDS[@]})" -- "$cur" ) ) ;; esac return 0 } && complete -F _gini gini
主要参考 Scripting with style [Bash Hackers Wiki]
activate some_very_long_option \ some_other_option
HEAD_KEYWORD parameters; BODY_BEGIN BODY_COMMANDS BODY_END
if ...; then ... elif ...; then ... else ... fi
for f in /etc/*; do ... done
while [[ $answer != [YyNn] ]]; do ... done
case $input in hello) echo "You said hello" ;; bye) echo "You said bye" if foo; then bar fi ;; *) echo "You said something weird..." ;; esac
MY_
), 以避免与Environment Variables冲突my_input="" my_array=() my_number=0
list="one two three" # you MUST NOT quote $list here for word in $list; do ... done
系统命令
类似getargs() { ... }
“$( … )”
, 如now="$(date)"
#!SHEBANG CONFIGURATION_VARIABLES FUNCTION_DEFINITIONS MAIN_CODE
while true; do echo "$(date) $(nc -z baidu.com 80; echo $?)" >> conn2baidu.log sleep 2 done
若想通用的 UNIX 工具实现锁, 则应使用 mkdir. 因为 mkdir:
而 touch 文件实现锁需要两步 ↓ 不是原子操作, 所以不推荐:
if
)touch
)一个简单的 mkdir 互斥锁(MUTEX) 如下:
if mkdir /var/lock/mylock; then echo "Locking succeeded" >&2 else echo "Lock failed - exit" >&2 exit 1 fi
I use math in bash scripts a lot, from simple crontab reports to Nagios monitoring plugins… Here is few small examples on how to do some maths in Bash with integers or float.
Integer Math
First way to do math with integer (and only integer) is to use the command “expr — evaluate expression“.
1 2 3 4 5 6 7 8 9 10 11 | Mac-n-Cheese:~ nicolas$ expr 1 + 1 2 Mac-n-Cheese:~ nicolas$ myvar=$( expr 1 + 1) Mac-n-Cheese:~ nicolas$ echo $myvar 2 Mac-n-Cheese:~ nicolas$ expr $myvar + 1 3 Mac-n-Cheese:~ nicolas$ expr $myvar / 3 1 Mac-n-Cheese:~ nicolas$ expr $myvar \* 3 9 |
When doing a “multiply by” make sure to backslash the “asterisk” as it’s a wildcard in Bash used for expansion.
Another alternative to expr, is to use the bash builtin command let.
1 2 3 4 5 6 7 8 9 10 11 | Mac-n-Cheese:~ nicolas$ echo $myvar 6 Mac-n-Cheese:~ nicolas$ let myvar+=1 Mac-n-Cheese:~ nicolas$ echo $myvar 7 Mac-n-Cheese:~ nicolas$ let myvar+1 Mac-n-Cheese:~ nicolas$ echo $myvar 7 Mac-n-Cheese:~ nicolas$ let myvar2=myvar+1 Mac-n-Cheese:~ nicolas$ echo $myvar2 8 |
Also, you can simply use the parentheses or square brackets :
1 2 3 4 5 6 7 | Mac-n-Cheese:~ nicolas$ echo $myvar 3 Mac-n-Cheese:~ nicolas$ echo $((myvar+2)) 5 Mac-n-Cheese:~ nicolas$ echo $[myvar+2] 5 Mac-n-Cheese:~ nicolas$ myvar=$((myvar+3)) |
This allow you to use C-style programming :
1 2 3 4 5 6 7 8 9 10 | Mac-n-Cheese:~ nicolas$ echo $myvar 3 Mac-n-Cheese:~ nicolas$ echo $((myvar++)) 3 Mac-n-Cheese:~ nicolas$ echo $myvar 4 Mac-n-Cheese:~ nicolas$ echo $((++myvar)) 5 Mac-n-Cheese:~ nicolas$ echo $myvar 5 |
Floating point arithmetic
If you need to do floating point arithmetic, you will have to use a command line tool, the most common one is “bc – An arbitrary precision calculator language“.
1 2 3 4 5 6 7 8 9 | Mac-n-Cheese:~ nicolas$ bc bc 1.06 Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc. This is free software with ABSOLUTELY NO WARRANTY. For details type `warranty'. 3*5.2+7 /8 15.6 15.6+299.33*2.3 /7 .4 108.6 |
Of course you can use the STDIN to send your formula to “bc” then get the output on STDOUT.
1 2 | Mac-n-Cheese:~ nicolas$ echo "3.4+7/8-(5.94*3.14)" | bc -15.25 |
I encourage you too take a look at the man pages to get more detail on how it works (man bc).
There are four special variables, scale, ibase, obase, and last. scale defines how some operations use digits after the decimal point. The default value of scale is 0. ibase and obase define the conver-
sion base for input and output numbers. The default for both input and output is base 10. last (an extension) is a variable that has the value of the last printed number.
The “scale” variable is really important for the precision of your results, especially when using integers only (Note: you can also use “bc -l” to use mathlib and see the result at max scale) .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | Mac-n-Cheese:~ nicolas$ echo "2/3" | bc 0 Mac-n-Cheese:~ nicolas$ echo "scale=2; 2/3" | bc .66 Mac-n-Cheese:~ nicolas$ echo "(2/3)+(7/8)" | bc 0 Mac-n-Cheese:~ nicolas$ echo "scale=2;(2/3)+(7/8)" | bc 1.53 Mac-n-Cheese:~ nicolas$ echo "scale=4;(2/3)+(7/8)" | bc 1.5416 Mac-n-Cheese:~ nicolas$ echo "scale=6;(2/3)+(7/8)" | bc 1.541666 Mac-n-Cheese:~ nicolas$ echo "(2/3)+(7/8)" | bc -l 1.54166666666666666666 |
You can also use the here-doc notation to pass your formula to bc :
1 2 | Mac-n-Cheese:~ nicolas$ bc -l <<< "(2/3)+(7/8)" 1.54166666666666666666 |
# clean line end spaces alias clend='sed -i "s/[ \t]*$//"' alias clendgitdiff='clend `git diff --name-only`' alias clendgitstaged='clend `git diff --staged --name-only`; git add `git diff --name-only`' alias regxp_debug='perl -Mre=debug -e ' # safe rm(xiaopei.li@2012-12-13) rm() { RMDIR=/home/xiaopei.li/.recycle/`date +%s`/ mkdir $RMDIR for var in "$@"; do is_arg=`echo $var | grep '^\-'` if [[ ! $is_arg ]]; then mv "$var" $RMDIR fi done }
debugging - How do you debug a regex? - Stack Overflow
$ perl -Mre=debug -e'"foobar"=~/(.)\1/' Compiling REx "(.)\1" Final program: 1: OPEN1 (3) 3: REG_ANY (4) 4: CLOSE1 (6) 6: REF1 (8) 8: END (0) minlen 1 Matching REx "(.)\1" against "foobar" 0 <> <foobar> | 1:OPEN1(3) 0 <> <foobar> | 3:REG_ANY(4) 1 <f> <oobar> | 4:CLOSE1(6) 1 <f> <oobar> | 6:REF1(8) failed... 1 <f> <oobar> | 1:OPEN1(3) 1 <f> <oobar> | 3:REG_ANY(4) 2 <fo> <obar> | 4:CLOSE1(6) 2 <fo> <obar> | 6:REF1(8) 3 <foo> <bar> | 8:END(0) Match successful! Freeing REx: "(.)\1"
test expr
和 [ expr ]
是等价的, 需注意中括号与其中的内容间需有空格$?
检查返回值&&
和 ||
操作返回值操作符 | 特征 |
---|---|
-d | 目录 |
-e | 存在(也可以用 -a) |
-f | 普通文件 |
-h | 符号连接(也可以用 -L) |
-p | 命名管道 |
-r | 可读 |
-s | 文件存在且非空(不应用此法检测目录是否为空) |
-S | 套接字 |
-w | 可写 |
-N | 从上次读取之后已经做过修改 |
-n | 测试 file1 是否比 file2 更新。修改日期将用于这次和下次比较。 |
-o | 测试 file1 是否比 file2 旧。 |
-e | 测试 file1 是不是 file2 的硬链接。 |
[ “$(ls -A $DIR)” ]
$ test 3 -gt 4 && echo True || echo false false $ [ "abc" != "def" ];echo $? 0 $ test -d "$HOME" ;echo $? 0
$ test "abc" = "def" ;echo $? 1 $ [ "abc" != "def" ];echo $? 0 $ [ "abc" \< "def" ];echo $? 0 $ [ "abc" \> "def" ];echo $? 1 $ [ "abc" \<"abc" ];echo $? 1 $ [ "abc" \> "abc" ];echo $? 1
$ set +o nounset $ [ -o nounset ];echo $? 1 $ set -u $ test -o nounset; echo $? 0
$ test "a" != "$HOME" -a 3 -ge 4 ; echo $? 1 $ [ ! \( "a" = "$HOME" -o 3 -lt 4 \) ]; echo $? 1 $ [ ! \( "a" = "$HOME" -o '(' 3 -lt 4 ')' ")" ]; echo $? 1
(( ))
复合命令计算算术表达式,如果表达式求值为 0,则设置退出状态为 False(1);如果求值为非 0 值,则设置为 True(0)。不需要对 (( 和 )) 之间的操作符转义。算术只对整数进行。除 0 会产生错误,但不会产生溢出。可以执行 C 语言中常见的算术、逻辑和位操作。 [[ ]]
可以对文件名和字符串使用更自然的语法[ian@pinguino ~]$ let x=2 y=2**3 z=y*3;echo $? $x $y $z 0 2 8 24 [ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w 0 3 8 16 [ian@pinguino ~]$ (( w=(y/x) + ( (~ ++x) & 0x0f ) )); echo $? $x $y $w 0 4 8 13
[ian@pinguino ~]$ [[ ( -d "$HOME" ) && ( -w "$HOME" ) ]] && > echo "home is a writable directory" home is a writable directory
[ian@pinguino ~]$ [[ "abc def .d,x--" == a[abc]*\ ?d* ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def c" == a[abc]*\ ?d* ]]; echo $? 1 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* ]]; echo $? 1
[ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || (( 3 > 2 )) ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 -gt 2 ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || 3 > 2 ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a > 2 ]]; echo $? 0 [ian@pinguino ~]$ [[ "abc def d,x" == a[abc]*\ ?d* || a -gt 2 ]]; echo $? -bash: a: unbound variable
[ian@pinguino ~]$ function mycalc () > { > local x > if [ $# -lt 1 ]; then > echo "This function evaluates arithmetic for you if you give it some" > elif (( $* )); then > let x="$*" > echo "$* = $x" > else > echo "$* = 0 or is not an arithmetic expression" > fi > }
在 shell 脚本中检测某服务器的某端口是否可访问
#!/bin/bash # 定时运行, 检测本地 memcached(port 11211) 连接是否正常 # 若不正常, 则重启 memcached, 并发信通知管理员 # (xiaopei.li@2012-04-28) exec 3>/dev/tcp/localhost/11211 if [ $? -ne 0 ] then service memcached restart echo 'memcached has a problem and restarted ' | sendmail admin@example.com fi
if diff foo bar > /dev/null 2>&1 ; then echo same fi
正确的用法是 >/dev/null 2>&1
, 而非 2>&1 >/dev/null
. 因为后者会先将 2
重定向到当时的 1
, 即 stdout
, 便会输出
经常可以在一些脚本,尤其是在crontab调用时发现如下形式的命令调用
/tmp/test.sh > /tmp/test.log 2>&1
前半部分/tmp/test.sh > /tmp/test.log
很容易理解,那么后面的2>&1
是怎么回事呢?
要解释这个问题,还是得提到文件重定向。我们知道>和<是文件重定向符。那么1和2是什么?在shell中,每个进程都和三个系统文件相关联:标准输入stdin,标准输出stdout和标准错误stderr,三个系统文件的文件描述符分别为0,1和2。所以这里2>&1的意思就是将标准错误也输出到标准输出当中。
下面通过一个例子来展示2>&1有什么作用:
$ cat test.sh t date
test.sh中包含两个命令,其中t是一个不存在的命令,执行会报错,默认情况下,错误会输出到stderr。date则能正确执行,并且输出时间信息,默认输出到stdout
./test.sh > test1.log ./test.sh: line 1: t: command not found $ cat test1.log Tue Oct 9 20:51:50 CST 2007
可以看到,date的执行结果被重定向到log文件中了,而t无法执行的错误则只打印在屏幕上。
$ ./test.sh > test2.log 2>&1 $ cat test2.log ./test.sh: line 1: t: command not found Tue Oct 9 20:53:44 CST 2007
这次,stderr和stdout的内容都被重定向到log文件中了。
实际上, > 就相当于 1> 也就是重定向标准输出,不包括标准错误。通过2>&1,就将标准错误重定向到标准输出了,那么再使用>重定向就会将标准输出和标准错误信息一同重定向了。如果只想重定向标准错误到文件中,则可以使用2> file。
2>1的意思是将stderr重定向输出到名字为1的文件中了
&1是引用stdout的文件句柄,也就是将stderr合并到stdout中去