linux脚本编程之函数和select循环

一、select循环

select 循环主要用于创建菜单,以数字显示菜单的选项,用户输入被保存在内置变量REPLY 中。select通常和case一起用,并且是一个死循环,注意要设置退出循环的选项。

与for 循环类似,可以省略in list ,此时使用位置参量

语法:

select 变量name  in 变量列表
do
    action
done
[[email protected] blog2]# bash select.sh
1) begin
2) stop
3) continue
#? 1
This program starts to run
#? 2
This program will exit
#? 3
Program continue run
#? 4
Error happended
[[email protected] blog2]# cat select.sh
#!/bin/bash
select choice in "begin" "stop" "continue"
do
case $choice in
"begin")
    echo "This program starts to run"
;;
"stop")
    echo "This program will exit"
;;
"continue")
    echo "Program continue run"
;;
*)
    echo "Error happended"
    exit 1
;;
esac
done
[[email protected] blog2]#

二、shell函数

在所有的编程语言中,函数都是一个非常重要的内容,函数可以实现某一个单一的功能并实现重用减少代码的冗余。

shell中的函数和其他语言有些差别。函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程,它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分,Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改。

shell的函数格式:

第一种:
function func_name () {
    函数体
}
第二种:
func_name () {
    函数体
}
第三种:
function func_name {
    函数体
}

函数的使用环境:

  • 可在交互式环境下定义函数
  • 可将函数放在脚本文件中作为它的一部分
  • 可放在只包含函数的单独文件中

函数可直接通过函数名调用,函数名出现的地方,会被自动替换为函数代码。

函数的生命周期:被调用时创建,返回时终止。

函数的返回值:

函数有多种返回方法

  • 使用echo或printf命令进行输出
  • 函数体中调用命令的输出结果
  • 使用return返回函数的状态码,和其他的面向对象语言不同,shell的return只能返回数字,无法返回字符串和函数本身

交互环境下定义函数:

[[email protected] blog2]# printchar() { echo "This is a function"; } ###第一个大括号后要有空格,命令结束后要有分号
[[email protected] blog2]# printchar 
This is a function
[[email protected] blog2]#

注意,函数名不要和命令名相同,会优先执行函数,如果函数名和别名重复且别名是某个带参数的命令,将无法创建这个函数。定义的函数将一直保留到用户从系统退出,或执行了如下所示的unset命令,类似本地变量。

脚本中定义函数:

脚本中的函数是脚本的一部分,无法被其他脚本调用,注意脚本中的函数里的变量将对脚本中的同名变量产生影响,要使用局部变量让变量只在函数体中有效。

[[email protected] blog2]# bash func1.sh
shell var
the first func var
the first func var
[[email protected] blog2]# cat func1.sh
#!/bin/bash
var="shell var"
echo $var
func() {
    var="the first func var"
}
func
echo $var
foo() {
    local var="the second func var"
}
foo
echo $var

函数文件:

可以在系统开机自动加载的文件中看到定义环境的函数,它们将在开机加载到内存中

可以将经常使用的函数存入函数文件,然后将函数文件载入shell。

一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。

若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件。

[[email protected] blog2]# cat funcfile.sh
#!/bin/bash
func1() {
    echo "this is the first func"
}
func2() {
    echo "this is the second func"
}
[[email protected] blog2]# . funcfile.sh

执行set查看当前环境

[[email protected] blog2]# func1 
this is the first func
[[email protected] blog2]# func2
this is the second func
[[email protected] blog2]# unset func2
[[email protected] blog2]# func2
-bash: func2: command not found
[[email protected] blog2]# func1
this is the first func

函数参数:

传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“testfuncarg1 arg2 ...”

在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用[email protected], $*, $#等特殊变量

可以使用函数的返回值来定义一个变量

[[email protected] blog2]# bash varfunc.sh hello
hello
[[email protected] blog2]# cat varfunc.sh 
#!/bin/bash
func() {
    echo "$1"
}
var=$(func $1)   ###不能返回函数本身或其他复杂的对象,功能有限
echo $var
[[email protected] blog2]#

函数递归:

函数可以直接或间接调用自身,但是递归有层次的限制

[[email protected] blog2]# bash rec.sh 6
720
[[email protected] blog2]# bash rec.sh 3
6
[[email protected] blog2]# bash rec.sh 4
24
[[email protected] blog2]# bash rec.sh 100
0
[[email protected] blog2]# bash rec.sh 20
2432902008176640000
[[email protected] blog2]# bash rec.sh 30
-8764578968847253504
[[email protected] blog2]# bash rec.sh 25
7034535277573963776
[[email protected] blog2]# bash rec.sh 26
-1569523520172457984
[[email protected] blog2]# cat rec.sh 
#!/bin/bash
func() {
    if [ $1  -le 1 ];then
        echo 1
    else
        echo $[${1}*$(func $[${1}-1])]
    fi
}
func $1

练习:

1、斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0、1、1、2、3、5、8、13、21、34、……,斐波纳契数列以如下被以递归的方法定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2)
写一个函数,求n阶斐波那契数列

[[email protected] blog2]# bash fibonacci.sh 16
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
[[email protected] blog2]# cat fibonacci.sh 
#!/bin/bash
func(){
    if [ $1 -eq 1 ];then
        echo 0
    elif [ $1 -eq 2 ];then
        echo 1
    else
        echo $[$(func $[${1}-1])+$(func $[${1}-2])]
    fi
}
for i in `seq 1 $1`;do
    func $i
done

上面利用递归函数的方法去算每一个数,由于每个数都是从0累加算出来的,如果超过15个数后将耗费大量的运算时间,这并不是一个合格的算法............

[[email protected] blog2]# bash fibonacci2.sh 20
0
1
1
2
3
5
8
13
21
34
55
89
144
233
377
610
987
1597
2584
4181
[[email protected] blog2]# cat fibonacci2.sh 
#!/bin/bash
declare -a a=(0 1)
for ((i=2;i<$1;i++));do
    let a[i]=a[i-1]+a[i-2]
done
echo ${a[@]}|tr ‘ ‘ ‘\n‘

运用数组,将每次计算的结果存起来,当计算下一个数时只需一次计算而无需从头开始,几乎没有等待时间。

2、汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。利用函数,实现N片盘的汉诺塔的移动步骤

[[email protected] day03]# bash hanlota.sh 3 A B C
Move 1 from A to C
Move 2 from A to B
Move 1 from C to B
Move 3 from A to C
Move 1 from B to A
Move 2 from B to C
Move 1 from A to C
[[email protected] day03]# bash hanlota.sh 4 A B C
Move 1 from A to B
Move 2 from A to C
Move 1 from B to C
Move 3 from A to B
Move 1 from C to A
Move 2 from C to B
Move 1 from A to B
Move 4 from A to C
Move 1 from B to C
Move 2 from B to A
Move 1 from C to A
Move 3 from B to C
Move 1 from A to B
Move 2 from A to C
Move 1 from B to C
[[email protected] day03]# cat hanlota.sh 
#!/bin/bash
func1() {
    echo "Move $1 from $2 to $3"
}
func2() {
    if [ $1 -eq 1 ];then
        func1 1 $2 $4
    else
        func2 $[$1-1] $2 $4 $3   #####第一步,执行递归函数
        func1 $1 $2 $4      ######第二步,只需移动一次,仅执行func1即可
        func2 $[$1-1] $3 $2 $4######第三步,执行递归函数
    fi
}
func2 $1 $2 $3 $4

汉诺塔问题是一个典型的递归问题。可以将移动汉诺塔的问题分为三步走。例如:我要移动n个圆盘从A柱移到C柱,将B柱看作一个辅助的柱子。可以将A看作源柱,B看做辅助柱,C看成目标柱

第一步:先将A柱上的n-1个小圆盘从A利用C移到B上,这时的源柱、辅助柱和目标柱的顺序为A-C-B

第二步:只需移动一个盘,即将A上最大的盘从A利用B移动到C上,这时的源柱、辅助柱和目标柱的顺序为A-B-C

第三步:将B柱上的n-1个圆盘从B利用A移动到C柱,这时的源柱、辅助柱和目标柱的顺序为B-A-C

依次类推,如果递归执行上面的步骤将一直到n=1,这时就简单了,直接将圆盘从源柱移动到目标柱即可。

在上面的脚本中我先定义了移动一次圆盘的动作函数func1。接着定义了实现上面三个步骤的函数func2。然后调用func2即可。

时间: 2024-10-13 13:55:33

linux脚本编程之函数和select循环的相关文章

8.17_Linux之bash shell脚本编程入门篇(三)之循环以及函数function的使用

bash shell脚本编程入门篇(三)之循环 什么是循环执行? 将某代码段重复运行多次 重复运行多少次: 循环次数事先已知 循环次数事先未知 有进入条件和退出条件 相关命令:for, while, until,selet, for命令的使用 作用: 依次将列表中的元素赋值给"变量名"; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束 命令格式: for 变量名 in 列表; do 循环体(正常执行的执行命令) 语句1 语句2 语句3 ... done 列表生成方式: (

嵌入式 Linux网络编程(四)——Select机制

嵌入式 Linux网络编程(四)--Select机制 一.select工作机制 poll和select,都是基于内核函数sys_poll实现的,不同在于在linux中select是从BSD Unix系统继承而来,poll则是从SYSTEM V Unix系统继承而来,因此两种方式相差不大.poll函数没有最大文件描述符数量的限制.poll和 select与一样,大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,开销随着文件描述符数量的增加而线性增大. select需要驱动程序的支持,驱动

linux脚本编程(shell)浅介 (转载)

linux脚本(shell)编程 啊,昨天上网看到一篇讲 linux/unix shell 的文章,想想自己最后写这东西也是一年前的事了,想想都快忘光了. 还是整理一下,做一次回顾,以后说不定还用得上:帖出来,方便第一次学习这东西的同道中人. 如果发现有错误的地方,请指出,留一句即可,我会感激的.废话少说了!!! linux 下最重要的脚本语言算是 bash 了,我也就写点这个吧(我也只会这个:)).跟其他开发语言(如C)比,bash 是比较简单的一种语言,主要用于写一些脚本代码,一些批处理或安

Linux系统编程-setitimer函数

功能:linux系统编程中,setitimer是一个经常被使用的函数,可用来实现延时和定时的功能. 头文件:sys/time.h 函数原型: int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value); 参数含义: 1.which参数用来设置定时器类型,可选的值为 (1)ITIMER_REAL : 设置定时器以系统真实所花费的时间来计时,运行指定时间后发送SIGALRM信号. (

linux脚本编程技术---8

一.什么是脚本 脚本是一个包含一系列命令序列的可执行(777)文本文件.当运行这个脚本文件时,文件中包含的命令序列将得到自动执行. 二.脚本编程 #!/bin/sh 首行固定格式 #!表明该脚本的的解析器,这里使用/bin/sh来解析 2.1变量 shell脚本允许用户设置和使用自己的变量,变量可以是数字或者字符串,用户无需指定其类型,也无需在使用前定义. #!/bin/sh a="hello world" b=5 echo “A is: a” //使用变量时要用 符号 echo “B

linux 网络编程常用函数及流程

一.网络编程之TCP流程 服务端:socket---bind---listen---while(1){---accept---recv---send---close---}---close 客户端:socket----------------------------------connect---send---recv-----------------close 二.网络编程常用函数 服务器端: 头文件包含: #include<sys/types.h> #include<sys/sock

谢烟客---------Linux之bash脚本编程---if补充和for循环

bash:过程式编程,为了完成更复杂的任务,支持顺序执行.选择执行.循环执行 顺序执行:从左而右,依次执行命令. 选择执行:依据condition(条件)的执行状态结果,选择执行不同的代码片段. 循环执行:依据condition(条件)的执行状态结果,决定是否进入循环. condition: ture: 表示条件状态结果为0 false: 表示条件执行状态结果非0 if 单分支.双分支.多分支.嵌套if语句 1.单分支结构 if condition; then     if-ture fi 2.

linux命令:function脚本编程之函数

  function命令简介: 定义函数 1.命令格式: function 函数名 {   content      #内容  } 或者    函数名() {    content  #内容 } 调用函数只要在需要调用的地方输入函数名即可 2.命令功能: 使得脚本简洁,调用重复内容 3.命令参数: 自定义函数执行状态返回值 return #   #为0-255 且函数可以接受参数 FUNCTION () { content } FUNCTION 5 6 $1=5 $2=6 4.命令实例: 1.使

Linux系统编程——I/O多路复用select、poll、epoll的区别使用

I/O 多路复用技术是为了解决进程或线程阻塞到某个 I/O 系统调用而出现的技术,使进程不阻塞于某个特定的 I/O 系统调用. select(),poll(),epoll()都是I/O多路复用的机制.I/O多路复用通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪,就是这个文件描述符进行读写操作之前),能够通知程序进行相应的读写操作.但select(),poll(),epoll()本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程