Shell编程积累

Shell编程积累
 
ls -lr反向排序结果
==============================
ls ${PATH//:/\ } | grep <searchword>
==============================
echo $RANDOM
==============================
[[ $# -ne 3 ]] && {echo "Usage: ${0##.*/} <param>"; exit 1}
==============================
awk '/'$VAR'/{print $0}' file
==============================
#得到绝对路径
bin=`dirname "$0"`
bin=`cd "$bin"; pwd`
==============================
echo $[23*34] <==> echo $((23*34))
==============================
echo $[7#23] #7是底,23是在这个底上的数字,因此最终的结果为17
==============================
echo ${!P*} #列出当前变量中,所有以P开头的变量的名称
==============================
shell的处理过程:alias la='ls -A'->{1..100}->~admin->$PATH->$(ls)->$((23*34))->rm a*o?[a-zA-Z]*
==============================
#下面这种处理方式等同于for f in "$(ls)",但是可以应对文件名中有空格的情况,而for f in "$(ls)"则不行。
[ $# -eq 0 ] && ls | while read f
do
    ll-2.sh "$f"
done
==============================
可以在一行内定义一个函数,写在shell脚本里可以,还可以直接写在命令行上,比如:
root@pc1:~#testfunc(){ echo "$# parameters;echo "$@";}
而且,如果你在命令行直接这么定义的,你想查看该函数的内容时,可以用type testfunc,你会看到:
testfunc is a function
testfunc ()
{
    echo "$# params";
    IFS=;
    echo "$*"
}
需要特别注意的是:{和echo之间的那个空格,其他地方有没有空格无所谓,但是这个空格如果没有的话,是必然会出错的;还有个地方是}前面那条命令的;必须有,否则也会有问题;最后,还需要提醒的是,如果你不是写在一行内,那么}前的;不必有,{后的空格也不必有。
===============================
对$*和$@作些说明:
其实要分四种情况:$*, $@, "$*", "$@"
对于前两种情况,都等同于$1 $2 $3 ...,如果某个参数内有了空格或者换行(不要觉得不可能,参数中是可以有换行符的,下面就有例子),比如a "b c" d,那么替换后的结果是a b c d,看上去就是四个参数了;
对于"$@",等同于"$1" "$2" "$3" ...,这下就不必担心参数中有空格或者换行符号了;
对于"$*",还要受到IFS的影响,其实是IFS中的第一个字符的影响,假定IFS中第一个字符是|的话,那么替换后的结果是"$1|$2|$3...";这里对IFS还得作下说明,如果 IFS 为空,则没有间隔空格。IFS 的默认值是空白、制表符和换行符。如果没有设置 IFS,则使用空白作为分隔符(仅对默认 IFS 而言),这里特别提到的是,如果你想把参数都串接起来,那么必须得显示设置IFS=''或者IFS=""或者IFS=即可。
下面给出一个验证上述描述的超级牛b的例子:
[ian@pinguino ~]$ type testfunc2
testfunc2 is a function
testfunc2 ()
{
    echo "$# parameters";
    echo Using '$*';
    for p in $*;
    do
        echo "[$p]";
    done;
    echo Using '"$*"';
    for p in "$*";
    do
        echo "[$p]";
    done;
    echo Using '$@';
    for p in $@;
    do
        echo "[$p]";
    done;
    echo Using '"$@"';
    for p in "$@";
    do
        echo "[$p]";
    done
}

[ian@pinguino ~]$ IFS="|${IFS}" testfunc2 abc "a bc" "1 2
> 3"
3 parameters
Using $*
[abc]
[a]
[bc]
[1]
[2]
[3]
Using "$*"
[abc|a bc|1 2
3]
Using $@
[abc]
[a]
[bc]
[1]
[2]
[3]
Using "$@"
[abc]
[a bc]
[1 2
3]
看到参数中使用换行符号了吧,哈哈。
===============================
函数返回值return 0~255必须为一个整数,而且范围只能是0~255,如果大于255,那么会返回$((x%256)),而且不能返回负数,但是允许你写负数,负数会被强制转换成整数。
===============================
#!/bin/sh会提供系统默认的shell解释器,会带来一些想不到的异常情况,建议都使用#!/bin/bash,在bash中定义函数时function关键字是可选的。
===============================
#!/bin/bash

showopts () {
  while getopts ":pq:" optname
    do
      case "$optname" in
        "p")
          echo "Option $optname is specified"
          ;;
        "q")
          echo "Option $optname has value $OPTARG"
          ;;
        "?")
          echo "Unknown option $OPTARG"
          ;;
        ":")
          echo "No argument value for option $OPTARG"
          ;;
        *)
        # Should not occur
          echo "Unknown error while processing options"
          ;;
      esac
    done
  return $OPTIND
}

showargs () {
  for p in "$@"
    do
      echo "[$p]"
    done
}

optinfo=$(showopts "$@")
argstart=$?
arginfo=$(showargs "${@:$argstart}")
echo "Arguments are:"
echo "$arginfo"
echo "Options are:"
echo "$optinfo"

 getopts 命令使用了两个预先确定的变量。OPTIND 变量开始被设为 1。之后它包含待处理的下一个参数的索引。如果找到一个选项,则 getopts 命令返回 true,因此常见的选项处理范例使用带 case 语句的 while 循环,本例中就是如此。getopts 的第一个参数是一列要识别的选项字母,在本例中是 p 和 r。选项字母后的冒号 (:) 表示该选项需要一个值;例如,-f 选项可能用于表示文件名,tar 命令中就是如此。此例中的前导冒号告诉 getopts 保持静默(silent)并抑制正常的错误消息,因为此脚本将提供它自己的错误处理。

此例中的第二个参数 optname 是一个变量名,该变量将接收找到选项的名称。如果预期某个选项应该拥有一个值,而且目前存在该值,则会把该值放入 OPTARG 变量中。在静默模式下,可能出现以下两种错误情况。

   1. 如果发现不能识别的选项,则 optname 将包含一个 ? 而 OPTARG 将包含该未知选项。
   2. 如果发现一个选项需要值,但是找不到这个值,则 optname 将包含一个 : 而 OPTARG 将包含丢失参数的选项的名称。

如果不是在静默模式,则这些错误将导致一条诊断错误消息而 OPTARG 不会被设置。脚本可能在 optname 中使用 ? 或 : 值来检测错误(也可能处理错误)。
此外,getopts ":pq:" optname后面还可以配置上可选的getopts ":pq:" optname "$@"

[ian@pinguino ~]$ ./testargs.sh -p -q qoptval abc "def ghi"
Arguments are:
[abc]
[def ghi]
Options are:
Option p is specified
Option q has value qoptval
[ian@pinguino ~]$ ./testargs.sh -q qoptval -p -r abc "def ghi"
Arguments are:
[abc]
[def ghi]
Options are:
Option q has value qoptval
Option p is specified
Unknown option r
[ian@pinguino ~]$ ./testargs.sh "def ghi"
Arguments are:
[def ghi]
Options are:
===============================
父shell设置的alias是不能传到子shell中的,但是在shell script中依然可以使用alias,不过要自己手动设定了,然后还要注意在脚本中显示开启alias的使用,即 shopt -s expand_aliases;有时候会希望alias能带参数就好了,其实可以用函数代替即可,在.bashrc中增加:cpdev1(){ [ $# -eq 1 ] && scp $1 [email protected]: ; },具体啥意思,自己去琢磨吧。
===============================
while [ "${i:-1}" -lt 30 ];do a="-"${a:="-"};((i++));done && echo $a
知识点:while的条件,不新建变量还能正常使用,while是个整体,可以有返回值,可以重定向其输出
===============================
如果想实现对shell变量的二次解析,那么可以用eval完成,例如:eval cd "\"\$$#\""
===============================
for var in "$@"; <==> for var;
===============================
在shell总一般extglob都是开着的,你可以用shopt查看一下,如果没开你可以用shopt -s extglob设置一下,在这个选项打开的状态下,我们可以使用一些扩展的glob选项,在shell中是这么使用的:
?(pattern-list)Matches zero or one occurrence of the given patterns
*(pattern-list)Matches zero or more occurrences of the given patterns
+(pattern-list)Matches one or more occurrences of the given patterns
@(pattern-list)Matches one of the given patterns
!(pattern-list)Matches anything except one of the given patterns
===============================
find . -regextype posix-extended -regex '.*\.(h|cpp)'
===============================
findc(){ [ $# -eq 1 ] && compgen -c | grep --color=auto -i $1; }
===============================
在.bashrc中添加:export PS1='\[\e[1;32;40m\]\u@\h:\w\$ '达到的效果:绿色高亮显示,且折行时不会在同一行,如果去掉\[和\]则会在同一行折行。
本人目前在用的一个PS1是:export PS1='\[\e[0;35m\]>>>>[\[\e[0;33m\]\t\[\e[0;35m\]][\[\e[0;31m\]\u@\h\[\e[0;35m\]:\[\e[0;33m\]\[\e[0;34m\]\w\[\e[0;35m\]]\n\[\e[1;$((31+3*!$?))m\]\$ \[\e[0;32m\]' ,这个PS1会换行,而且新行的开始部分($或者#)会显示红色高亮(上次的命令执行失败)或者蓝色高亮(上次的命令执行正常)。
对于配色,可以参考:
   前景             背景              颜色
   ---------------------------------------
   30                40               黑色
   31                41               紅色
   32                42               綠色
   33                43               黃色
   34                44               藍色
   35                45               紫紅色
   36                46               青藍色
   37                47               白色

   代码              意义
   -------------------------
   0                 OFF
   1                 高亮显示
   4                 underline
   5                 闪烁
   7                 反白显示
   8                 不可见
此外,我在ubuntu上设置了上述PS1之后,当我在终端里打开vi再关闭vi之后,发现命令行出现了乱码,此时输入reset即可恢复正常,此后再使用vi将不再出现乱码,不过每次都这么搞一通实在是不爽,不知道在其他发行版上会不会有这个问题。
现在终于解决了这个问题,有两种方法,一种可以在虚拟终端的title设置中给原先的终端二字前后分别添加一个英文空格即可;另一种方法是重新设置PS1为\[\e]0;\u@\h: \w\a\]\[\e[1;31m\]\u\[\e[1;32m\]@\h\[\e[1;31m\]:\[\e[1;32m\]\w\[\e[1;31m\]\$ \[\e[0m\]
==========================
echo "export LS_COLORS='$LS_COLORS'" >> .bashrc之后打开.bashrc,将di=01;34改为di=01;33即可使得目录以黄色显示
==========================
shell脚本调试,可以先设置
export PS4='+{$LINENO:${FUNCNAME[0]}} ',再 用sh -x来执行脚本,即可在+号后面打印行号和函数名;使用trap  <cmd> DEBUG/ERR/EXIST可以分别在执行每一行前/某个命令返回值不为0时/函数或程序退出时执行cmd;可以在脚本里面的脚本段落前后分别用sh -x和sh +x来使得只有该段落的内容执行sh -x;可以通过变量DEBUG作为开关来统一控制调试与否,当然需要有个函数,比如也叫做DEBUG的话,那这个函数的定义大概为:DEBUG() { [ "$DEBUG" = "true" ] && $@ ; },然后你就可以肆无忌惮的DEBUG echo $a,$b或者在一段落前后分别加上DEBUG set -x和DEBUG set +x来进行控制了;还可以使用类似于Gdb调试的bash调试工具bashdb,官网地址为:http://bashdb.sourceforge.net/;上述总结来自于http://www.ibm.com/developerworks/cn/linux/l-cn-shell-debug/index.html
============================
export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "可以让history显示出执行的时间
============================
date -d "1970-01-01 0:0:0 UTC `date +%s` sec" +'%F %T' 可以将时间戳转换为指定的时间表示形式
用date -d @1313418365 +%F\ %T也可以实现将时间戳转换为指定的时间格式
============================
find . \( -path './rrds*' -o -path './bash*' \) -prune -o -maxdepth 3 -name '*m*' -printf '%f\n'
该命令实现的功能是查找当前目录下,除了rrds*和bash*文件夹之外的名字中含有m字符的且深度不超过3层的, 将符合上述条件的目录或文件名打印出来, 不打印全部路径, 只打印文件名或目录名. 写该命令时需要注意的几点:\(和-path之间以及\)之前都必须有空格;针对path或name定义它们需要符合的pattern时必须要./开头或者*开头,因为这里的匹配都是全匹配,而不是包含即可的意思. 对该命令的几点解释:find查找文件名或目录名(其实目录也是文件)符合pattern所有项, 如果只是中间路径中含有pattern, 并不能保证该目录下的所有文件都能和pattern匹配,因为匹配发生在整个路径的最后,也就是文件名或目录名上;prune本意为删除或砍掉的意思,基本上可以理解为和print恰恰相反即可,它们都是action,find命令其实类似于一个复合的if语句, action前面的默认连接是使用-a(即and的意思, 类似于我们常见的command1 && command2 || command3中的&&的作用), 当然也可以显示的指定-o(即or的意思, 类似于||的作用), 既然谈到了and和or,那么自然就有not了,在find命令里,not是!,举个例子就清楚了:find . ! -name '*.sh'会找到所有不是以.sh结尾的文件, 此外还有就是通过\(和\)能够把一些复合起来的条件作为一个整体, 因此对于该条命令, 执行过程是这样的, if ( -path './rrds*' && -path './bash*' ); then -prune else { if (-maxdepth 3 && -name '*m*' ); then -printf '%f\n' fi } fi
此外对于find命令, 最常用的几个参数是 -type和-name,其实还有-iname(忽略大小写的匹配), 还有-regex和-iregex,注意-name只是用基本的shell的pattern,即*,?,[]三种, 如果真想用正则来匹配,还是用-regex吧; 此外对于find最神奇的莫过于-exec或者-ok或者xargs了,这里给出两个例子即可:
find . -name '*my*' -exec rm '{}' \;   <==>  find . -name '*my*' | xargs rm
find. -name '*my*' -exec cp '{}' ~ \;   <==> find . -name '*my*' | xargs -i cp '{}' ~
对于rm这种单参数的命令, find找到的结果直接会塞给rm的; 对于cp这种双参数的命令, 可以用'{}'这个东西来代表find找到的部分, 但对于xargs中需要注意使用-i参数, 该参数其实和-I参数作用是一样的,但是特别之处在于当不指定replace-str时, -i这个参数会把{}作为replace-str, 因此使用-i就用经管道|过来的标准输入去替换{},因此cp命令就生效了.
================
注释大段shell脚本的方法:
:||:<<\{{
...
{{
这里对here document也多少作些讨论:该文档其实是个临时文档,被生成到/tmp目录下,你用命令bash -c 'lsof -a -p $$ -d0' <<EOF
EOF就可以很清楚的看到这一点;该临时文档为交互式shell脚本或者可执行程序提供输入,最典型的是为cat和ed这样的命令feed输入;最典型的应用是在shell中通过cat<<来大段输出文本, 还有一些比较巧妙的用法,比如为脚本中的函数塞入数据(注意不是为函数塞入参数, 而是当函数中有类似于read这样的命令时通过here文档来feed给它), 在比如为变量赋值variable=$(cat <<SETVAR
This variable
runs over multiple lines.
SETVAR)
再比如
vi $TARGETFILE <<\EOF
i
This is line 1 of the example file.
This is line 2 of the example file.
^[
ZZ
EOF
here document自身所特有的几点包括:
1.用<<-代替<<可以使内容中的leading tabs被忽略,对leading spaces无效
2.用’HERE’, “HERE”, \HERE代替<<后面的HERE,可以使内容中变量不被替换,诸如$HOME
3.如果只希望输入一行数据,可以用<<<即可,例如cat <<<'hello world' (说到这里, 还真觉得蛮有意思的,一个<表示0号标准输入; 两个<<表示here document的临时性输入;三个<<<表示单行的here document输入)
更多详细例子,可以参考:http://tldp.org/LDP/abs/html/here-docs.html
===============
对于cat这种可以接受标准输出或者标准输入流的程序来说, 可以通过一个-来承载送过来的流, 大致可分为通过管道|过来的标准输出流和通过<送进来的标准输入流, 通过这样一个例子就可以看出ls | cat - t.C - -   < t.sh    <<<'single here document'   <<{{
multiple
here
document
{{
结果只是<<发挥作用了, 它们的优先级别是: <<最高, <<<次之, < 排第三, | 排第四
这样就可以利用该特性为某t.C这样的文档添加页眉和页脚了:
页眉: cat - t.C <<<'header'
页脚: cat t.C - <<<'footer'
=======================================
在脚本中,对于那些脚本依赖的变量,如果没定义的话就不希望继续执行脚本,那么可以使用:
set -u
使用了那些你关注的变量的代码段
set +u
这样就可以保证脚本在执行过程中没有发现这些变量时就自动报错退出
==========================================
按照字符串的长度进行排序:
#! /bin/sh
awk 'BEGIN { FS=RS } { print length, $0}' $1 |
sort -k 1n,1 |
sed 's/^[0-9][0-9]* //' | tee $1 >/de
==========================================
有关shell脚本的第一行,这一行可不是随便写的,是有讲究的,首先要从这一行中提取出的是一个可执行程序的全路径,比需要全路径,在#!后面可以跟空格;其次,会为这个命令要么附加一个选项,要么附加一个参数,记住,有且只能有一个,而且是option和arg二选一,如果有多个参数,其实可以合并,举个例子,比如ls,可以把-a和-f合并为-af,因此在这条规则下:#!/usr/bin/env sed -f就不能work了,因为sed本身就已经是env的一个arg了,其实在这种情况下,系统会认为'sed -f'整体为一个arg,系统判断你指定的究竟是一个arg还是一个option的依据就是:是否以-开头。
=============================================
cd ~1 与cd ~-1代表cd到dirs命令列表中左数/右数第1个目录(从0开始计数); 注意这种用法并不会与pushd冲突, 因为pushd会对dirs列表进行rotate(一定要好好理解这个单词的含义, 它相当于把dirs的结果看作是一个round robin的环状结构, pushd要更改的就是从这个环上的哪个地方找到队首,然后顺时针就可以找到其他的元素了)操作, 举个例子: 假如dirs的结果为: ~    /usr     /etc    /tmp四个目录,那么cd ~1后的结果是/usr /usr /etc /tmp; cd ~-1的结果是/etc /usr /etc /tmp; pushd +1的结果为: /usr /etc /tmp ~; pushd -1的结果为: /etc /tmp ~ /usr
=============================================
查看bash内建命令的help内容,可以用help ulimit,不过这样得到的信息不全面,全面的信息可以在man builtins里面找到;
========================================
echo -e 'a\tb'和echo $'a\tb'的结果一样,$'string'形式的字符串会被特殊处理,字符串会被展开,并像c语言那样将反斜杠及紧跟的字符进行替换,扩展后的结果将被单引号包裹,就好像美元符号一直就不存在一样,例如$'\n' <=> $'\012' <=> $'\x0a' <=> $'\x0A';$"string"将会使得字符串被翻译成符合当前locale的语言,如果当前locale是 C 或者 POSIX,美元符号会被忽略,如果字符串被翻译并替换,替换后的字符串仍被双引号包裹。
=========================================
command > out.log 2>&1  可以被command &> out.log代替,二者意义一样,注意&和>之间不能有空格,否则&就会被认为是要把命令放到后台执行,有关bash一些新增的用法可以参见:http://zh.wikipedia.org/wiki/Bash
============================================
grep -e a -e b file能够匹配到file中的行内含有a或者b的行
============================================
chvt可以替代Ctrl+Alt+Fn功能键;openvt可以在开满了6个tty之后再新开tty
============================================
在man或者less搜索的时候,用-i来切换大小写是否敏感(当最左下角显示为:时进行)
=============================================
export -f可以export函数;unset -f/export -f -n可以取消函数定义;declare -f可以看到当前bash里面所有的函数
==========================================================
函数中可以再定义函数,但却不是嵌在函数里面的子函数,外部依然可以随意调用该函数,也就是说和放在外面一样,只不过看上去能感觉到:这个定义在函数A里面的函数B,应该是专注于为函数A服务的。
==========================================================
在bash里fork子shell
#!/bin/bash
if [ "$PROC_PID" != "$PPID" ]; then
    export PROC_PID=$$
    var="mype"
    echo "initial $var"
    declare -r var
    export var
    $0 & # child process
else
    echo "before $var"
    var="netty5"
    echo "after $var"
fi
=================================================================================================
在脚本中$@代表当前shell脚本执行时的所有参数或者代表传递到当前函数内部的所有参数,通过shift命令,可以改变$@的内容,但是$0始终代表当前进程的命令名字,而不是函数名字
=================================================================================================
getopts函数(bash builtin)的功能还是很弱的,比如要解析pq:,那么必须得这么写命令才行:<your_command> -p -q test arg1 arg2 ...;参数之间不能断开,而且还得必须紧跟在command的后面,当然,getopts是解析$@,如果你用shift处理后的$@能满足$1开始就是option,那么也行。
======================================================
在linux下作各种进制的转化,有两种方法比较好用:
printf "%x\n" 2342  #该方法和c语言中的printf没什么区别
echo  'obase=16; 2342' | bc  #该方法很强大, 可以转化为任意进制, 比如printf不能做到的2进制转化
============================================================
echo $content | while read line;
do
...
done
上面这段代码有3个问题:
1. echo时应该给$content添加双引号,否则content中的空格换行tab之类的东西就都丢了;
2. 用管道会导致while是在一个新的子shell里, 导致在while中用的变量其实是新定义的, 而不是context中的;
3. read会忽略前导空格, 因为read会使用IFS的第一个字符作为分割符号, 把当前line分割为若干个word, 你用read word1 word2 word3会读取到各个单词, 默认IFS的第一个字符是空格, 因此假定当前line为'  a'的话, 那么会被切割为3部分(因为有2个空格), 最后组装的时候,  会忽略掉空单词, 因此最后就只剩下a了.

正确的做法是:
IFS=
while read line;
do
...
done < <(echo "$content")
这里提一下<()和>()的用法, 这两个东西都是命名管道(但这里是没有名字的, 实则它们是借助于某一个fd来完成的), 相当于你先mkfifo了一个命令管道xxx, 然后你echo "$content" > xxx, 同时你的read函数已经在xxx的另一端候着了while read line; do ... done < xxx. 用<()和>()省略了建立xxx的过程.
==============================================================================
tr 'a-z' 'A-Z' < t1.txt > t1.txt像这种语句,都会把t1.txt清空,其实是bash在这里做了手脚,我们都知道在bash中ls *时,其实bash会先把*扩展为当前所有的文件,然后再让ls去显示,这里也差不多,bash发现>时,会先把该文件清空,然后再交给tr去处理,所以tr啥都不用做就完成了,而此时t1.txt就变成空文件了,要想避免这种情况发生,可以tr 'a-z' 'A-Z' < t1.txt >> t1.txt,但这样会保留t1.txt之前的内容,现在借助于<>,搞出个简单的方法:tr 'a-z' 'A-Z' < t1.txt 1<> t1.txt,这里1<>代表通过fd为1的文件进行read and write,这样就绕过了bash的提前解析,因此t1.txt中的内容也被成功替换掉了。但要注意的是,同时从一个文件既读取又写入,而且是以管道这种流的形式来搞(正因为是流的形式[读一点写一点],所以就不会全部读到内存中,然后再处理,再把处理后的结果从内存中dump到输出文件),所以这种只对那种本位替换有效果,例如tr或者sed这种,其实说到这里,主要是为了引出<>这个符号的用法,并让大家知道bash会先把>给解析了,其他的都不重要。
strace是个太好太好的命令,例如strace sed -i 's/a/A/g' t1.cpp即可看到sed是使用了临时文件进行替换的。
================================================
#!/bin/bash

mput() {
    eval "__map_$1"__"$2"='$3'
}

mget() {
    eval echo '${__map_'"$1__$2"'}'
}

mgetall() {
    for i in $(eval echo '${!__map_'"$1"'__*}'); do
        echo -n "${i#__map_$1__*}:"
        eval echo '${'"$i"'}';
    done
}

mput capitals France Paris
mput capitals Spain Madrid

echo "$(mget capitals France)"
mgetall capitals
=================================================
func () {
    local unset_x=false
    [[ "$(echo $-)" =~ x ]] || unset_x=true
    [ "false" == "$unset_x" ] && set +x
    #procedure here
    [ "false" == "$unset_x" ] && set -x
}
=================================================
ulimit -c unlimited
export PS4='+{$LINENO($(date +"%F %T")):${FUNCNAME[0]}} '
exec 1>${0%.*}.log
exec 2>${0%.*}.err
mkdir -p .tmp
export TMPDIR=.tmp
set -x
====================================================
__created_tmp_files__=""
delete_tmp_files() {
    if [ -n "$__created_tmp_files__" ]; then
        rm -f $__created_tmp_files__
    fi
}

trap delete_tmp_files EXIT

create_tmp_file() {
    local unset_x=false
    [[ "$(echo $-)" =~ x ]] || unset_x=true
    [ "false" == "$unset_x" ] && set +x
    if [ -z "$(eval "echo $"$1)" ]; then
        eval "$1=.tmp/.__${0##*/}_${LINENO}_$1_${RANDOM}__.tmp"
        rm -f $(eval "echo $"$1)
        __created_tmp_files__="$__created_tmp_files__ $(eval "echo $"$1)"
    fi
    [ "false" == "$unset_x" ] && set -x
}
===================================================
http://www.blogjava.net/bacoo/archive/2009/06/16/282493.html

你可能感兴趣的:(编程,shell,脚本,command,bash,colors)