bash shell技巧汇总

详细参考: ChinaUnix » 论坛 » Shell

一、用户登陆进入系统后的系统环境变量:
$HOME 使用者自己的目录
$PATH 执行命令时所搜寻的目录
$TZ 时区
$MAILCHECK 每隔多少秒检查是否有新的信件
$PS1 在命令列时的提示号
$PS2 当命令尚未打完时,Shell 要求再输入时的提示号
$MANPATH man 指令的搜寻路径

二、特殊变量:

$0 这个程序的执行名字
$n 这个程序的第n个参数值,n=1..9
$* 这个程序的所有参数
$# 这个程序的参数个数
$$ 这个程序的PID
$! 执行上一个指令的PID
$? 执行上一个指令的返回值

三、shell中的变元:
* 任意字符串
? 一个任意字符
[abc] a, b, c三者中之一
[a-n] 从a到n的任一字符

四、几个特殊字符表示

\b 退回
\c 打印一行时没有换行符 这个我们经常会用到
\f 换页
\r 回车
\t 制表
\v 垂直制表
\\ 反斜线本身

五、判断文件的属性

格式:-操作符 filename
返回true如果...

-e        文件存在
-a        文件存在
这个选项的效果与-e相同.但是它已经被弃用了,并且不鼓励使用
-f        file是一个regular文件(不是目录或者设备文件)
-s        文件长度不为0
-d        文件是个目录
-b        文件是个块设备(软盘,cdrom等等)
-c        文件是个字符设备(键盘,modem,声卡等等)
-p        文件是个管道
-h        文件是个符号链接
-L        文件是个符号链接
-S        文件是个socket
-t        关联到一个终端设备的文件描述符
这个选项一般都用来检测是否在一个给定脚本中的stdin[-t0]或[-t1]是一个终端
-t fd       : 当fd 是与终端设备相关联的文件描述符时返回真
-r        文件具有读权限(对于用户运行这个test)
-w        文件具有写权限(对于用户运行这个test)
-x        文件具有执行权限(对于用户运行这个test)
-g        set-group-id(sgid)标志到文件或目录上
如果一个目录具有sgid标志,那么一个被创建在这个目录里的文件,这个目录属于创建
这个目录的用户组,并不一定与创建这个文件的用户的组相同.对于workgroup的目录
共享来说,这非常有用.见<<UNIX环境高级编程中文版>>第58页.
-u        set-user-id(suid)标志到文件上
如果运行一个具有root权限的文件,那么运行进程将取得root权限,即使你是一个普通
用户.[1]这对于需要存取系统硬件的执行操作(比如pppd和cdrecord)非常有用.如果
没有suid标志的话,那么普通用户(没有root权限)将无法运行这种程序.
见<<UNIX环境高级编程中文版>>第58页.
-rwsr-xr-t    1 root       178236 Oct  2  2000 /usr/sbin/pppd
对于设置了suid的文件,在它的权限标志中有"s".
-k        设置粘贴位,见<<UNIX环境高级编程中文版>>第65页.
对于"sticky bit",save-text-mode标志是一个文件权限的特殊类型.如果设置了这
个标志,那么这个文件将被保存在交换区,为了达到快速存取的目的.如果设置在目录
中,它将限制写权限.对于设置了sticky bit位的文件或目录,权限标志中有"t".
drwxrwxrwt    7 root         1024 May 19 21:26 tmp/
如果一个用户并不时具有stick bit位的目录的拥有者,但是具有写权限,那么用户只
能在这个目录下删除自己所拥有的文件.这将防止用户在一个公开的目录中不慎覆盖
或者删除别人的文件,比如/tmp(当然root或者是目录的所有者可以随便删除或重命名
其中的文件).
-O        你是文件的所有者.
-G        文件的group-id和你的相同.
-N        从文件最后被阅读到现在,是否被修改.

f1 -nt f2
文件f1比f2新
f1 -ot f2
f1比f2老
f1 -ef f2
f1和f2都硬连接到同一个文件.

!        非--反转上边测试的结果(如果条件缺席,将返回true)

六、测试字符串
字符串1 = 字符串2 当两个字串相等时为真
字符串1 != 字符串2 当两个字串不等时为真
-n 字符串      当字符串的长度大于0时为真
-z 字符串      当字符串的长度为0时为真
字符串       当串字符串为非空时为真

七、测试两个整数关系
数字1 -eq 数字2     两数相等为真
数字1 -ne 数字2     两数不等为真
数字1 -gt 数字2     数字1大于数字2为真
数字1 -ge 数字2     数字1大于等于数字2为真
数字1 -lt 数字2     数字1小于数字2为真
数字1 -le 数字2     数字1小于等于数字2为真

八、逻辑测试
-a         与
-o        或
!        非



今天介绍shell特殊字符的引用
===============================
shell中的特殊字符有

1、$ 美元符
2、\ 反斜杠
3、` 反引号
4、" 双引号
5、< ,>,*,?,[,]

下面我一一举列说明
一、$符号
1、echo $? 显示的是上一条指令退出状态
2、echo "$?" 效果同上
3、echo '$?' 显示的是$?
4、echo \$? 显示的是$?
5、echo "\$?" 显示的是$?

大家可能已经看出 $符号在双引号中具有特殊意义 双引号对$符号不起作用
而单引号可以将特殊字符的的特殊意义屏蔽掉,使其能显示为字符本身,反斜
杠也可以将特殊字符的特殊含义屏蔽掉,使特殊字符失去特殊含义。

二、\ 反斜杠
反斜杠的作用是将特殊符号字符的特殊含义屏蔽掉,使其还是原字符
A=1234
echo \$A 显示为$A 如果不加\将显示为1234
echo \` 显示为`
echo \" 显示为双引号
echo \\ 显示为\

三、` 反引号
反引号的功能是命令替换,将反引号中的字符串做为命令来执行,我们在用shell编程时经常用的到 将系统命令的执行结果赋给一个变量

A=`date`
echo $A 显示的不是date而是当时的时间串
比如有一文件A的内容如下
ABCDEFG
1234456
abcdefg

B=`cat A|grep 234`

数组
显式声明一个数组
declare -a 数组名

可以整体定义数组:
ARRAY_NAME=(value0 value1 value2 value3 ...)
还可以单独定义数组的各个分量:
ARRAY_NAME[0]=value0
...
取得数组中的元素:
valuen=${ARRAY_NAME[n]}
取得数组元素的个数:
length=${#ARRAY_NAME[@]}
或者
length=${#ARRAY_NAME[*]}
取得数组单个元素的长度:
lengthn=${#ARRAY_NAME[n]}

打印数组所有的值
echo ${ARRAY_NAME[@]}

清空单个元素:
ARRAY_NAME[n]=

将整个数组清空:
ARRAY_NAME=()
unset ARRAY_NAME


1.test测试命令

test命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试
同时,bash也能完成简单的算术运算,格式如下:

$[expression]

例如:var1=2

var2=$[var1*10+1]

则:var2的值为21。

2.if条件语句

if [ -x /sbin/quotaon ]; then

echo "Turning on Quota for root filesystem"

/sbin/quotaon /

elif [ -x /sbin/quotaon ]; then

/usr/bin/bash

else

echo "ok"

fi

3.for 循环

#!/bin/sh

for((i=19;i<FILENUM;i++))
do
echo $i
done

WORD="a b c d e f g h i j l m n o p q r s t u v w x y z"

for i in $WORD ; do

echo $i

done

#!/bin/sh

FILES=`ls /txt/*.txt`

for txt in $FILES ; do

doc=`echo $txt | sed "s/.txt/.doc/"`

mv $txt $doc

done

4.while和until 循环

#!/bin/sh

while [ -f /var/run/ppp0.pid ] ; do

killall pppd

done

i=1
sum=0
while [ $i -le 100 ]; do
sum=$((sum+i))
i=$((i+1))
done
echo "SUM=$sum"

local COUNT=0
while [ $COUNT -lt 20 ] ; do
sleep 1
let COUNT++
done


#!/bin/sh

until [ -f /var/run/ppp0.pid ] ; do

sleep 1

done

Shell还提供了true和false两条命令用于建立无限循环结构的需要,它们的返回状态分别是总为0或总为非0

5.case 条件选择

#!/bin/sh

case $1 in

start | begin)

echo "start something"

;;

stop | end)

echo "stop something"

;;

*)

echo "Ignorant"

;;

esac

case表达式中也可以使用shell的通配符(“*”、“?”、“[ ]”)。

6.无条件控制语句break和continue

break 用于立即终止当前循环的执行,而contiune用于不执行循环中后面的语句而立即开始下一个循环的执行。这两个语句只有放在do和done之间才有效。

7.函数定义

在shell中还可以定义函数。函数实际上也是由若干条shell命令组成的,因此它与shell程序形式上是相似的,不同的是它不是一个单独的进程,而是shell程序的一部分。函数定义的基本格式为:

functionname

{

若干命令行

}

调用函数的格式为:

functionname param1 param2 ……

shell函数可以完成某些例行的工作,而且还可以有自己的退出状态,因此函数也可以作为if、while等控制结构的条件。在函数定义时不用带参数说明,但在调用函数时可以带有参数,此时shell将把这些参数分别赋予相应的位置参数$1、$2、...及$*。

8.命令分组

在shell 中有两种命令分组的方法:“()”和“{}”,前者当shell执行()中的命令时将再创建一个新的子进程,然后这个子进程去执行圆括弧中的命令。当用户 在执行某个命令时不想让命令运行时对状态集合(如位置参数、环境变量、当前工作目录等)的改变影响到下面语句的执行时,就应该把这些命令放在圆括弧中,这 样就能保证所有的改变只对子进程产生影响,而父进程不受任何干扰;{}用于将顺序执行的命令的输出结果用于另一个命令的输入(管道方式)。当我们要真正使 用圆括弧和花括弧时(如计算表达式的优先级),则需要在其前面加上转

义符(\)以便让shell知道它们不是用于命令执行的控制所用。

9.信号

trap命令用于在shell程序中捕捉到信号,之后可以有三种反应方式:

(1)执行一段程序来处理这一信号

(2)接受信号的默认操作

(3)忽视这一信号

trap对上面三种方式提供了三种基本形式:

第一种形式的trap命令在shell接收到signal list清单中数值相同的信号时,将执行双引号中的命令串。

trap 'commands' signal-list

trap "commands" signal-list

为了恢复信号的默认操作,使用第二种形式的trap命令:trap signal-list

第三种形式的trap命令允许忽视信号:trap " " signal-list

注意:(1)对信号11(段违例)不能捕捉,因为shell本身需要捕捉该信号去进行内存的转储。

(2)在trap中可以定义对信号0的处理(实际上没有这个信号),shell程序在其终止(如执行exit语句)时发出该信号。

(3)在捕捉到signal-list中指定的信号并执行完相应的命令之后,如果这些命令没有将shell程序终止的话,shell程序将继续执行收到信号时所执行的命令后面的命令,这样将很容易导致shell程序无法终止。

另 外,在trap语句中,单引号和双引号是不同的,当shell程序第一次碰到trap语句时,将把commands中的命令扫描一遍。此时若 commands是用单引号括起来的话,那么shell不会对commands中的变量和命令进行替换,否则commands中的变量和命令将用当时具体

的值来替换。

10. 运行shell程序的方法

执行shell程序的方法有三种:

(1)sh shell程序文件名

格式为:bash shell 程序文件名

这实际上是调用一个新的bash命令解释程序,而把shell程序文件名作为参数传递给它。新启动的shell将去读指定的文件,执行文件中列出的命令,当所有的命令都执行完结束。该方法的优点是可以利用shell调试功能。

(2)sh
格式为:bash
这种方式就是利用输入重定向,使shell命令解释程序的输入取自指定的程序文件。

(3)用chmod命令使shell程序成为可执行的

11. bash程序的调试

bash -选择项 shell程序文件名

几个常用的选择项是:

-e:如果一个命令失败就立即退出

-n:读入命令但是不执行

-x: 进入跟踪方式,显示所执行的每一条命令

12.复杂表达式,语句
#echo "scale=2;0.78-0.1"|bc    /*scale=2保留小数位,bc计算器 */
#echo "scale=3;0.78>=0.781"|bc

#a=$(awk 'BEGIN{printf "%.2f",0.76-0.77}')
#expr index $a -

PAMV_ANUM=0.99.6.2
expr $PAMV_NUM : '[0-9.]*$'` -eq 0
PAMV_NUM=`echo ${PAMV_ANUM}|awk -F "." '{ printf "%d.%d", $1,$2 }'`

获取程序执行的路径
W=$0
echo ${W%/*}

批量替换文件字符
sed -i "s/oldString/newString/g" filename

执行多个命令:
bash -c "cmd1 && cmd2"
bash -c "source filename && source filename"

13. 条件变量替换:
Bash Shell可以进行变量的条件替换,既只有某种条件发生时才进行替换,替换
条件放在{}中.
(1) ${value:-word}
当变量未定义或者值为空时,返回值为word的内容,否则返回变量的值.
(2) ${value:=word}
与前者类似,只是若变量未定义或者值为空时,在返回word的值的同时将
word赋值给value
(3) ${value:?message}
若变量以赋值的话,正常替换.否则将消息message送到标准错误输出(若
此替换出现在Shell程序中,那么该程序将终止运行)
(4) ${value:+word}
若变量以赋值的话,其值才用word替换,否则不进行任何替换
(5) ${value:offset}
${value:offset:length}
从变量中提取子串,这里offset和length可以是算术表达式.
(6) ${#value}
变量的字符个数
(7) ${value#pattern}
${value##pattern}
去掉value中与pattern相匹配的部分,条件是value的开头与pattern相匹配
#与##的区别在于一个是最短匹配模式,一个是最长匹配模式.
(8) ${value%pattern}
${value%%pattern}
于(7)类似,只是是从value的尾部于pattern相匹配,%与%%的区别与#与##一样
(9) ${value/pattern/string}
${value//pattern/string}
进行变量内容的替换,把与pattern匹配的部分替换为string的内容,/与//的区
别与上同

注意:上述条件变量替换中,除(2)外,其余均不影响变量本身的值

vim中在paste剪贴板中的数据时会自动增加缩进,这对python这种对缩进要求严格的语言来说简直是恶梦, vim都默认设置为set nopaste。如何让它不缩进,保持原格式?为了避免 这些智能选项的影响,可以把paste开关打开。
输入 :set paste
需要关闭时
输入:set nopaste
我是在vimrc中加入了下面两句:
"Paste toggle - when pasting something in, don't indent.
set pastetoggle=<F3>
这样就可以用F3来切换了。


14.Troubleshooting
经典的 while 循环中的变量作用范围问题了。borne shell 有这个问题,如果存在标准输入有重定向时,(如例子中的 while read ... ),这时while 是在子shell中运行,所以出了循环,值就丢了。解决的办法是另外再建立一个管道比如 test-nslookup (如果使用for,while,until,if,case这些命令时用到了重定向,那么sh会产生一个子shell来运行它们。)

break命令    使用这条命令能够从封闭的for/while/until循环中退出。还可加上数值参数来表示退出循环的层数(break 2),不加参数退出一层循环。

在 shell 脚本中,不管是否在函数中定义,变量默认就是全局的,一旦定义之后,对于此后执行的命令全部可见。bash 也支持局部变量,不过需要使用 local 关键字进行显式地声明。local 是bash 中的一个内嵌命令,其作用是将变量的作用域设定为只有对本函数及其子进程可见。局部变量只能在变量声明的代码块中可见,这也就意味着在函数内声明的局部变 量只能在函数代码块中才能被访问,它们并不会污染同名全局变量。

$? 所能传递的最大值是 255,超过该值就没有办法利用这种方式来传递返回值了。解决这个问题的方法有两种,一种是利用全局变量,另外一种则是利用其他方式进行周转(例如标准输 入输出设备)。

在计算 1 到 500 的累加和时,利用标准输入输出设备传递返回值的方法速度要比利用全局变量慢 1 倍以上。随着迭代次数的增加,二者的差距也会越来越大,主要原因标准输入输出设备都是字符设备,从中读写数据耗时会很长;而全局变量则是在内存中进行操作 的,速度会明显快很多。

为了提高 shell 脚本的性能,在编写 shell 脚本时,应该尽量多使用 shell 的内嵌命令,而不能过多地调用外部脚本或命令,因为调用内嵌命令时不会 fork 新的进程,而是在当前 shell 环境中直接执行这些命令,这样可以减少很多系统开销。以计算表达式的值为例,前面的例子我们都是通过调用 expr 来对表达式进行求值的,但是 bash 中提供了一些内嵌的计算表达式手段,例如 ((i = $j + $k)) 与 i=`expr $j + $k` 的效果就是完全相同的,都是计算变量 j 与 k 的值,并将结果赋值给变量 i,但是前者却节省了一次 fork 新进程以及执行 expr 命令的开销。

IFS 是个很有用的变量,默认下用来分割空格、制表、换行等,也可以用来分割指定字符,比如把 www:vpsee:com:8080 分割成 www vpsee com 8080 就可以用 IFS:
bash-3.00$ line=www:vpsee:com:8080
bash-3.00$ IFS=':'
bash-3.00$ for i in $line; do  echo $i; done
www
vpsee
com
8080



多行文本给变量

VAR=`cat <<EOF

aaaaaa

   bbbb

cccc

EOF`

写多行文本到文件

cat > filename << EOF

aaaaaaaaa

   bbbb

cccccc

EOF



LINUX/UNIX数据流重定向及相关问题


我们经常会在UNIX系统下的一些脚本中看到类似”2>&1″这样的用法,例如“/path/to/prog 2>&1 > /dev/null &”,那么它的具体含义是什么呢?
UNIX有几种输入输出流,它们分别与几个数字有如下的对应关系:
0-标准输入流(stdin),1-标准输出流(stdout),2-标准错误流 (stderr)。
"2>&1" 的意思就是将stderr重定向至stdout,并一起在屏幕上显示出来。如果不加数字,那么默认的重定向动作是针对stdout(1)的,比如”ls -l > result”就等价于”ls -l 1 > result”。这样便于我们更普遍性的理解重定向过程。
下面举例说明:
#cat std.sh
#!/bin/sh
echo “stdout”
echo “stderr” >&2

#/bin/sh std.sh 2>&1 > /dev/null
stderr

#/bin/sh std.sh > /dev/null 2>&1

第一条命令的输出结果是stderr,因为stdout和stderr合并后一同重定向到/dev/null,但stderr并未被清除,因此 仍将在屏幕中显示出来;第二条命令无输出,因为当stdout重定向至/dev/null后,stderr又重定向到了stdout,这样stderr也 被输出到了/dev/null。

/dev/null是什么设备?
/dev/null是“垃圾黑洞”,所有无用的东西都可以写到里面区。举个实例,你把一个文件move到/dev/null,那他就永远消失了,就想掉进了黑洞里。 并不是什么具体的设备。一些不想现实的信息都可以重定向到/dev/null中。
通 常用法: #/bin/sh std.sh > /dev/null 2>&1 把标准输出了标准出错都重定向到/dev/null,就是空设备,不现实出错信息。 nohup sh std.sh>/dev/null 2>&1 &,此时nohup.out中将没有信息。



收藏  | 打印  | 字体:  -缩小  放大+    
[ x ] 请正确填写下面信息


是否保存此网页快照 是否公开此收藏

查看全部评论(4)我来说两句

更多... bash shell 相关文章