一些小细节

注意$variable事实上只是${variable}的简写形式。赋值时如果在变量中存在空白,那么就必须加上引用.other_numbers=1 2 3 # 给出一个错误消息.

一个未初始化的变量是没有值的, 但是在做算术操作的时候, 这个未初始化的变量看起来值为0. 这是一个未文档化(并且可能不具可移植性)的行为.
 cut , sort,join这些命令智能用单个分隔符,如果要用转义字符做分隔则可以用-t/d $'\t'的形式。awk则可以直接用-F'\t'的形式,并且支持正则表达式。但是用"^&"作为分隔符时,必须只用-F'\\^&',单引号双斜杠,换成双引号或者单斜杠转义都不行。

 %在crontab中被认为是newline,要用\来escape才行。比如crontab执行行中,如果有"date +%Y%m%d",必须替换为:"date +\%Y\%m\%d"
47 09 * * * cd /fourthinfo/zhaoxiang/zp_user && echo "this is a test" > "logs/$(date +\%Y-\%m-\%d_\%H:\%M:\%S).log" 2>&1

 

 1 {xxx,yyy,zzz,...}  大括号扩展.
 3 cat {file1,file2,file3} > combined_file # 把file1, file2, file3 连接在一起, 并且重定向到combined_file中.
 4 cp file22.{txt,backup} # 拷贝"file22.txt""file22.backup" 5 一个命令可能会对大括号中的以逗号分割的文件列表起作用. (通配(globbing) )将对大括号中的文件名做扩展.
 6 在大括号中, 不允许有空白, 除非这个空白被引用或转义.
 7 echo {file1,file2}\ :{\ A," B",' C'}
 8 file1 : A file1 : B file1 : C file2 : A file2 : B file2 : C
 9 
10 $' ... '
11 Quoted string expansion. This construct expands single or multiple escaped octal or hex values into ASCII [18] or Unicode characters.
12 
13 {a..z} Extended Brace expansion.
14 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  # Echoes characters between a and z.
15 echo {0..3} # 0 1 2 3
16 base64_charset=( {A..Z} {a..z} {0..9} + / = )  # Initializing an array, using extended brace expansion.
17 The {a..z} extended brace expansion construction is a feature introduced in version 3 of Bash.

 

正则强引用问题
1 sed -e '/^$/d' $filename
2 # -e选项, 将会使得后边的字符被看作为编辑指令.  (如果只给"sed"传递了单个指令, 那么"-e"是可选的.)
4 # ""引用('')将会保护指令中的RE(正则表达式)字符串,  也就是防止脚本将RE重新解释为特殊字符. (这会为sed命令, 保存指令的RE表达式.)

在某些特定的情况下, sed编辑命令将不会和单引号的强引用一起工作.
1 filename=file1.txt
2 pattern=BEGIN
3
4 sed "/^$pattern/d" "$filename" # 工作正常. 
5 # sed '/^$pattern/d' "$filename"  就会出现异常的结果.  在这个实例中, 被强引用(' ... ')引起的 "$pattern"就不会扩展为"BEGIN". 
  sed -n '/xzy/p' $filename   # -n选项会让sed只打印那些匹配模式的行.
反斜线将会强制sed替换命令延续到下一行. 类似于, 在第一行的结尾使用换行作为替换字符串.
   1 s/^ */\
   2 /g
这将每行开头的空格用换行来替换. 最后的结果就是将每段的缩进替换为一个空行。

 

 

 通常,正则表达式需要用单引号括起来,阻止特殊字符被解释,而通配符不能用引号。 单引号中的所有字符包括\都没有特殊意义。如果想在一对单引号中显示一个单引号是不行的

在一个双引号中通过直接使用变量名的方法来引用变量, 一般情况下都是没问题的. 这么做将阻止所有在引号中的特殊字符被重新解释 -- 包括变量名 [1]-- 但是$,`(后置引用), 和\(转义符)除外。使用双引号还能够阻止单词分割(word splitting). [3]如果一个参数被双引号扩起来的话, 那么这个参数将认为是一个单元, 即使这个参数包含有空白, 那里面的单词也不会被分隔开。

在echo语句中, 只有在单词分割(word splitting)或者需要保留空白的时候, 才需要把参数用双引号括起来.

使用-e选项的'echo'命令来打印转义符.  echo -e "\v\v\v\v" # 打印4个垂直制表符.

如果使用$'\X'结构,那-e选项就不必要了.  echo $'\t \042 \t' # 被水平制表符括起来的引号(").

variable2="" # Empty.
COMMAND $variable2 $variable2 $variable2 # COMMAND将不带参数执行.
COMMAND "$variable2" "$variable2" "$variable2" # COMMAND将以3个空参数来执行.
COMMAND "$variable2 $variable2 $variable2" # COMMAND将以1个参数来执行(2空格)

file_list="/bin/cat /bin/gzip /bin/more /usr/bin/less /usr/bin/emacs-20.7"   # 列出的文件都作为命令的参数
 ls -l /usr/X11R6/bin/xsetroot /sbin/dump $file_list  # 加两个文件到参数列表中, 列出所有的文件信息.

(cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)

 每个命令都会返回一个 退出状态码 (有时候也被称为 返回状态 ). 成功的命令返回0, 而不成功的命令返回非零值, 非零值通常都被解释成一个错误码,虽然偶尔也会有例外。exit nnn命令将会把 nnn退出码传递给shell( nnn必须是十进制数, 范围必须是0- 255)。

(( ... ))和let ...结构也能够返回退出状态码, 当它们所测试的算术表达式的结果为非零的时候, 将会返回退出状态码0. 这些算术扩展结构被用来做算术比较.

在[[和]]之间所有的字符都不会发生文件名扩展或者单词分割, 但是会发生参数扩展和命令替换.   使用[[ ... ]]条件判断结构, 而不是[ ... ], 能够防止脚本中的许多逻辑错误. 比如,&&, ||, <, 和>操作符能够正常存在于[[ ]]条件判断结构中, 但是如果出现在[ ]结构中的话, 会报错.

"if COMMAND"结构将会返回COMMAND的退出状态码.  与此相似, 在中括号中的条件判断也不一定非得要if不可, 也可以使用列表结构. 

[ "$var1" -ne "$var2" ] && echo "$var1 is not equal to $var2"
 home=/home/bozo
[ -d "$home" ] || echo "$home directory does not exist."

(( ))结构扩展并计算一个算术表达式的值. 如果表达式的结果为0, 那么返回的退出状态码为1, 或者是"假". 而一个非零值的表达式所返回的退出状态码将为0, 或者是"true". 这种情况和先前所讨论的test命令和[ ]结构的行为正好相反。

当-n使用在中括号中进行条件测试的时候, 必须要把字符串用双引号引用起来,习惯于使用引用的测试字符串才是正路。

 

变量的间接引用

var1=var2

var 2=value

(1) eval tmpvar=\$$var1

(2) tmpvar=${!var1}

 

在awk中调用linux命令,要用双引号引起来。 awk -F: '{print $1 | "sort"}' /etc/passwd

 

子shell能继承父shell的一些属性,但是不可能改变父shell的属性。

shell默认将变量看作只有一个元素的数组,而@和*都可用于数组。

 

nameList=`wget "$reqUrl" -O- 2>/dev/null|grep -Po '(?<=:9000">)[^/]*?(?=</a></B><br></td>)'|sed '/^_/d'`
    while read name
    do
        downFileFullName=$baseHdfsPath/$name
        tmpUrl="http://localhost:50075/streamFile$downFileFullName?nnaddr=host:9000"        
        wget "$tmpUrl"  -O $name
    done< <(echo "$nameList") #进程替换
 echo "$nameList" | while read name   #管道,在这里不能用命令替换,命令替换把一个进程的输出赋值给一个变量。
    do
        downFileFullName=$baseHdfsPath/$name
        tmpUrl="http://localhost:50075/streamFile$downFileFullName?nnaddr=host:9000"        
        wget "$tmpUrl"  -O $name
    done

 - 用于重定向stdin或stdout,相当于一个文件名。   (cd /source/directory && tar cf - . ) | (cd /dest/directory && tar xpvf -)

  grep Linux file1 | diff file2 -

 

 Bash变量是不区分类型的

  a=2334 # 整型.
5 let "a += 1"
6 echo "a = $a " # a = 2335
7 echo # 还是整型.
8
9
10 b=${a/23/BB} # 将"23"替换成"BB".
11 # 这将把变量b从整型变为字符串.
12 echo "b = $b" # b = BB35
13 declare -i b # 即使使用declare命令也不会对此有任何帮助.
14 echo "b = $b" # b = BB35
15
16 let "b += 1" # BB35 + 1 =
17 echo "b = $b" # b = 1
18 echo
19
20 c=BB34
21 echo "c = $c" # c = BB34
22 d=${c/BB/23} # 将"BB"替换成"23".
23 # 这使得变量$d变为一个整形.
24 echo "d = $d" # d = 2334
25 let "d += 1" # 2334 + 1 =
26 echo "d = $d" # d = 2335
27 echo
28
29 # null变量会如何呢?
30 e=""
31 echo "e = $e" # e =
32 let "e += 1" # 算术操作允许一个null变量?
33 echo "e = $e" # e = 1
34 echo # null变量将被转换成一个整型变量.
35
36 # 如果没有声明变量会怎样?
37 echo "f = $f" # f =
38 let "f += 1" # 算术操作能通过么?
39 echo "f = $f" # f = 1
40 echo # 未声明的变量将转换成一个整型变量.
 # 所以说Bash中的变量都是不区分类型的.

{}标记法提供了一种提取从命令行传递到脚本的最后一个位置参数的简单办法. 但是这种方法同时还需要使用间接引用.

1 args=$# # 位置参数的个数.
2 lastarg=${!args}
3 # 或: lastarg=${!#}
4 # (感谢, Chris Monson.)
5 # 注意, 不能直接使用lastarg=${!$#} , 这会产生错误.

  参数替换好方法: ${1:-$DefaultVal}

 引用可以阻止单词分割,还可以改掉echo's不换行的"毛病".

bash$ echo $(ls -l)
total 8 -rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh -rw-rw-r-- 1 bozo bozo 78 
Aug 21 12:57 t71.sh
bash$ echo "$(ls -l)"
total 8
-rw-rw-r-- 1 bozo bozo 130 Aug 21 12:57 t222.sh
-rw-rw-r-- 1 bozo bozo 78 Aug 21 12:57 t71.sh

8
9 variable2="" # Empty.
10
11 COMMAND $variable2 $variable2 $variable2 # COMMAND将不带参数执行.
12 COMMAND "$variable2" "$variable2" "$variable2" # COMMAND将以3个空参数来执行.
13 COMMAND "$variable2 $variable2 $variable2" # COMMAND将以1个参数来执行(2空格).

 

即使是转义符(\)在单引号中也是按照字面意思解释的, 所以如果想在一对单引号中显示一个单引号是不行的。

 echo "Why can't I write 's between single quotes"
 # 一种绕弯的方法.
 echo 'Why can'\''t I write '"'"'s between single quotes'
    # |-------|  |----------|   |-----------------------|
   # 三个被单引号引用的字符串, 在这三个字符串之间有一个用转义符转义的单引号, 和一个用双引号括起来的单引号

转义字符

echo -e "\042"  # 打印" (引号, 8进制的ASCII 码就是42).
# 如果使用 $'\X' 结构,那-e选项就不必要了

 

使用[[ ... ]]条件判断结构, 而不是[ ... ], 能够防止脚本中的许多逻辑错误. 比如,&&, ||, <, 和>操作符能够正常存在于[[ ]]条件判断结构中, 但是如果出现在[ ]结构中的话, 会报错.
在if后面也不一定非得是test命令或者是用于条件判断的中括号结构( [ ] 或 [[ ]] ).
1 dir=/home/bozo
2
3 if cd "$dir" 2>/dev/null; then # "2>/dev/null" 会隐藏错误信息.
4 echo "Now in $dir."
5 else
6 echo "Can't change to $dir."
7 fi
"if COMMAND"结构将会返回COMMAND的退出状态码.
与此相似, 在中括号中的条件判断也不一定非得要if不可, 也可以使用列表结构.
1 var1=20
2 var2=22
3 [ "$var1" -ne "$var2" ] && echo "$var1 is not equal to $var2"
4
5 home=/home/bozo
6 [ -d "$home" ] || echo "$home directory does not exist."
(( ))结构扩展并计算一个算术表达式的值. 如果表达式的结果为0, 那么返回的退出状态码为1, 或者是"". 而一个非零值的表达式所返回的退出状态码将为0, 或者是"true". 这种情况和先前所讨论的test命令和[ ]结构的行为正好相反.
1 #!/bin/bash
2 # 算术测试.
3
4 # (( ... ))结构可以用来计算并测试算术表达式的结果. 
5 # 退出状态将会与[ ... ]结构完全相反!
6
7 (( 0 ))
8 echo "Exit status of \"(( 0 ))\" is $?." # 1
9
10 (( 1 ))
11 echo "Exit status of \"(( 1 ))\" is $?." # 0
12
13 (( 5 > 4 )) # 真
14 echo "Exit status of \"(( 5 > 4 ))\" is $?." # 0
15
16 (( 5 > 9 )) # 假
17 echo "Exit status of \"(( 5 > 9 ))\" is $?." # 1
(( 5 / 4 )) # 除法也可以.
23 echo "Exit status of \"(( 5 / 4 ))\" is $?." # 0
24
25 (( 1 / 2 )) # 除法的计算结果< 1.
26 echo "Exit status of \"(( 1 / 2 ))\" is $?." # 截取之后的结果为0.
27 # 1
整数比较
-eq  等于 if [ "$a" -eq "$b" ]
-ne  不等于 if [ "$a" -ne "$b" ]
-gt  大于 if [ "$a" -gt "$b" ]
-ge  大于等于 if [ "$a" -ge "$b" ]
-lt  小于 if [ "$a" -lt "$b" ]
-le  小于等于 if [ "$a" -le "$b" ]
<    小于(在双括号中使用)    (("$a" < "$b"))
<=   小于等于(在双括号中使用) (("$a" <= "$b"))
>    大于(在双括号中使用) (("$a" > "$b"))
>=   大于等于(在双括号中使用)(("$a" >= "$b"))
字符串比较
=  等于 if [ "$a" = "$b" ]
==  等于 if [ "$a" == "$b" ]   与=等价. ==比较操作符在双中括号对和单中括号对中的行为是不同的.
1 [[ $a == z* ]] # 如果$a以"z"开头(模式匹配)那么结果将为真
2 [[ $a == "z*" ]] # 如果$a与z*相等(就是字面意思完全一样), 那
么结果为真.
3
4 [ $a == z* ] # 文件扩展匹配(file globbing)和单词分割有效. 
5 [ "$a" == "z*" ] # 如果$a与z*相等(就是字面意思完全一样), 那么结果为真. 
!=  不等号  if [ "$a" != "$b" ] 这个操作符将在[[ ... ]]结构中使用模式匹配.
<  小于, 按照ASCII字符进行排序  if [[ "$a" < "$b" ]],if [ "$a" \< "$b" ]。注意"<"使用在[ ]结构中的时候需要被转义.
>  大于, 按照ASCII字符进行排序 if [[ "$a" > "$b" ]],if [ "$a" \> "$b" ]。 注意">"使用在[ ]结构中的时候需要被转义.
-z 字符串为"null", 意思就是字符串长度为零 
-n 字符串不为"null".
当-n使用在中括号中进行条件测试的时候, 必须要把字符串用双引号引用起来. 如果采用了未引用的字符串来使用! -z, 甚至是在条件测试中括号(参见例子 7-6)中只使用未引用的字符串的话, 一般也是可以工作的, 然而,这是一种不安全的习惯. 习惯于使用引用的测试字符串才是正路.如果$string为空的话, [ -n "$string" -o "$a" = "$b" ]可能会在某些版本的Bash中产生错误. 安全的做法是附加一个额外的字符给可能的空变量, [ "x$string" != x -o "x$a" = "x$b" ]("x"字符是可以相互抵消的).

  if [ $# -eq 0 ] # 与if [ -z "$1" ]效果相同。    # (译者注: 上边这句注释有问题), $1是可以存在的, 可以为空, 如: zmore "" arg2 arg3
  then
  echo "Usage: `basename $0` filename" >&2 # 错误消息输出到stderr.
  exit $NOARGS
  fi

-a  逻辑与  exp1 -a exp2如果表达式exp1和exp2都为真的话, 那么结果为真.
-o  逻辑或  exp1 -o exp2如果表达式exp1和exp2中至少有一个为真的话, 那么结果为真.
这与Bash中的比较操作符&&和||非常相像, 但是这个两个操作符是用在双中括号结构中的.
[[ condition1 && condition2 ]]
-o和-a操作符一般都是和test命令或者是单中括号结构一起使用的.
if [ "$exp1" -a "$exp2" ]

使用算术操作符

#!/bin/bash
let "n = $n + 1" # let "n = n + 1"  也可以.
: $((n = $n + 1))   # ":" 是必需的, 因为如果没有":"的话,  Bash将会尝试把"$((n = $n + 1))"解释为一个命令.
 (( n = n + 1 )) #  上边这句是一种更简单方法.
 n=$(($n + 1))
: $[ n = $n + 1 ] # ":" 是必需的, 因为如果没有":"的话, Bash将会尝试把"$[ n = $n + 1 ]"解释为一个命令.即使"n"被初始化为字符串, 这句也能够正常运行. 
 n=$[ $n + 1 ] 应该尽量避免使用这种类型的结构, 因为它已经被废弃了, 而且不具可移植性.
 # 现在来一个C风格的增量操作.
let "n++" # let "++n"  也可以.
 (( n++ )) # (( ++n )  也可以.
: $(( n++ )) # : $(( ++n )) 也可以.
: $[ n++ ] # : $[ ++n ] 也可以.
Bash不能够处理浮点运算. 它会把包含小数点的数字看作字符串.如果非要做浮点运算的话, 可以在脚本中使用bc, 这个命令可以进行浮点运算, 或者调用数学库函数.
awk中等价于${string:pos:length}的命令是substr(string,pos,length).
String=23skidoo1
     # 012345678 Bash
     # 123456789 awk
# 注意不同的字符串索引系统: Bash的第一个字符是从'0'开始记录的. Awk的第一个字符是从'1'开始记录的.
echo | awk '{ print substr("'"${String}"'",3,4) } # skid    使用一个空的"echo"通过管道传递给awk一个假的输入, 这样就不必提供一个文件名给awk.

 变量间接引用

如果a=letter_of_alphabet并且letter_of_alphabet=z, 那么我们能够通过引用变量a来获得z么? 这确实是可以做到的, 它被称为间接引用. 它使用eval var1=\$$var2这种不平常的形式.另一个方法是使用${!t}符号
t=table_cell_3
table_cell_3=24
echo "\"table_cell_3\" = $table_cell_3" # "table_cell_3" = 24
echo -n "dereferenced \"t\" = "; eval echo \$$t # 解引用"t" = 24
# 在这个简单的例子中, 下面的表达式也能正常工作么(为什么?).
# eval t=\$$t; echo "\"t\" = $t"
但是直接使用echo \$$t是不行的,\$会输出$.
另一个方法是使用${!t}符号
Suites="Clubs
Diamonds
Hearts
Spades"
suite=($Suites) # 读入一个数组.

 双圆括号结构

与let命令很相似, ((...))结构允许算术扩展和赋值. 举个简单的例子, a=$(( 5 + 3 )), 将把变量"a"设为"5 + 3", 或者8. 然而, 双圆括号结构也被认为是在ash中使用C语言风格变量操作的一种处理机制.
(( a = 23 )) # C语言风格的变量赋值, "="两边允许有空格.
 (( a++ )) # C语言风格的后置自加.   (( a-- )) # C语言风格的后置自减.
(( ++a )) # C语言风格的前置自加.  (( --a )) # C语言风格的前置自减.
 #  注意: 就像在C语言中一样, 前置或后置自减操作 会产生一些不同的副作用.
 n=1; let --n && echo "True" || echo "False" # False
 n=1; let n-- && echo "True" || echo "False" # True
(( t = a<45?7:11 )) # C语言风格的三元操作.
也可以参考一些"for" 和"while" 循环中使用((...))结构的例子

 awk引用外部变量  引号,变量的处理

一、用awk 有以下几种方法去调用变量:
1.  awk '{print a, b}' a=111 b=222 yourfile
注意, 变量位置要在 file 名之前, 否则就不能调用。
还有, 于 BEGIN{}中是不能调用这些的variable. 要用之后所讲的第二种方法才可解决.
2.  awk –v a=111 –v b=222 '{print a,b}' yourfile
注意, 对每一个变量加一个 –v 作传递.
3.  awk '{print " ' "$LOGNAME" ' "}' yourfile
如果想调用environment variable, 要用以上的方式调用, 方法是:
"  '  "  $LOGNAME  "  '  "
原文:
awk如何引用外部变量
http://hi.baidu.com/icqncvjhgmbbsyq/item/02ef096ac1d49c94c5d2495a
二、进一步解释
第3种方法为什么要加两个双引号和一个单引号?
$ str=Hello
$ awk 'BEGIN{print " '$str' "}'
Hello
看上去是双引号套单引号,其实真正的原因为:
这是shell的功能,shell对单引号和双引号,按从左到右的顺序成对匹配
awk命令用单引号引起来,就是防止shell对其中内容进行解释
awk '{print " '$str' "}' file
实际上就是2部分
1:awk '{print " '
2:'"}'
即awk对2个单引号内的命令起作用。
至于$str就被shell正常解释为变量str的值。
所以,如果str=hello,则经解释后成为,awk {print "hello"}file
而如果str=hello world,则解释时,在解释前一部分:awk {print " 后,在替换了变量后,变成了hello world,当shell读到hello和world中间的空格时,认为这是IFS,于是,把他们放在于不同的域中,这样解释成了:
awk BEGIN{print "hello
world"}两部分。
按照上面的解释,就可以这么来修改,比如
a)$ awk 'BEGIN{print " ' "$a" ' "}'
或者
b)$ awk "BEGIN{print \"$a\"}"
或者
c)$ awk BEGIN\{print\""$a"\"\}
对于a,解释成为:
awk BEGIN{print "hello world"} #因为$a在替换后,还在“”中包括中,所以当成了一个字符串处理。
三、总结
awk怎么说也是要运行在shell环境中的。所以,写在awk中的命令,要先经过shell解析后,再交由awk来解释和执行。
原文:
awk引用外部变量
http://hi.baidu.com/liheng_2009/item/6466a4c0e087222447d5c0c8
以下两个链接给了更多的讨论:
http://www.linuxsir.org/bbs/thread121709.html
http://bbs.chinaunix.net/thread-1381166-1-1.html

 子shell :    ( date; uname -a ) >>logfile

expr如何使用转义括号对-- \( ... \) --和正则表达式一起来分析和匹配子串

expr如何使用转义括号对-- \( ... \) --和正则表达式一起来分析和匹配子串
1 # 去掉字符串开头和结尾的空白.   :操作可以替换match命令.
2 LRFDATE=`expr "$LRFDATE" : '[[:space:]]*\(.*\)[[:space:]]*$'`
需要转义括号,这样才能触发子串的匹配

 命令替换使命令的输出就能够保存到变量中, 或者传递到另一个命令中作为这个命令的参数, 甚至可以用来产生for循环的参数列表.变量替换允许将一个loop的输出设置到一个变量中. 这么做的关键就是将循环中echo命令的输出全部截取.

variable1=`for i in 1 2 3 4 5  #对于命令替换来说, $(COMMAND)形式已经取代了后置引用"`",$(...)形式的命令替换是允许嵌套的
do,
echo -n "$i" #  对于在这里的命令替换来说
done`

for i in 1 2 3 4 5
do
echo -n "$i" #  对于在这里的命令替换来说
done > file 或者 | cmd

 重定向

1>filename  # 重定向stdout到文件"filename". 
1>>filename # 重定向并追加stdout到文件"filename". 
2>filename # 重定向stderr到文件"filename". 
2>>filename# 重定向并追加stderr到文件"filename".
&>filename# 将stdout和stderr都重定向到文件"filename". M>N # "M"是一个文件描述符, 如果没有明确指定的话默认为1. "N"是一个文件名. 文件描述符"M"被重定向到文件"N". M>&N# "M"是一个文件描述符, 如果没有明确指定的话默认为1."N"是另一个文件描述符. 2>&1# 重定向stderr到stdout. 将错误消息的输出, 发送到与标准输出所指向的地方. i>&j# 重定向文件描述符i到j. 指向i文件的所有输出都发送到j. >&j# 默认的, 重定向文件描述符1(stdout)到j.所有传递到stdout的输出都送到j中去.
0< FILENAME < FILENAME# 从文件中接受输入. # 与">"是成对命令, 并且通常都是结合使用. | # 管道. 通用目的处理和命令链工具,可以将输入输出重定向和(或)管道的多个实例结合到一起写在同一行上. command < input-file > output-file command1 | command2 | command3 > output-file 关闭文件描述符 n<&-关闭输入文件描述符n. 0<&-, <&-关闭stdin. n>&-关闭输出文件描述符n. 1>&-, >&-关闭stdout. 子进程继承了打开的文件描述符. 这就是为什么管道可以工作. 如果想阻止fd被继承, 那么可以关掉它.

 Bash本身并不会识别正则表达式. 在脚本中, 使用RE的是命令和工具 -- 比如sed和awk-- 这些工具能够解释RE.

而是使用通配符. 通配(globbing)解释标准通配符, *, ?, 中括号扩起来的字符, 还有其他一些特殊字符(比如^或!用来表示取反匹配). 然而通配(globbing)所使用的通配符有很大的局限性. 包含*的字符串不能匹配以"点"开头的文件, 比如,.bashrc. [2]另外, RE中所使用的?, 与通配(globbing)中所使用的?, 含义并不相同.

 ( command1; command2; command3; ... )圆括号中命令列表的命令将会运行在一个子shell中.   子shell中的目录更改不会影响到父shell.

在大括号中的命令不会启动子shell. { command1; command2; command3; . . . commandN; }

使用"|"管道操作符, 将I/O流重定向到一个子shell中, 比如ls -al | (command).

陷阱

在大括号包含的代码块中, 最后一条命令必须以分号结尾.

不合时宜的使用空白字符. 与其它编程语言相比, Bash非常讲究空白字符的使用.  let c = $a - $b   # 'let c=$a-$b''let "c = $a - $b"'才是正确的.

混淆测试符号=和-eq. 请记住, =用于比较字符变量, 而-eq用来比较整数

在单中括号结构([ ])中, "<"必须被转义.   while [ "$number" \< "5ffsaf" ]  , if [ "$greater" \< "$lesser" ]

有时候在"test"中括号([ ])结构里的变量需要被引用起来(双引号). 如果不这么做的话, 可能会引起不可预料的结果.

一个带有DOS风格换行符(\r\n)的脚本将会运行失败, 因为#!/bin/bash\r\n是不合法的.

如果在here document中, 结尾的limit string之前加上空白字符的话, 将会导致脚本的异常行为.

脚本不能将变量export到它的父进程(即调用这个脚本的shell), 或父进程的环境中. 就好比我们在生物学中所学到的那样, 子进程只会继承父进程, 反过来则不行.

在子shell中设置和操作变量之后, 如果尝试在子shell作用域之外使用同名变量的话, 将会产生令人不快的结果.

将echo的输出通过管道传递给read命令可能会产生不可预料的结果. 在这种情况下, read命令的行为就好像它在子shell中运行一样. 可以使用set命令来代替.echo "one two three" | read a b c # 尝试重新给变量a, b, 和c赋值. 失败.   # 试试下边这种方法. var=`echo "one two three"`; set -- $var ; a=$1; b=$2; c=$3

通过管道将输出传递到任何循环中, 都会引起类似的问题.因为管道通常运行在子shell中,变量无法传回给父shell。 在本人的测试中,即使通过管道传给大括号括起来的代码块,也还是相当于子shell。

对于测试来说, [[ ]]结构可能比[ ]结构更合适. 同样地, 在算术比较中, 使用(( ))结构可能会更有用
# 下面所有的比较都是等价的. 
4 test "$a" -lt 16 && echo "yes, $a < 16" # "与列表"
5 /bin/test "$a" -lt 16 && echo "yes, $a < 16" 
6 [ "$a" -lt 16 ] && echo "yes, $a < 16" 
7 [[ $a -lt 16 ]] && echo "yes, $a < 16" # 在[[ ]]和(( ))结构中, 
8 (( a < 16 )) && echo "yes, $a < 16" # 不用将变量引用起来. 
9
10 city="New York"
11 # 同样地, 下面所有的比较都是等价的. 
12 test "$city" \< Paris && echo "Yes, Paris is greater than $city" # 按照ASCII顺序比较. 
13 /bin/test "$city" \< Paris && echo "Yes, Paris is greater than $city" 
14 [ "$city" \< Paris ] && echo "Yes, Paris is greater than $city" 
15 [[ $city < Paris ]] && echo "Yes, Paris is greater than $city" # 不需要引用$city.

 
一个相关的问题: 当你尝试将tail -f的stdout通过管道传递给grep时, 会产生问题.
1 tail -f /var/log/messages | grep "$ERROR_MSG" >> error.log       # "error.log"文件将不会写入任何东西.

 各种小技巧

>>操作符可以在文件末尾添加内容. 如果你想在文件的头部添加内容怎么办, 难道要粘贴到文件头?
file=data.txt
title="***This is the title line of data text file***"
echo $title | cat - $file >$file.new   # "cat -" 将stdout连接到$file.   最后的结果就是生成了一新文件, 并且成功的将$title的内容附加到了文件的*开头*.

将你最喜欢的变量定义和函数实现都放到一个文件中. 在你需要的时候, 通过使用点(.)命令,或者source命令, 来将这些"库文件""包含"到脚本中.

使用$?退出状态变量, 因为脚本可能需要测试一个参数是否都是数字, 以便于后边可以把它当作一个整数来处理.

函数的返回值严格限制在0 - 255之间. 使用全局变量或者其他方法来代替函数返回值, 通常都很容易产生问题. 从函数中, 返回一个值到脚本主体的另一个办法是, 将这个"返回值"写入到stdout(通常都使用echo命令), 然后将其赋值给一个变量. 这种做法其实就是命令替换的一个变种.   相同的技术也可以用在字符串上. 这意味着函数可以"返回"非数字的值.  使用这种办法甚至能够"返回"多个值.
newstring=$(func params)

下一个技巧, 是将数组传递给函数的技术, 然后"返回"一个数组给脚本的主体.使用命令替换将数组中的所有元素(元素之间用空格分隔)赋值给一个变量, 这样就可以将数组传递到函数中了. 我们之前提到过一种返回值的策略, 就是将要从函数中返回的内容, 用echo命令输出出来, 然后使用命令替换或者( ... )操作符, 将函数的输出(也就是我们想要得返回值)保存到一个变量中. 如果我们想让函数"返回"数组, 当然也可以使用这种策略.
Pass_Array ()
{
local passed_array # 局部变量. 
passed_array=( `echo "$1"` )
echo "${passed_array[@]}"#  列出这个新数组中的所有元素,  这个新数组是在函数内声明的, 也是在函数内赋值的. 
 }

original_array=( element1 element2 element3 element4 element5 )

# 下面是关于如何将数组传递给函数的技巧. 
24 # **********************************
25 argument=`echo ${original_array[@]}`
26 # **********************************
27 #  将原始数组中所有的元素都用空格进行分隔,  然后合并成一个字符串, 最后赋值给一个变量.    注意, 如果只把数组传递给函数, 那是不行的. 
33 # 下面是让数组作为"返回值"的技巧. 
34 # *****************************************
35 returned_array=( `Pass_Array "$argument"` )
36 # *****************************************
37 # 将函数中'echo'出来的输出赋值给数组变量. 
39 echo "returned_array = ${returned_array[@]}"

利用双括号结构, 就可以让我们使用C风格的语法, 在for循环和while循环中, 设置或者增加变量.

一项很有用的技术是, 重复地将一个过滤器的输出(通过管道)传递给这个相同的过滤器, 但是这两次使用不同的参数和选项. 尤其是tr和grep, 非常适合于这种情况.

在错误的情况下, if-grep test可能不会返回期望的结果, 因为出错文本是输出到stderr上, 而不是stdout.将stderr重定向到stdout上, 就可以解决这个问题.

if ls -l nonexistent_filename 2>&1 | grep -q 'No such file or directory'
    then echo "File \"nonexistent_filename\" does not exist."
 fi

Bash, 版本3

一个新的, 更加通用的{a..z}大括号扩展操作符.
 for i in {1..10} # 比下面的方式更简单, 更直接   for i in $(seq 10)

${!array[@]}操作符, 用于扩展给定数组所有元素索引.

Array=(element-zero element-one element-two element-three)
echo ${Array[0]} # 元素0 数组的第一个元素. 
echo ${!Array[@]} # 0 1 2 3 # 数组的全部索引. 
for i in ${!Array[@]}
do
    echo ${Array[i]} 
done# 数组的全部元素. 

=~ 正则表达式匹配操作符, 在双中括号测试表达式中的应用

if [[ "$variable" =~ "T*fin*es*" ]]  # 在[[ 双中括号]]中使用=~操作符进行正则匹配.

 

posted @ 2014-09-18 16:44  赵翔  阅读(202)  评论(0编辑  收藏  举报