详细介绍Linux shell脚本基础学习(二)

时间:2022-08-03 15:36:43

接详细介绍Linux shell脚本基础学习(一)我们来看一下shell中Here documents和函数的使用。

6. Here documents

HERE Documentbash里面定义块变量的途径之一

定义的形式为: 

命令<<HERE

...

...

...

HERE

它的作用即可以用来定义一段变量,会把命令和HERE之间的内容利用转向输入的方式交给该命令去处理。其中HERE相当于标记,可以是任何的字符串。注意后一个HERE的最后不要有空格,否则会报错。

使用HERE Document用在变量设定:

[aa@aa shell]$ wow='Wow,great!'
[aa@aa shell]$ m1=$(cat <<Here
> Line 1 is good.
> They are jack,marry and john.
> $wow
> Here
> )
[aa@aa shell]$ echo $m1
Line 1 is good. They are jack,marry and john. Wow,great!

注意这里HERE还有一个特殊的用法 :
就是在HERE前面加上-或者给HERE加上' ',加上-表明下述文字段所有TAB键将全部忽略,加上' '表明以下凡是变量定义用到了' ',将会使变量呈现所见即所得的形式,也即关闭变量替换;如果加上的是" "双引号,则会进行变量替换。

[aa@aa shell]$ wow='Wow,great!'
[aa@aa shell]$ m1=$(cat <<'Here'
> Line 1 is good.
> They are jack,marry and john.
> $wow
> Here
> )
[aa@aa shell]$ echo $m1
Line 1 is good. They are jack,marry and john. $wow

注意:这里的$wow被原样输出了。至于加上-的效果这里不细说,大家可自行测试。

它也可以用来定义一段注释 ,利用HERE Document做多行批注,方法是:
:<<HERE 
这是第一行批注
这是第二行批注
这是第三行批注
其它行,类推
HERE 
:代表什么都不做 
原本bash只支持单行批注(用#表示),现就可用于多行注释了,此处不细说。

当要将几行文字传递给一个命令时,here documents是一种不错的方法。对每个脚本写一段帮助性的文字是很有用的,此时如果我们四有那个 here documents就不必用echo函数一行行输出。 一个 "Here document" 以 << 开头,后面接上一个字符串,这个字符串还必须出现在here document的末尾。

下面我们来看一个例子,在该例子中,我们对多个文件进行重命名,并且使用here documents打印帮助:

 1 #!/bin/sh
 2 # we have less than 3 arguments. Print the help text:
 3 if [ $# -lt 3 ] ; then
 4 cat <<help
 5 -- renames a number of files using sed regular expressions
 6 USAGE: $0 'regexp' 'replacement' files...
 7 EXAMPLE: rename all *.HTM files in *.html:
 8 $0 'HTM$' 'html' *.HTM
 9 help
10 exit 0
11 fi
12 
13 OLD="$1"
14 NEW="$2"
15 # The shift command removes one argument from the list of
16 # command line arguments.
17 shift
18 shift
19 # $* contains now all the files:
20 for file in $*; do
21         if [ -f "$file" ] ; then
22                 newfile=`echo "$file" | sed "s/${OLD}/${NEW}/g"`
23                 if [ -f "$newfile" ]; then
24                         echo "ERROR: $newfile exists already"
25                 else
26                         echo "renaming $file to $newfile ..."
27                         mv "$file" "$newfile"
28                 fi
29         fi
30 done

执行脚本:

[aa@aa shell]$ ll
总用量 4
-rw-rw-r--. 1 yongli yongli   0 2月  17 14:06 a.html
-rw-rw-r--. 1 yongli yongli   0 2月  17 14:06 b.html
-rw-rw-r--. 1 yongli yongli   0 2月  17 14:06 c.html
-rwxrwxr-x. 1 yongli yongli 681 2月  17 14:21 here_doc.sh
[aa@aa shell]$ /data/shell/here_doc.sh

-- renames a number of files using sed regular expressions
USAGE: /data/shell/here_doc.sh 'regexp' 'replacement' files...
EXAMPLE: rename all *.HTM files in *.html:
/data/shell/here_doc.sh 'HTM$' 'html' *.HTM
[aa@aa shell]$ /data/shell/here_doc.sh 'htm$' 'html' *.htm

renaming a.htm to a.html ...
renaming b.htm to b.html ...
renaming c.htm to c.html ...

下面让我们详细说明下。

第一个if表达式判断输入命令行参数是否小于3个 (特殊变量$#表示包含参数的个数) 。如果输入参数小于3个,则将帮助文字传递给cat命令,然后由cat命令将其打印在屏幕上。打印帮助文字后程序退出($0: 表示当前脚本的名称)。

如果输入参数等于或大于3个,我们 就将第一个参数赋值给变量OLD,第二个参数赋值给变量NEW。

下一步,我们使用shift命令将第一个和第二个参数从参数列表中删除,这样原来的第三个参数就成为参数列表$*的第一个参数。

然后我们开始循环,命令行参数列表被一个接一个地被赋值给变量$file。

接着我们判断该文件是否存在,如果存在则通过sed命令搜索和替换来产生新的文件名。然后将反短斜线内命令结果赋值给newfile。这样我们就达到了我们的目的:得到了旧文件名和新文件名。然后使用mv命令进行重命名。

这里再看一个实例,利用HERE Document,打包C(或其它程序语言)的原始码。这是Cracker散布安全漏洞程序,最喜欢的方法

#!/bin/bash  
# Filename:create_prg.sh  
echo "creating hello.c..."  
echo  
cat <<'EOF' > hello.c  
#include <stdio.h>  
int main()  
{  
        printf("Hello world!\n");  
        return 0;  
}  
EOF
echo "compiling hello.c..."  
#compile hello.c,create the excutable file  
gcc -o hello hello.c

#if compile successfully,then excute it  
if [ $? -eq 0 ];then
        echo "excute hello..."  
        chmod u+x hello
        ./hello
else
        echo 'Compile Error:hello.c'  
fi

 7.函数

函数是一串命令的集合,函数可以把大的命令集合分解成若干较小的任务。编程人员可以基于函数进一步的构造更复杂的Shell 程序,而不需要重复编写相同的代码。

基本形式:

function_name()
{
command1
command2

commandN
}

对函数结构进行解释:
其中标题:函数名,函数体:函数内的命令集合,在编写脚本时要注意标题名应该唯一,如果不唯一,脚本执行时会产生错误。
在函数名前可以加上关键字function,但加上和省略关键字function对脚本的最终执行不产生任何影响。
函数体中的命令集合必须含有至少一条命令,即函数不允许空命令,这一点和C语言不同。
函数之间可通过参数、函数返回值交互,函数在脚本中出现的次序可以是任意的,但是必须按照脚本中的调用次序执行这些函数。
向函数传递参数
在bash Shell编程中,向函数传递的参数仍然是以位置参数的方式来传递的,而不能传递数组等其它形式变量,这与C语言或Java语言中的函数传递是不同的。

function1.sh

#!/bin/bash

half()
{
     let "n = $1"
     let "n = n/2"
     echo "In function half() n is $n"
}
if [ -z $1 ];then
    echo 'Please input number m.'
    echo "Uage:$0 number"
    echo "Example:$0 2"
    exit 0
fi
let "m = $1"
echo "Before the function half() is called, m is $m"
half $m
echo "After the function half() is called, m is $m"

在Linux Shell编程中,函数还可传递间接参数,但该方式传递方式远远没有C语言和Java语言灵活,而只能使用间接变量引用来传递参数,这种方式是一种笨拙的间接参数传递方式。

function2.sh

#!/bin/bash

ind_func()
{
    echo " $1"
}

if [ -z $1 ];then
        echo 'Please input arguments.'
        echo "Uage:$0 args"
        echo "Example:$0 a"
        exit 0
fi

parameter=ind_arg
ind_arg=Hello

ind_func "$parameter"

ind_func "${!parameter}"

echo "***********************"

ind_arg=World
ind_func "$parameter"
ind_func "${!parameter}"

函数返回值
有时需要脚本执行完成后返回特定的值来完成脚本的后继操作,这些特定的值就是函数返回值。在Linux Shell编程中,函数通过return返回其退出状态,0表示无错误,1表示有错误。在脚本中可以有选择的使用return语句,因为函数在执行完最后一条语句后将执行调用该函数的地方执行后继操作。

function3.sh

#!/bin/bash

show_week()
{
    echo -n "What you input is: "
    echo "$1"

    case $1 in 
    0)
        echo "Today is Sunday. "
        return 0;;
    1)
        echo "Today is Monday. "
        return 0;;
    2)
        echo "Today is Tuesday. "
        return 0;;
    3)
        echo "Today is Wednesday. "
        return 0;;
    4)
        echo "Today is Thursday. "
        return 0;;
    5)
        echo "Today is Friday. "
        return 0;;
    6)
        echo "Today is Saturday. "
        return 0;;
    *)
        return 1;;
    esac
}

if show_week "$1"
then
    echo "What you input is right! "
else 
    echo "What you input is wrong! "
fi
exit 0

脚本放置多个函数
可以在脚本中放置多个函数,脚本执行时按照调用函数的顺序执行这些函数.

function4.sh

#!/bin/bash

show_week()
{
    for day in Monday Tuesday Wednesday Thursday Friday Saturday Sunday
    do
        echo  -n "$day "
    done
    echo ""
}

show_number()
{
    for(( integer = 1; integer <= 7; integer++ ))
    do
        echo -n "$integer "
    done
     
    echo ""
}

show_square()
{
    i=0
    
    until [[ "$i" -gt 7 ]]
    do
        let "square=i*i"
        echo "$i * $i = $square"
        let "i++"
    done
}

show_week
show_number
show_square

函数相互调用
在Linux Shell编程中,函数之间可以相互调用,调用时会停止当前运行的函数转去运行被调用的函数,直到调用的函数运行完,再返回当前函数继续运行。
一个函数调用多个函数
在Linux Shell编程中允许一个函数调用多个函数,在该函数调用其他函数时同样需要按照调用的顺序来执行调用的函数。
局部变量和全局变量
在Linux Shell编程中,可以通过local关键字在Shell函数中声明局部变量,局部变量将局限在函数范围内。此外,函数也可调用函数外的全局变量,如果一个局部变量和一个全局变量名字相同,则在函数中局部变量将会覆盖掉全局变量.
函数递归
Linux Shell中可以递归调用函数,即函数可以直接或间接调用其自身。在递归调用中,主调函数又是被调函数。执行递归函数将反复调用其自身,每调用一次就进入新的一层。

function5.sh

#!/bin/bash

#递归调用函数
foo()
{
    read y
    foo $y
    echo "$y"
}

#调用函数
foo
exit 0

使用局部变量进行递归一般是针对数值运行来使用的。阶乘运算是一个使用局部变量的递归调用过程,实现了n! 的运算,其可以通过下面的公式表示:
n!=1 (n=0)
n!=n*(n-1)! (n>=1)
按照该公式可实现对阶乘的运算,由于该阶乘运算中存在终止条件“0!=1”,所以可以使用函数递归来实现该运算。

function6.sh

#!/bin/bash
#$1的阶乘

fact ()
{
    local num=$1
    if [ "$num" -eq 0 ]
    then
        factorial=1
    else
        let "decnum=num-1"
        fact $decnum
        let "factorial=$num * $?"
    fi
    return $factorial
}

if [ -z $1 ];then
        echo 'Please input an intger number argument.'
        echo "Uage:[$0 args]"
        echo "Example:[$0 5]"
        exit 0
fi

fact $1
echo "Factorial of $1 is $?"
exit 0

 ok.到这里先暂告一段落,其他的后续写出。