linux shell 高级,Linux Shell高级技巧

一、在循环中使用管道的技巧:

在Bash Shell中,管道的最后一个命令都是在子Shell中执行的。这意味着在子Shell中赋值的变量对父Shell是无效的。所以当我们将管道输出传送到一个循环结构,填入随后将要使用的变量,那么就会产生很多问题。一旦循环完成,其所依赖的变量就不存在了。

/> cat > test8_1.sh

#!/bin/sh

#1. 先将ls -l命令的结果通过管道传给grep命令作为管道输入。

#2. grep命令过滤掉包含total的行,之后再通过管道将数据传给while循环。

#3. while read line命令从grep的输出中读取数据。注意,while是管道的最后一个命令,将在子Shell中运行。

ls -l | grep -v total | while read line

do

#4. all变量是在while块内声明并赋值的。

all="$all $line"

echo $line

done

#5. 由于上面的all变量在while内声明并初始化,而while内的命令都是在子Shell中运行,包括all变量的赋值,因此该变量的值将不会传递到while块外,因为块外地命令是它的父Shell中执行。

echo "all = " $all

/> ./test8_1.sh

-rw-r--r--.  1 root root 193 Nov 24 11:25 outfile

-rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh

-rwxr-xr-x. 1 root root 108 Nov 24 12:48 test8_1.sh

all =

为了解决该问题,我们可以将while之前的命令结果先输出到一个临时文件,之后再将该临时文件作为while的重定向输入,这样while内部和外部的命令都将在同一个Shell内完成。

/> cat > test8_2.sh

#!/bin/sh

#1. 这里我们已经将命令的结果重定向到一个临时文件中。

ls -l | grep -v total > outfile

while read line

do

#2. all变量是在while块内声明并赋值的。

all="$all $line"

echo $line

#3. 通过重定向输入的方式,将临时文件中的内容传递给while循环。

done 

#4. 删除该临时文件。

rm -f outfile

#5. 在while块内声明和赋值的all变量,其值在循环外部仍然有效。

echo "all = " $all

/> ./test8_2.sh

-rw-r--r--.  1 root root   0 Nov 24 12:58 outfile

-rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh

-rwxr-xr-x. 1 root root 140 Nov 24 12:58 test8_2.sh

all =  -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh -rwxr-xr-x. 1 root root 135 Nov 24 13:16 test8_2.sh

上面的方法只是解决了该问题,然而却带来了一些新问题,比如临时文件的产生容易导致性能问题,以及在脚本异常退出时未能及时删除当前使用的临时文件,从而导致生成过多的垃圾文件等。下面将再介绍一种方法,该方法将同时解决以上两种方法同时存在的问题。该方法是通过HERE-Document的方式来替代之前的临时文件方法。

/> cat > test8_3.sh

#!/bin/sh

#1. 将命令的结果传给一个变量

OUTFILE=`ls -l | grep -v total`

while read line

do

all="$all $line"

echo $line

done <

#2. 将该变量作为该循环的HERE文档输入。

$OUTFILE

EOF

#3. 在循环外部输出循环内声明并初始化的变量all的值。

echo "all = " $all

/> ./test8_3.sh

-rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh

-rwxr-xr-x. 1 root root 135 Nov 24 13:16 test8_3.sh

all =  -rwxr-xr-x. 1 root root 284 Nov 24 10:01 test7.sh -rwxr-xr-x. 1 root root 135 Nov 24 13:16 test8_3.sh

二、自链接脚本:

通常而言,我们是通过脚本的命令行选项来确定脚本的不同行为,告诉它该如何操作。这里我们将介绍另外一种方式来完成类似的功能,即通过脚本的软连接名来帮助脚本决定其行为。

文件名:test9.sh

#!/bin/sh

#basename命令将剥离脚本的目录信息,只保留脚本名,从而确保在相对路径的模式下执行也没有任何差异。

#通过sed命令过滤掉脚本的扩展名。

dowhat=`basename $0 | sed 's/\.sh//'`

echo $dowhat

#这里的case语句只是为了演示方便,因此模拟了应用场景,在实际应用中,可以为不同的分支执行不同的操作,或将某些变量初始化为不同的值和状态。

case $dowhat in

test9)

echo "I am test9.sh"

;;

test9_1)

echo "I am test9_1.sh."

;;

test9_2)

echo "I am test9_2.sh."

;;

*)

echo "You are illegal link file."

;;

esac

[root@wu 桌面]# ln -s test9.sh test9_2.sh

[root@wu 桌面]# ./test9_2.sh

test9_2

I am test9_2.sh.

三、编写一个更具可读性的df命令输出脚本

#!/bin/bash

#1. $$表示当前Shell进程的pid。

#2. trap信号捕捉是为了保证在Shell正常或异常退出时,仍然能够将该脚本创建的临时awk脚本文件删除。

awk_script_file="/tmp/scf_tmp.$$"

trap "rm -f $awk_script_file" EXIT

#3. 首先需要说明的是,'EOF'中的单引号非常重要,如果忽略他将无法通过编译,这是因为awk的命令动作必须要用单引号扩住。

#4. awk脚本的show函数中,int(mb * 100) / 100这个技巧是为了保证输出时保留小数点后两位。

cat < $awk_script_file

function show(size) {

mb = size / 1024;

int_mb = (int(mb * 100)) / 100;

gb = mb / 1024;

int_gb = (int(gb * 100)) / 100;

if (substr(size,1,1) !~ "[0-9]" || substr(size,2,1) !~ "[0-9]") {

return size;

} else if (mb 

return size "K";

} else if (gb 

return int_mb "M";

} else {

return int_gb "G";

}

}

#5. 在BEGIN块中打印重定义的输出头信息。

BEGIN {

printf "%-20s %7s %7s %7s %8s %s\n","FileSystem","Size","Used","Avail","Use%","Mounted"

}

#6. !/文件系统/ 表示过滤掉包含Filesystem的行,即df输出的第一行。其余行中,有个域字段可以直接使用df的输出,有的需要通过show函数的计算,以得到更为可读的显示结果。

!/文件系统/ {

size = show($2);

used = show($3);

avail = show($4);

printf "%-20s %7s %7s %7s %8s %s\n",$1,size,used,avail,$5,$6

}

EOF

df -k | awk -f $awk_script_file

[wulei@bogon 桌面]$ ./dd

FileSystem              Size    Used   Avail     Use% Mounted

/dev/sda2             17.45G   2.47G  14.09G      15% /

tmpfs                503.26M    232K 503.03M       1% /dev/shm

/dev/sda1            290.51M  31.03M 244.47M      12% /boot

你可能感兴趣的:(linux,shell,高级)