1.函数中输出函数名:$FUNCNAME
注意该变量是一个数组,如果嵌套调用函数,那么最里层函数中的此变量会是按调用顺序排列的所有函数的数组,如:
function log() { echo ${FUNCNAME[@]}}function action() { log}action
输出内容是:log action
如果只想获得当前函数的名称,直接输出即可:echo $FUNCNAME
用途:日志函数或者函数名传递等。
参考:
2.循环中的跳转:continue [n]
首先需要声明的是continue可用于子函数,跳转到父函数中的指定层次循环。如:
function son() { for z in 1 2 3; do if true; then echo $x,$y,$z continue 3 fi done}function father() { for x in 1 2 3; do for y in 1 2 3; do son done done}father
- n的值从1开始(不写默认为1,数字越大则层数越高,超过总循环层数时值为最大层数);
- 以上代码中只有最上层"x"会正常循环完,其余子层都在首轮跳出,此时n的最大值为3;
- 若在son后加一个“&”使其在后台运行,此时continue能控制y、z两层,但不能跳转到x,即n的最大值为2。
3.uniq注意事项
Note: 'uniq' does not detect repeated lines unless they are adjacent.该命令容易被忽略的一点是,它只会比较相邻的行而不会跨行比较,除非必要,否则最好先排序: sort -u
4.变量间接引用(indirect expansion)
两种方法见如下代码:
a=1b=aecho ${!b}eval echo \$$b
参考:、
5.sed模式空间
以下4种用法,第一个会替换每一行的第一个小写a,第二与第四个则会替换所有小写a,第三个只会替换第一行的第一个小写a:
printf 'aaa\naaa' | sed 's/a/A/'printf 'aaa\naaa' | sed 's/a/A/g'printf 'aaa\naaa' | sed ':a;N;$!ba;s/a/A/'printf 'aaa\naaa' | sed ':a;N;$!ba;s/a/A/g'
但是如果原始数据只有一行,那么最后两种写法均不会生效,
因此最好的写法是只替换第一行的第一个字符:printf 'aaa\naaa\n' | sed '1s/a/A/'
参考:
6.条件表达式
( ! [[ '123' =~ ^[0-9]+$ || 1 -gt 0 ]] ) && echo 1 || echo 2( ! [ 1 -eq 2 -o 1 -gt 0 ] ) && echo 1 || echo 2[ ! 1 -eq 2 -o 1 -gt 0 ] && echo 1 || echo 2! [ 1 -eq 2 -o 1 -gt 0 ] && echo 1 || echo 2注意:
- 带正则表达式的条件语句必须要写在双中括号“[[]]”中;
注意:在[] 表达式中,常见的>,<需要加转义字符,表示字符串大小比较,以acill码 位置作为比较。 不直接支持<>运算符,还有逻辑运算符|| && 它需要用-a[and] –o[or]表示
注意:[[]] 运算符只是[]运算符的扩充。能够支持<,>符号运算不需要转义符,它还是以字符串比较大小。里面支持逻辑运算符:|| &&
- 如果要对整个条件语句取反,最好是将“!”写在“[]”之外,并且尽量用“()”包围起来;
- 以上第三种写法没有达到要求,第四行不推荐,推荐前两种写法;
- 经测试,“[[]]”的条件运算效率是最高的,参见:
不考虑对低版本bash和对sh的兼容的情况下,用[[]]是兼容性强,而且性能比较快,在做条件运算时候,可以使用该运算符。
7.awk输出单引号
awk '{print "'\''"}',注意反斜杠后面是两个单引号。
解释:这是shell的问题,以上代码其实是三个引用(引用自blackold):
- '{print "'
- \'
- '"}'
shell解释后,awk看到的是:{print "'"}
参考:
8.删除UTF8文件中的BOM
简单来说BOM被微软用于区分UTF-8 和 ASCII编码文件,但是Linux中使用的UTF8标准不包含它
因此在执行包含BOM的UTF8编码的脚本的时候会报错:M-oM-;M-?#!/bin/bash$
用Win记事本保存的UTF8文件都会在文件头部包含一段不可见字符
查看:cat -A file | head -1 。去除方式有:
tail -c +4 old_file > new_filegrep -rIl $'^\xEF\xBB\xBF' . | xargs sed -ie '1s/\xEF\xBB\xBF//'find . -type f -exec sed '1s/^\xEF\xBB\xBF//' -i.bak {} \; -exec rm {}.bak \;
或者通过vim命令: :set nobomb(删除)、:set bomb(保留)
9.函数动态参数
function fun() { echo $9,$10,$11,$12 names=('a' 'b' c d e f g h x j k l) for i in $(seq 1 $#); do eval ${names[i-1]}=\'${!i}\' echo ${names[i-1]}=${!names[i-1]} done}fun 1 2 3 "4 5" 6 7 8 9 10 11 12 13
以上函数第一行输出的结果是:10,10,11,12
第2行开始结合使用数组、变量间接引用通过循环来赋值参数。
但是为什么“$10”不为11而重复一次,为何通过变量间接引用的时候又能正确输出?先往下看。
参考:
10.shift的用法
function fun() { local params=('a' 'b' c d e f g h x j k l others) local a b c d e f g h i j k l others for i in $(seq 1 $#); do if [ $i -le 12 ]; then eval ${params[i-1]}=\'$1\' else eval others=\'$others$1\' fi shift done for i in $(seq 1 ${#params[*]}); do echo ${params[i-1]}=${!params[i-1]} done}fun 1 2 3 "4 5" 6 7 8 9 10 11 12 13 14 15 16
shift [n]:其作用就是将n+1的参数位移到1,n不能大于最大参数个数,为0无效,默认为1。
以上脚本中,for循环里的shift每循环一次将参数位左移1位,使得“$1”始终取得的是逐一往后的参数;
最后再将剩余的参数全部赋值给others。
11.$BASH_REMATCH 用法
bash3.0以上版本扩展运算符“[[]]”中可用,该变量是一个数组,
实现了正则匹配的分组功能,类似于sed中的下标分组法:\n
echo "" | while read line; do if [[ $line =~ id=([0-9]+).*name=([^/]*) ]]; then echo ${BASH_REMATCH[1]} : ${BASH_REMATCH[2]} fidone
参考:
更多内置变量参考:,中文版(不全):
12.关于 IFS=$'\n'
该用法被称作:
bash中定义了一种申明特殊字符变量的写法:$'string'
在重定义系统变量IFS的时候经常用到,IFS的默认值为“<space><tab><newline>”,
经过美元符号修饰的换行符变成了事实上的换行符,而非两个字符“\”和“n”。
参考:
13.让for循环支持多个参数
当然这里并没有直接给for循环声明多个参数(语法不支持),而是通过一些小把戏来实现的:
xyz='a bc de f'function fun() { local a b i OLD_IFS=$IFS IFS=$'\n' # 注意该处变量xyz不能加引号包围 for i in $xyz; do # [临时变量]和[过程替换]结合使用 IFS=$OLD_IFS read a b < <(echo $i) echo $a,$b,$i done # 还原默认分隔符 IFS=$OLD_IFS}fun
注意:
- shell 中定义的变量都是全局变量,使用local(仅可在函数中使用)可以定义局部变量,在并发时全局变量很容易引起混乱
- read 读取内容的默认分隔符同样是“$IFS”,因此执行 read 读取变量前一定要将 IFS值还原。
- 关于“过程替换(Process Substitution)”,请参看我之前的文章: