09-shell函数

为什么学函数

学习 Bash 函数 是运维工程师和脚本开发者提升效率的关键技能。以下是学习 Bash 函数的核心价值及实际应用场景:


一、为什么要学 Bash 函数?

  1. 代码复用
    避免重复代码,将常用操作封装为函数,例如日志格式化、错误检查、数据验证等。
    示例

    ```bash

    封装颜色输出函数

    语法 function 函数名(){

    函数体
    

    }

    函数名

    red() {

    echo -e "\033[31m$1\033[0m";  # $1 可以给函数传入参数,且第一个参数
    

    }

green() { echo -e "\033[32m$1\033[0m"; } red "Error: Disk full!" # 直接调用 green "Success!"


2. **逻辑模块化**  
   将复杂脚本拆解为多个函数,提升可读性和维护性。  
   **示例**:  

   ```bash
   # 函数支持传入参数 $1 $2 $3 $* $@ 提取参数for遍历

   # 校验输入的必须是纯数字
   validate_input() { 
       [[ "$1" =~ ^[0-9]+$ ]] || return 1; 
   }


   process_data() { 
       awk '{sum+=$1} END{print sum}' "$1"; 
   }


   # 主流程清晰
   validate_input "$input" && process_data "data.txt"
  1. 错误处理统一化
    集中管理错误逻辑,避免重复的 if-else 检查。
    示例
    ```bash die() {

    die函数,调用red函数,对打印,红色格式化

    red "FATAL: $1"; exit 1; 
    
    }

check_root() { [ $(id -u) -eq 0 ] || die "you are not root !"; }

check_root # 调用权限检查


4. **参数传递灵活性**  
   支持动态参数和返回值,适应不同场景需求。  
   **示例**:  

   ```bash
   get_disk_usage() {
       df -h "$1" | awk 'NR==2 {print $5}'; 
   }
   usage=$(get_disk_usage "/")  # 获取根分区使用率
  1. 团队协作标准化
    通过函数库实现代码共享,减少团队成员重复造轮子。

二、典型运维场景案例

案例 1:自动化备份(函数封装)

#!/bin/bash
# 封装备份函数,支持自定义目录和保留天数
backup_dir="/backups"

# 计算器,输入1,2,选择计算符

backup_data() {
  local src_dir=$1
  local keep_days=$2
  local timestamp=$(date +%Y-%m-%d_%H:%M:%S)
  local backup_name="backup-$(basename $src_dir)-$timestamp.tar.gz"

  tar -czf "$backup_dir/$backup_name" "$src_dir" || return 1
  find "$backup_dir" -name "backup-$(basename $src_dir)-*" -mtime +$keep_days -delete
}

# 调用示例
#backup_data "/var/www" 7    # 备份网站目录,保留7天
#backup_data "/etc" 30       # 备份配置目录,保留30天

# 函数参数,以文件参数传入形式执行,更动态
backup_data  $1 $2

执行脚本

bash t_func_backup.sh /var/log/ 7
bash t_func_backup.sh /var/log/ 7
bash t_func_backup.sh /var/log/ 3
bash t_func_backup.sh /etc 7
bash t_func_backup.sh /etc/apt/ 7

案例 2:服务健康检查(函数复用)

#!/bin/bash
# 定义通用服务检查函数
check_service() {
  local service_name=$1
  if systemctl is-active "$service_name" &>/dev/null; then
    green "$service_name is running"
    return 0
  else
    red "$service_name is DOWN!"
    return 1
  fi
}

# 批量检查关键服务
check_service nginx
check_service mysql
check_service redis

案例 3:安全审计(函数组合)

#!/bin/bash

# 加载颜色库
. colour.sh

# 定义审计函数库
check_ssh_root() {
# 如果改参数是no,系统禁用root登录,安全
  grep -q "^PermitRootLogin no" /etc/ssh/sshd_config || {
    yellow  "WARNING: SSH root login is enabled"
    return 1
  }
}

check_firewall() {
  iptables -L | grep -q "REJECT" && {
    green "INFO: Firewall is  blocking anything"
    return 0
  }
}

# 主审计流程
run_security_audit() {
  check_ssh_root
  check_firewall
  # 添加更多检查项...
}


run_security_audit

三、函数高级技巧

  1. 返回值处理
    使用 return 返回状态码,通过 $? 捕获结果:

    is_file_valid() { [ -s "$1" ] && [ -r "$1" ]; }
    is_file_valid "data.csv" || die "Invalid file"
    
  2. 参数传递
    通过 $1, $2 获取参数,$@ 处理所有参数:

    batch_compress() {
      for file in "$@"; do
        gzip -k "$file" && green "Compressed $file"
      done
    }
    batch_compress *.log
    
  3. 局部变量
    使用 local 避免变量污染:

    calculate() {
      local a=$1
      local b=$2
      echo $((a + b))
    }
    
  4. 函数库调用
    通过 source 导入外部函数:

    # utils.sh
    logger() { echo "[$(date)] $1"; }
    # main.sh
    source utils.sh
    logger "Starting backup..."
    

四、学习建议

  1. 从简单工具函数开始(如日志、错误处理)
  2. 逐步将现有脚本重构为函数式结构
  3. 学习调试技巧:set -x 跟踪函数执行
  4. 参考成熟项目的实现(如 Linux 启动脚本)

掌握 Bash 函数后,你将能编写出更专业、可维护性强的运维脚本,大幅提升自动化工作效率。

1.函数作用

函数是一个非常实用的技能,用于封装代码块,复用代码,省去同一段代码,重复写,导致代码像一块烂抹布;

封装函数后,代码立刻化身为高级绸缎!

shell代码,自上而下

先定义、后调用

在Bash脚本中,函数是一段可重复使用的代码块,用于执行特定任务。函数的主要作用包括:

  1. 代码复用:将常用代码封装在函数中,避免重复编写相同代码,提高脚本的可维护性。

  2. 模块化:将复杂脚本分解为多个函数,每个函数负责一个独立任务,使代码更清晰、易读。

  3. 简化调试:通过隔离问题到特定函数,简化调试过程。

  4. 提高可读性:函数名可以描述其功能,使脚本逻辑更易理解。

  5. 参数传递:函数可以接受参数,根据输入执行不同操作,增加灵活性。

  6. 局部变量:函数内定义的变量默认局部,避免与全局变量冲突。

示例

# 定义一个函数
greet() {
    local name=$1
    echo "Hello, $name!"
}

# 调用函数
greet "Alice"
greet "Bob"

输出

Hello, Alice!
Hello, Bob!

关键点

  • 使用 function_name()function function_name 定义函数。
  • 通过 $1, $2 等访问传递给函数的参数。
  • 使用 local 关键字定义局部变量,避免影响全局变量。

总结

Bash函数通过封装代码、模块化、简化调试和提高可读性,增强了脚本的灵活性和可维护性。

2.函数定义与调用

在Bash脚本中,函数可以通过两种方式定义,并通过函数名直接调用。以下是定义和调用函数的详细说明及示例。


1. 定义函数

Bash 函数有两种定义方式:

方式 1:使用 function 关键字

function 函数名 {
    函数体
}

方式 2:省略 function 关键字

函数名() {
    函数体
}

2. 调用函数

定义函数后,直接通过函数名调用:

函数名

如果函数需要参数,可以在调用时传递参数:

函数名 参数1 参数2 ...

3. 示例

示例 1:无参数函数

# 定义函数
halo() {
    echo "Hello, World!"
}

# 调用函数
halo

输出:

Hello, World!

示例 2:带参数函数

# 定义函数
greet() {
    local name=$1
    echo "Hello, $name!"
}

# 调用函数并传递参数
greet "Alice"
greet "Bob"

输出:

Hello, Alice!
Hello, Bob!

示例 3:返回值

Bash 函数可以通过

态码(0 表示成功,非 0 表示失败),或者通过 echo 返回字符串。

# 定义函数
add() {
    local sum=$(( $1 + $2 ))
    echo $sum  # 返回结果
}

# 调用函数并捕获返回值
result=$(add 3 5)
echo "The sum is: $result"

输出:

The sum is: 8

示例 4:局部变量

使用 local 关键字定义局部变量,避免影响全局变量。

# 全局变量
var="I am global"


# 定义函数
my_function() {
    local var="I am local"
    echo "Inside function: $var"
}


echo "Before function: $var"

# 调用函数
my_function

echo "After function: $var"

输出:

Before function: I am global
Inside function: I am local
After function: I am global

4. 注意事项

  1. 参数传递:函数参数通过 $1, $2, $3 等访问,$0 是脚本名称,$# 是参数个数,$@ 是所有参数。
  2. 返回值return 只能返回整数状态码,如果需要返回字符串,可以使用 echo
  3. 局部变量:函数内使用 local 定义局部变量,避免污染全局作用域。
  4. 函数调用顺序:函数必须在调用之前定义。
#!/bin/bash
foo(){
        echo "文件名:$0 参数1:$1 参数2:$2"

}
foo $1 $2

总结

  • 定义函数:函数名() { ... }function 函数名 { ... }
  • 调用函数:函数名 参数1 参数2 ...
  • 使用 local 定义局部变量,return 返回状态码,echo 返回字符串。

通过函数,可以使 Bash 脚本更模块化、可读性更高,同时减少代码重复。

函数参数遍历

在 Bash 脚本中,你可以通过 $@$* 来遍历传递给函数的所有参数。以下是一个简单的示例,展示了如何在 Bash 函数中遍历所有参数:

#!/bin/bash

# 定义一个函数
my_function() {
    # 使用 $@ 遍历所有参数
    #for arg in "$@"; do
    for arg in "$*"; do
        echo "参数: $arg"
    done
}

# 调用函数并传递参数
my_function 1 2 3 a 4 b 6 c 8


my_function "参数1" "参数2" "参数3"

解释:

  • $@:表示所有传递给函数的参数,每个参数都被视为独立的字符串。
  • for arg in "$@"; do:遍历所有参数,arg 是当前迭代的参数。
  • echo "参数: $arg":打印当前参数。

示例输出:

参数: 参数1
参数: 参数2
参数: 参数3

其他相关变量:

  • $#:表示传递给函数的参数个数。
  • $1, $2, $3, ...:表示第1个、第2个、第3个参数,依此类推。
  • $*:表示所有参数,但它们被视为一个单一的字符串。
$@
$#
$*

参数处理
for
函数传参
数组

示例:使用 $# 获取参数个数

#!/bin/bash

my_function() {
    echo "你小子传递了 $# 个参数"
    for arg in "$@"; do
        echo "参数: $arg"
    done
}

my_function "参数1" "参数2" "参数3"

示例输出:

参数个数: 3
参数: 参数1
参数: 参数2
参数: 参数3

# 遍历,数文件个数的函数
my_func() {
    echo "你传入了 $# 个文件"
    for file in $@;do
            echo "文件:$file"
    done
}

通过这些方法,你可以在 Bash 函数中轻松地遍历和处理参数。

方法1,完整写法

function hello(){

    echo "hello chaoge linux."
}


function ngx_status(){
    systemctl status nginx
    ps -ef|grep nginx
}



# 执行函数
hello
ngx_status

方法2,懒人写法

hello(){
    echo "welcome my linux~~ www.yuchaoit.cn"
}


start(){
    systemctl start nginx
    ps -ef|grep nginx
    netstat -tunlp|grep nginx

}


# 先后调用函数
start
hello

函数返回值

Bash函数可以定义成两种形式,一种是function name { ... },另一种是name() { ... }。

在函数里,可以使用return语句来退出函数并返回一个状态码。

这个状态码是一个整数,范围应该是0到255之间,对吧?因为Unix的退出状态码通常都是这样的。

那return的作用是什么呢?比如说,在函数执行到某个条件时,用return来结束函数,并且返回一个值。

这个返回值可以被调用函数的地方通过$?来获取,或者可以在条件判断中直接使用函数,比如if myfunc; then...。这个时候,函数的返回值会影响条件判断的结果。

不过,这里可能有个误区。

和某些编程语言不同,Bash的return并不是用来返回数据的,比如字符串或者数值。

它只能返回状态码。

如果需要返回数据,通常是通过全局变量,或者使用echo输出,然后在调用函数的时候用命令替换的方式获取,比如result=$(myfunc)。

举个例子,如果我有一个函数,检查文件是否存在,如果存在返回0,否则返回1。

这时候,函数的返回状态码就可以用在if语句中,比如:

check_file() {

  if [ -f "$1" ]; then

  return 0

  else

  return 1

  fi

}

然后调用的时候:

if check_file data.txt; then

  echo "存在"

else

  echo "不存在"

fi

这种情况下,return的作用就体现出来了。

而如果我想要函数返回一个字符串,比如返回文件的路径,这时候就不能用return了。

得用echo或者printf,然后用命令替换来获取输出。stdout

# 定义函数,存在输出结果
foo(){
    tail -1 /etc/passwd # stdout
}
# 变量要获取,保存函数执行结果
# 语义,先执行,再保存结果
res_foo=$(foo)

另外,需要注意的一点是,如果在函数中没有显式使用return,那么函数的退出状态是最后执行的命令的退出状态。比如:



myfunc() {
    # 函数拿到最后一条命令的执行状态吗
  ls /non/existent/directory
  # 这里正常,所以函数也看到0,非显示传入return的坑
    echo 有点意思
}

这里,ls命令执行失败,返回非零的状态码,所以myfunc的返回值也就是这个非零值。如果函数里有多个命令,最后一个命令的返回值就是函数的返回值。

那么,return的语法是return [n],其中n就是返回的状态码。如果不指定n,默认返回的是上一条命令的退出状态。例如:

myfunc() {
    return 5
}

调用这个函数后,$?的值会是5。

所以,总结一下,return在Bash函数中的主要作用是设置函数的退出状态码

供调用者检查函数执行的结果是成功还是失败,或者根据不同的状态码进行不同的处理。if else

它不能用来返回其他类型的数据,只能返回整数状态码。

不过,有时候可能会有人混淆return和exit的用法。

exit是退出整个脚本,并返回指定的状态码,而return只是退出当前函数,并返回状态码。

比如,在函数中如果用了exit,整个脚本就会终止,而return只会终止函数的执行,继续执行脚本的其他部分。

所以,在编写Bash函数时,正确使用return和exit非常重要,避免意外终止脚本。

另外,关于返回值的范围,如果返回的值超过255,会发生什么?比如,return 256的话,实际返回值会是0,因为Bash使用8位来存储状态码,所以超过的部分会被取模运算,256 mod 256等于0。同理,return 257会变成1,依此类推。这可能是一个需要注意的问题,比如如果期望返回的是错误码255,如果返回256就变成了0,可能会被误认为成功。

举个例子:

exit_func(){
    echo 777
    return 7
    #exit 6 # 提供脚本结束后的状态吗
}


exit_func
# 处理函数退出吗,紧跟着函数后面用$?处理。。

echo 666
echo 666
echo 666
echo 666
echo 666
# 最后一个命令的执行状态吗  127
echo000 666



func() {

    return 256

}

func

echo $?  # 输出0

所以,在编写函数的时候,要确保返回的状态码在0-255之间,否则可能会有意想不到的结果。

再举一个例子,假设函数处理一些逻辑,需要返回不同的状态码来表示不同的错误类型,这时候就要注意不要超过255的限制。

总的来说,Bash函数的return语句用于控制函数的退出状态,而不是传递数据。正确使用它可以让脚本的逻辑更清晰,特别是在条件判断和错误处理方面。

在Bash脚本中,return 用于函数内部指定退出状态码,影响函数执行后的流程控制。以下是关键点总结:

  1. 基本用法
    return [n] 退出函数并返回状态码 n(0-255)。0 表示成功,非0 表示失败或特定错误。

    myfunc() {
        if [ 条件 ]; then
            return 0   # 成功
        else
            return 1   # 失败
        fi
    }
    
  2. 默认行为
    若未显式使用 return,函数返回最后一条命令的退出状态。

    func() {
        ls /无效路径   # 若失败,函数返回非0状态
    }
    
  3. 获取返回值
    通过 $? 或直接用于条件判断:

    myfunc
    echo "状态码: $?"
    
    if myfunc; then
        echo "成功"
    else
        echo "失败"
    fi
    
  4. exit 的区别

    • return 退出函数,脚本继续执行。
    • exit 终止整个脚本。
  5. 返回值范围限制
    超过255的值会取模(如 return 256 实际返回0)。

  6. 返回数据而非状态码
    需用 echo 输出并捕获:

    get_data() {
        echo "Hello"
    }
    result=$(get_data)
    

示例:检查文件存在性

check_file() {
    if [ -f "$1" ]; then
        return 0
    else
        return 1
    fi
}

if check_file "somefile.txt"; then
    echo "文件存在"
else
    echo "文件不存在"
fi

总结
return 用于设置函数退出状态码,控制脚本流程,而非传递数据。正确使用可提升脚本健壮性,避免逻辑错误。

local局部函数

在 Bash 函数中,使用 local 关键字定义的变量是局部变量,其作用域仅限于当前函数内部。局部变量在函数外部不可见,也不会影响全局变量或其他函数中的同名变量。以下是 local 变量的作用及其重要性:


1. local 变量的作用

  • 限制作用域local 变量只在定义它的函数内部有效,函数执行完毕后,变量会被自动销毁。
  • 避免冲突:防止函数内的变量与全局变量或其他函数中的变量发生命名冲突。
  • 提高可维护性:明确变量的作用范围,使代码更清晰、更易于理解和维护。

2. 示例对比

示例 1:不使用 local

#!/bin/bash

# 全局变量
var="Global Value"

my_function() {
    var="Local Value"  # 修改了全局变量
    echo "Inside function: $var"
}

echo "Before function: $var"
my_function
echo "After function: $var"

输出:

Before function: Global Value
Inside function: Local Value
After function: Local Value

说明

  • 函数内修改了全局变量 var,导致全局变量的值被意外更改。

示例 2:使用 local

#!/bin/bash

# 全局变量
var="Global Value"

my_function() {
    local var="Local Value"  # 定义局部变量
    echo "Inside function: $var"
}

echo "Before function: $var"
my_function
echo "After function: $var"

输出:

Before function: Global Value
Inside function: Local Value
After function: Global Value

说明

  • 使用 local 定义的变量 var 是局部变量,不会影响全局变量。

3. local 的重要性

  1. 避免副作用

    • 如果不使用 local,函数内修改的变量可能会意外影响全局变量或其他函数中的变量。
    • 使用 local 可以确保变量只在函数内部有效,避免副作用。
  2. 提高代码可读性

    • 使用 local 明确变量的作用范围,使代码更易于理解和维护。
  3. 支持递归函数

    • 在递归函数中,使用 local 可以确保每次递归调用都有独立的变量副本,避免变量冲突。

4. 递归函数示例

#!/bin/bash

# 递归函数计算阶乘
factorial() {
    local n=$1
    if [ $n -eq 0 ]; then
        echo 1
    else
        local result=$(factorial $((n - 1)))
        echo $((n * result))
    fi
}

# 计算 5 的阶乘
echo "Factorial of 5 is: $(factorial 5)"

输出:

Factorial of 5 is: 120

说明

  • 使用 local 确保每次递归调用都有独立的 nresult 变量。

5. 注意事项

  1. local 只能在函数内使用

    • 在函数外部使用 local 会导致语法错误。
  2. local 变量的优先级

    • 如果函数内定义了 local 变量,且存在同名的全局变量,函数内会优先使用 local 变量。
  3. local 变量的初始化

    • local 变量可以在定义时初始化,也可以先定义后赋值。

6. 总结

  • 作用local 用于定义局部变量,限制变量的作用域,避免与全局变量冲突。
  • 优点:提高代码的可读性、可维护性,避免副作用。
  • 适用场景:在函数内定义变量时,优先使用 local,除非明确需要修改全局变量。

通过合理使用 local,可以使 Bash 脚本更加健壮和易于维护。

3.函数传参

在 Bash 脚本中,函数可以通过参数接收外部传递的值。函数参数的传递方式与脚本参数的传递类似,使用位置参数($1, $2, $3 等)来访问传递的参数。以下是 Bash 函数传参的详细说明及示例。


1. 函数参数的基本用法

  • 函数通过 $1, $2, $3 等位置参数访问传递的参数。
  • $0 是脚本名称(不是函数名称)。
  • $# 表示传递给函数的参数个数。
  • $@ 表示所有传递给函数的参数列表。
  • $* 表示所有参数作为一个字符串。

2. 示例

示例 1:传递单个参数

# 定义函数
greet() {
    local name=$1  # $1 是第一个参数
    echo "Hello, $name!"
}

# 调用函数并传递参数
greet "Alice"
greet "Bob"

输出:

Hello, Alice!
Hello, Bob!

示例 2:传递多个参数

# 定义函数
add() {
    local num1=$1  # $1 是第一个参数
    local num2=$2  # $2 是第二个参数
    local sum=$((num1 + num2))
    echo "Sum: $sum"
}

# 调用函数并传递参数
add 3 5
add 10 20

输出:

Sum: 8
Sum: 30

示例 3:使用 $@ 处理所有参数

# 定义函数
print_all_args() {
    echo "All arguments: $@"
    echo "Number of arguments: $#"
}

# 调用函数并传递参数
print_all_args "Alice" "Bob" "Charlie"

输出:

All arguments: Alice Bob Charlie
Number of arguments: 3

示例 4:使用 $* 将所有参数作为一个字符串

# 定义函数
print_as_string() {
    echo "All arguments as a single string: $*"
}

# 调用函数并传递参数
print_as_string "Alice" "Bob" "Charlie"

输出:

All arguments as a single string: Alice Bob Charlie

示例 5:参数个数检查

# 定义函数
check_args() {
    if [ $# -lt 2 ]; then
        echo "Error: At least 2 arguments are required."
        return 1
    fi
    echo "Arguments received: $@"
}

# 调用函数
check_args "Alice"  # 错误:参数不足
check_args "Alice" "Bob" "Charlie"  # 正确

输出:

Error: At least 2 arguments are required.
Arguments received: Alice Bob Charlie

示例 6:默认参数

Bash 不支持默认参数,但可以通过条件判断模拟:

# 定义函数
greet() {
    local name=${1:-"Guest"}  # 如果未传递参数,默认值为 "Guest"
    echo "Hello, $name!"
}

# 调用函数
greet "Alice"
greet  # 不传递参数

输出:

Hello, Alice!
Hello, Guest!

3. 注意事项

  1. 参数顺序$1, $2, $3 等位置参数按顺序对应传递的参数。
  2. 参数个数:使用 $# 检查传递给函数的参数个数。
  3. 参数列表$@$* 都可以表示所有参数,但 $@ 更适合遍历参数。
  4. 默认值:Bash 不支持默认参数,但可以通过 ${1:-默认值} 实现类似功能。
  5. 返回值:函数可以通过 return 返回状态码(0 表示成功,非 0 表示失败),或通过 echo 返回字符串。

4. 总结

  • 使用 $1, $2, $3 等访问函数参数。
  • 使用 $# 获取参数个数,$@ 获取所有参数。
  • 通过 ${1:-默认值} 实现默认参数功能。
  • 函数参数传递方式简单灵活,适合处理各种任务。

通过合理使用函数参数,可以使 Bash 脚本更模块化、可复用性更高。

1. 这里的函数传参,是指单独给函数传递执行参数,和给脚本传入参数是两码事
2. 函数传参是指,函数在执行的时候,可以传入位置参数,这样函数连带参数一起执行。

3.1 函数传参语法(细节)

#!/bin/bash 

function hello(){
    echo "函数开始执行"
    # 注意这里的参数,
    echo "函数体中接收的参数1 :" $1
    echo "函数体中接收的参数2 :" $2
}

# 这里传入的是函数参数
hello laoliu laoba

echo "函数外,可以正常的接收位置参数1:" $1
echo "函数外,可以正常的接收位置参数2:" $2

image-20220626204947157

3.2 计算器函数(注意参数语法)

下面是一个使用 Bash 函数实现的简易计算器示例。这个计算器支持加、减、乘、除四种基本运算,并通过函数封装计算逻辑。


代码实现

#!/bin/bash

# 定义加法函数
add() {
    echo "Result: $(($1 + $2))"
}

# 定义减法函数
subtract() {
    echo "Result: $(($1 - $2))"
}

# 定义乘法函数
multiply() {
    echo "Result: $(($1 * $2))"
}

# 定义除法函数
divide() {
    if [ $2 -eq 0 ]; then
        echo "Error: Division by zero is not allowed."
    else
        echo "Result: $(($1 / $2))"
    fi
}

# 主程序
echo "Simple Bash Calculator"
echo "Enter operation (+, -, *, /):"
read op
echo "Enter first number:"
read num1
echo "Enter second number:"
read num2

# 根据用户输入调用相应的函数
case $op in
    +)
        add $num1 $num2
        ;;
    -)
        subtract $num1 $num2
        ;;
    \*)
        multiply $num1 $num2
        ;;
    /)
        divide $num1 $num2
        ;;
    *)
        echo "Error: Invalid operation. Please use +, -, *, or /."
        ;;
esac

功能说明

  1. 支持的运算

    • 加法(+
    • 减法(-
    • 乘法(*
    • 除法(/
  2. 输入

    • 用户输入运算符(+, -, *, /)。
    • 用户输入两个数字。
  3. 输出

    • 计算结果或错误信息。
  4. 错误处理

    • 如果除数为 0,会提示错误信息。
    • 如果输入了无效的运算符,会提示错误信息。

示例运行

示例 1:加法

Simple Bash Calculator
Enter operation (+, -, *, /):
+
Enter first number:
10
Enter second number:
5
Result: 15

示例 2:除法

Simple Bash Calculator
Enter operation (+, -, *, /):
/
Enter first number:
20
Enter second number:
4
Result: 5

示例 3:除数为 0

Simple Bash Calculator
Enter operation (+, -, *, /):
/
Enter first number:
10
Enter second number:
0
Error: Division by zero is not allowed.

示例 4:无效运算符

Simple Bash Calculator
Enter operation (+, -, *, /):
%
Enter first number:
10
Enter second number:
5
Error: Invalid operation. Please use +, -, *, or /.

代码改进

  1. 支持浮点数运算: Bash 默认只支持整数运算。如果需要支持浮点数,可以使用 bc 工具:

    divide() {
        if [ $2 == 0  ]; then
            echo "Error: Division by zero is not allowed."
        else
            echo "Result: $(echo "scale=2; $1 / $2" | bc)"
        fi
    }
    
  2. 支持更多运算: 可以扩展函数以支持更多运算,如幂运算、取模等。

  3. 用户友好性: 可以添加循环,让用户多次计算而不退出程序。


总结

这个简易计算器通过 Bash 函数实现了基本的四则运算,代码简单易懂,适合初学者学习和扩展。如果需要更复杂的功能(如浮点数运算),可以结合 bc 等工具实现。

bc计算命令

bc 是 Linux/Unix 系统中一个基于命令行的任意精度计算器语言,它常被用在 bash 脚本中来进行数学计算,尤其适合那些需要高精度运算(例如浮点数计算)或比较复杂的数学表达式。下面是对 bc 及其在 bash 中使用的一些详细解释:


1. 基本介绍

  • 全称bc 的全称是 Basic Calculator,但它不仅仅是一个简单的计算器,而是一个完整的编程语言,支持变量、数组、控制流语句等。
  • 精度:与 shell 内置的整数运算不同,bc 支持小数运算,并且可以设置精度(即小数点后几位)。

2. 常见用法

2.1. 交互模式

直接在命令行中输入 bc 可以进入交互模式:

$ bc
1+2
3
scale=3         # 设置小数点精度为3位
1/3
.333
quit            # 输入 quit 退出 bc

2.2. 非交互模式

通常在脚本中,我们可以使用管道或者重定向来传递表达式给 bc

echo "1+2" | bc

result=$(echo "scale=3; 10/3" | bc)
echo $result   # 输出 3.333

3. 常用选项

  • -l:加载数学库,默认将 scale 设置为 20 位小数。这样可以使用更多的数学函数,例如 s(x) (正弦),c(x) (余弦),e(x) (指数) 等。

    echo "scale=10; 4*a(1)" | bc -l   # 计算圆周率 PI,结果大约为 3.1415926535
    
  • -q:安静模式,启动时不显示欢迎信息。
  • -s:将 bc 以“标准模式”启动,允许从文件中读取表达式。

4. 数学函数和语法

在使用 -l 选项时,bc 默认定义了某些数学函数:

  • s(x):计算 x 的正弦(x 的单位为弧度)。
  • c(x):计算 x 的余弦。
  • a(x):计算反正切函数(返回弧度值)。
  • l(x):计算 x 的自然对数。
  • e(x):计算 e 的 x 次幂。

另外,bc 支持基本的数学运算符,如 +, -, *, /, %(取模)等。

例如:

echo "scale=5; s(1)" | bc -l

5. 使用示例

示例1:简单加法

result=$(echo "2+3" | bc)
echo "2 + 3 = $result"    # 输出: 2 + 3 = 5

示例2:浮点数除法

result=$(echo "scale=4; 10/3" | bc)
echo "10 / 3 = $result"   # 输出: 10 / 3 = 3.3333

示例3:结合变量与计算

a=5
b=3
result=$(echo "$a * $b" | bc)
echo "$a * $b = $result"  # 输出: 5 * 3 = 15

6. 脚本中的高级用法

由于 bc 支持条件语句和循环等编程构造,所以可以编写复杂的计算脚本。比如,可以将多个表达式写入一个文件,然后使用 bc 执行:

cat <<EOF > calc.bc
scale=5
x=10
y=3
result = x / y
result
EOF

result=$(bc -l calc.bc)
echo "结果是: $result"

总结

  • bc 是一个强大的命令行计算器工具,适合在 bash 脚本中进行复杂和高精度的数学运算。
  • 使用 -l 选项可以加载数学库,并设置默认的精度为 20 位。
  • 可以通过管道或重定向将数学表达式传递给 bc 进行计算。

通过这些介绍,希望你对 bash 中的 bc 命令有了更深入的了解,可以在实际脚本中灵活应用。

github高星的bash脚本学习参考

https://github.com/dylanaraps/pure-bash-bible

肯定推荐python替代BASH脚本

  • 语法更加优雅
  • 功能更强大
  • 适用面很广,世界第一语言
  • 90%替代BASH脚本的运维工作

4.函数实战练习

4.1 redis管理脚本

下面是一个使用 Bash 函数实现的 Redis 管理脚本。

该脚本封装了常见的 Redis 操作,如启动、停止、重启、查看状态、连接 Redis CLI 等。

通过函数的方式,脚本更加模块化和易于维护。


脚本功能

  1. 启动 Redis:启动 Redis 服务。
  2. 停止 Redis:停止 Redis 服务。
  3. 重启 Redis:重启 Redis 服务。
  4. 查看状态:检查 Redis 服务是否运行。
  5. 连接 Redis CLI:连接到 Redis 命令行界面。
  6. 备份数据:备份 Redis 数据到指定目录。
  7. 恢复数据:从备份文件恢复 Redis 数据。

脚本代码

# apt install redis-server -y
# netstat -tunlp|grep redis
# [root@www.yuchaoit.cn ~]$redis-cli -h 127.0.0.1 -p 6379
127.0.0.1:6379> ping
PONG


#######################################################
#!/bin/bash

# Redis 配置文件路径
REDIS_CONF="/etc/redis/redis.conf"
# Redis 数据备份目录
BACKUP_DIR="/var/backups/redis"

# 启动 Redis
start_redis() {
    echo "Starting Redis..."
    redis-server $REDIS_CONF
    if [ $? -eq 0 ]; then
        echo "Redis started successfully."
    else
        echo "Failed to start Redis."
    fi
}

# 停止 Redis
stop_redis() {
    echo "Stopping Redis..."
    redis-cli shutdown
    if [ $? -eq 0 ]; then
        echo "Redis stopped successfully."
    else
        echo "Failed to stop Redis."
    fi
}

# 重启 Redis
restart_redis() {
    stop_redis
    start_redis
}

# 查看 Redis 状态
status_redis() {
    echo "Checking Redis status..."
    redis-cli ping
    if [ $? -eq 0 ]; then
        echo "Redis is running."
    else
        echo "Redis is not running."
    fi
}

# 连接 Redis CLI
connect_redis() {
    echo "Connecting to Redis CLI..."
    redis-cli
}

# 备份 Redis 数据
backup_redis() {
        # redis持久化文件,2种,rdb模式,aof模式,混合持久化模式
        # 备份定期策略,主动持久化
        #  内存数据,写入到本地文件,永久存储
    local backup_file="$BACKUP_DIR/redis_backup_$(date +%Y-%m-%d_%H:%M:%S).rdb"
    echo "Backing up Redis data to $backup_file..."
    cd /var/lib/redis
    cp $(grep '^dbfilename' $REDIS_CONF | awk '{print $2}') $backup_file
    if [ $? -eq 0 ]; then
        echo "Backup completed successfully."
    else
        echo "Failed to backup Redis data."
    fi
}

# 恢复 Redis 数据
# redis_backup_20250202143506.rdb 存储了2个数据 
restore_redis() {
    local backup_file=$1
    if [ -z "$backup_file" ]; then
        echo "Usage: $0 restore <backup_file>"
        return 1
    fi
    if [ ! -f "$backup_file" ]; then
        echo "Backup file $backup_file does not exist."
        return 1
    fi
    echo "Restoring Redis data from $backup_file..."
    cd /var/lib/redis/
    cp $backup_file $(grep '^dbfilename' $REDIS_CONF | awk '{print $2}')
    if [ $? -eq 0 ]; then
        echo "Restore completed successfully. Please restart Redis to apply changes."
    else
        echo "Failed to restore Redis data."
    fi
}

# 显示帮助信息
show_help() {
    echo "Redis Management Script"
    echo "Usage: $0 {start|stop|restart|status|connect|backup|restore}"
    echo "Commands:"
    echo "  start       - Start Redis"
    echo "  stop        - Stop Redis"
    echo "  restart     - Restart Redis"
    echo "  status      - Check Redis status"
    echo "  connect     - Connect to Redis CLI"
    echo "  backup      - Backup Redis data"
    echo "  restore     - Restore Redis data from backup"
}

# 主程序
if [ $# -eq 0 ]; then
    show_help
    exit 1
fi

case $1 in
    start)
        start_redis
        ;;
    stop)
        stop_redis
        ;;
    restart)
        restart_redis
        ;;
    status)
        status_redis
        ;;
    connect)
        connect_redis
        ;;
    backup)
        backup_redis
        ;;
    restore)
        restore_redis $2
        ;;
    *)
        echo "Error: Invalid command."
        show_help
        exit 1
        ;;
esac

使用方法

  1. 将脚本保存为 redis_manager.sh
  2. 赋予脚本执行权限:
    chmod +x redis_manager.sh
    
  3. 运行脚本并指定操作:
    ./redis_manager.sh <command>
    

示例

启动 Redis

./redis_manager.sh start

停止 Redis

./redis_manager.sh stop

重启 Redis

./redis_manager.sh restart

查看状态

./redis_manager.sh status

连接 Redis CLI

./redis_manager.sh connect

备份数据

./redis_manager.sh backup

恢复数据

./redis_manager.sh restore /var/backups/redis/redis_backup_20231010120000.rdb

注意事项

  1. 配置文件路径:确保 REDIS_CONF 变量指向正确的 Redis 配置文件路径。
  2. 备份目录:确保 BACKUP_DIR 目录存在且有写权限。
  3. 权限问题:某些操作(如启动、停止 Redis)可能需要 root 权限。
  4. 数据恢复:恢复数据后需要重启 Redis 服务以生效。

总结

这个 Redis 管理脚本通过函数封装了常见的 Redis 操作,提供了启动、停止、重启、状态检查、连接 CLI、备份和恢复数据等功能。脚本结构清晰,易于扩展和维护,适合用于日常 Redis 管理任务。

#!/bin/bash

Usage(){
    echo "Usage: bash $0 {start|stop|restart}"
}


start_nginx(){
    echo "nginx启动中"
}

stop_nginx(){

    echo "nginx已关闭"
}

# 接收用户输入指令
case $1 in
start)
    start_nginx ;;
stop)
    stop_nginx ;;
restart)
    stop_nginx
    start_nginx
    ;;
*)
    Usage
esac

image-20220626213037705

4.2 多级菜单版

核心功能就在于条件判断的嵌套

包括while循环、函数、break、continue的总和用法

代码

#!/bin/bash
# author: www.yuchaoit.cn


# 菜单封装函数,便于调用

menu1(){

echo -e "
=============欢迎来到于超老师的linux课程========
兄弟,请你按照如下规则,输入选项
1. Install Nginx
2. Install Mysql
3. Install Redis
4. bye
============================================
"
}


nginx_menu(){
echo -e "
===============请选择对于软件版本==============
1. Install Nginx1.15
2. Install Nginx1.16
3. Install Nginx1.17
4. 返回上一层
=============================================
"
}


mysql_menu(){
    echo "mysql菜单还在开发中....."
}




# 程序打印菜单1
menu1


while true
do
    # 用户选择菜单1
    read -p "您请输入对应的序号:" num1
    # 一级条件判断
    case $num1 in

    1)
        # 进入菜单2
        nginx_menu
        while true
        do
            read -p "请选择对应的nginx版本:" num2
        case $num2 in
            1)
              echo "成功安装nginx 1.15版本!!" 
              ;;
            2)
              echo "成功安装nginx 1.16版本!!" 
              ;;
            3)
              echo "成功安装nginx 1.7版本!!"
              ;;
            4)
              # 清屏,返回上一层
              clear
              menu1
              # 中断二级菜单的循环
              break
              ;;
             *)
              echo "请按规则填写 1 ~ 4 序号...."
         esac
            done
            ;;
    2)
        mysql_menu
        ;;
    3)
        echo "redis菜单于超老师努力开发中。。。"
        ;;
    4)
        echo "bye bye 。"
        exit
        ;;
    *)
        echo "请按规则输入菜单序号1 ~ 4 !!"
        continue
    esac
done

5.运维生产脚本

在运维工作中,编写脚本时使用函数可以提高代码的模块化、可读性和可维护性。以下是一个运维生产脚本的示例,使用函数封装常见的运维任务,如系统监控、日志清理、备份、服务管理等。


脚本功能

  1. 系统监控:检查 CPU、内存、磁盘使用情况。
  2. 日志清理:清理指定目录下的旧日志文件。
  3. 数据备份:备份指定目录到备份目录。
  4. 服务管理:启动、停止、重启指定服务。
  5. 用户管理:添加或删除系统用户。

脚本代码

#!/bin/bash

# 系统监控函数
monitor_system() {
    echo "===== System Monitoring ====="
    echo "CPU Usage: $(top -bn1 | grep load | awk '{printf "%.2f%%\n", $(NF-2)}')"
    echo "Memory Usage: $(free -m | awk '/Mem:/ {printf "%.2f%%\n", $3/$2*100}')"
    echo "Disk Usage: $(df -h / | awk '/\// {print $5}')"
    echo "============================="
}

# 日志清理函数
clean_logs() {
    local log_dir=$1
    local days_to_keep=$2

    if [ -z "$log_dir" ] || [ -z "$days_to_keep" ]; then
        echo "Usage: clean_logs <log_dir> <days_to_keep>"
        return 1
    fi

    if [ ! -d "$log_dir" ]; then
        echo "Error: Directory $log_dir does not exist."
        return 1
    fi

    echo "Cleaning logs in $log_dir older than $days_to_keep days..."
    find "$log_dir" -type f -name "*.log" -mtime +"$days_to_keep" -exec rm -f {} \;
    echo "Log cleanup completed."
}

# 数据备份函数
backup_data() {
    local source_dir=$1
    local backup_dir=$2

    if [ -z "$source_dir" ] || [ -z "$backup_dir" ]; then
        echo "Usage: backup_data <source_dir> <backup_dir>"
        return 1
    fi

    if [ ! -d "$source_dir" ]; then
        echo "Error: Source directory $source_dir does not exist."
        return 1
    fi

    mkdir -p "$backup_dir"
    local backup_file="$backup_dir/backup_$(date +%Y%m%d%H%M%S).tar.gz"

    echo "Backing up $source_dir to $backup_file..."
    tar -czf "$backup_file" -C "$(dirname "$source_dir")" "$(basename "$source_dir")"
    echo "Backup completed: $backup_file"
}

# 服务管理函数
manage_service() {
    local service_name=$1
    local action=$2

    if [ -z "$service_name" ] || [ -z "$action" ]; then
        echo "Usage: manage_service <service_name> {start|stop|restart|status}"
        return 1
    fi

    case $action in
        start)
            systemctl start "$service_name"
            ;;
        stop)
            systemctl stop "$service_name"
            ;;
        restart)
            systemctl restart "$service_name"
            ;;
        status)
            systemctl status "$service_name"
            ;;
        *)
            echo "Error: Invalid action. Use start, stop, restart, or status."
            return 1
            ;;
    esac
}

# 用户管理函数
manage_user() {
    local username=$1
    local action=$2

    if [ -z "$username" ] || [ -z "$action" ]; then
        echo "Usage: manage_user <username> {add|delete}"
        return 1
    fi

    case $action in
        add)
            useradd "$username"
            echo "User $username added."
            ;;
        delete)
            userdel -r "$username"
            echo "User $username deleted."
            ;;
        *)
            echo "Error: Invalid action. Use add or delete."
            return 1
            ;;
    esac
}

# 显示帮助信息
show_help() {
    echo "===== Ops Management Script ====="
    echo "Usage: $0 {monitor|clean_logs|backup|manage_service|manage_user}"
    echo "Commands:"
    echo "  monitor                     - Monitor system resources"
    echo "  clean_logs <dir> <days>     - Clean logs older than <days> in <dir>"
    echo "  backup <source> <target>    - Backup <source> directory to <target>"
    echo "  manage_service <name> <action> - Manage service (start/stop/restart/status)"
    echo "  manage_user <name> <action> - Manage user (add/delete)"
    echo "================================"
}

# 主程序
if [ $# -eq 0 ]; then
    show_help
    exit 1
fi

case $1 in
    monitor)
        monitor_system
        ;;
    clean_logs)
        clean_logs "$2" "$3"
        ;;
    backup)
        backup_data "$2" "$3"
        ;;
    manage_service)
        manage_service "$2" "$3"
        ;;
    manage_user)
        manage_user "$2" "$3"
        ;;
    *)
        echo "Error: Invalid command."
        show_help
        exit 1
        ;;
esac

使用方法

  1. 将脚本保存为 ops_manager.sh
  2. 赋予脚本执行权限:
    chmod +x ops_manager.sh
    
  3. 运行脚本并指定操作:
    ./ops_manager.sh <command> [arguments]
    

示例

系统监控

./ops_manager.sh monitor

日志清理

清理 /var/log 目录下超过 7 天的日志文件:

./ops_manager.sh clean_logs /var/log 7

数据备份

备份 /data 目录到 /backups

./ops_manager.sh backup /data /backups

服务管理

重启 nginx 服务:

./ops_manager.sh manage_service nginx restart

用户管理

添加用户 testuser

./ops_manager.sh manage_user testuser add

总结

这个脚本通过函数封装了常见的运维任务,包括系统监控、日志清理、数据备份、服务管理和用户管理。使用函数使脚本模块化、易于扩展和维护,适合用于生产环境中的日常运维工作。

Copyright © www.yuchaoit.cn 2025 all right reserved,powered by Gitbook作者:于超 2025-02-02 15:43:13

results matching ""

    No results matching ""