Shell脚本

Shell 脚本

#!/bin/bash  # #!脚本声明

# file   # 脚本说明

ls  # 功能
pwd
reboot

逻辑判断

 [ $USER = root ] && echo "you are admin" || echo "you are user"
# [   ] 逻辑判断,前后均有空格
# && 为 与 运算
# || 为 或 运算
[ `free -m | grep Mem: | awk '{print $4}'` -lt 1024 && echo "buzu" || echo "chongzu"
# 判断空闲内存是否充足

文件测试所用的参数

运算符 作用
-d 测试文件是否为目录类型
-e 测试文件是否存在
-f 判断是否为一般文件
-r 测试当前用户是否有权限读取
-w 测试当前用户是否有权限写入
-x 测试当前用户是否有权限执行

可用的整数比较运算符

操作符 作用
-eq 是否等于
-ne 是否不等于
-gt 是否大于
-lt 是否小于
-le 是否等于或小于
-ge 是否大于或等于

常见的字符串比较运算符

操作符 作用
= 比较字符串内容是否相同
!= 比较字符串内容是否不同
-z 判断字符串内容是否为空

ping

获取ip地址

ping -c 1 linuxcool.com | grep from | cut -d " " -f 4
# -c 几次
# -i 间隔多少秒

vim ping.sh

#!/bin/bash
ping -c 3 -i 0.2 -W 3 $1 >> /dev/null
if [ $? -eq 0 ] ;  then
echo "$1 is On-line"
else
echo "$1 is Off-line"
fi

bash ping.sh 163.com

判断成绩

vim test.sh

#!/bin/bash

read -p "Please input your grade:" GRADE

if [ $GRADE -ge 85 ] && [ $GRADE -le 100 ]; then
    echo "$GRADE is Great!"
elif [ $GRADE -ge 70 ] && [ $GRADE -lt 85 ]; then
    echo "$GRADE is Pass."
else
    echo "$GRADE is Fail."
fi

bash test.sh

猜大小

#!/bin/sh
PRICE=$( expr $RANDOM % 1000 )
TIMES=0
while true
do
read -p "Guess a INT number:" INT
let TIMES++
if [ $INT -eq $PRICE ]; then
        echo "You are right:$INT"
        echo "Guess $TIMES times"
        exit 0
elif [ $INT -gt $PRICE ]; then
        echo "High"
else
        echo "Low"
fi
done

Case

#!/bin/bash
read -p "Enter:" KEY
case $KEY in
[a-z]|[A-Z])
echo "Zimu"
;;
[0-9])
echo "Number"
;;
*)
echo "Error"
esac

一次性任务

at 20:30
systemctl restart httpd
#按Ctrl+D结束编写任务
at -l #查看任务列表
atrim 任务序号 #删除任务

长期任务

crontab -l # 查看任务列表
crontab -e #创建任务
# 分 时 日 月 星期 命令
25 3 * * 1,3,5 /usr/bin/tar -czvf backup.tar.gz /home/wwwroot
# 每周1、3、5,3:25,备份
0 */2 * * 1-5 /usr/bin/rm -rf /tmp/*
# 周一到周五,每隔两个小时,清空/tmp

kvm-arm64

#!/bin/bash

ISOURL=$1
rm -rf /home/sandy/arm64.qcow2
qemu-img create -f qcow2 /home/sandy/arm64.qcow2 20G

virsh undefine arm64 --nvram

install_from_iso_vga_UOS() {
virt-install -n arm64 --memory 2048 --arch aarch64 --vcpus 2 \
     --disk /home/sandy/arm64.qcow2,device=disk,bus=virtio \
     --os-type=generic \
     --boot uefi \
     --video vga \
     --noautoconsole \
     --noreboot \
     --cdrom $ISOURL
}
install_from_iso_vga_UOS

写入多行文本到文件

cat >/etc/product-info <<EOF
## cat >>为追加
UOS device Enterprise V20 $(uname -m) $(date +%Y%m%d)
EOF

脚本运行保存为日志

#!/bin/bash
logfile=/var/log/logfile.log
exec >$logfile 2>&1

重定向脚本输出到日志文件并同时显示

logfile=log.log
exec > >(tee $logfile) 2>&1

选择执行

#!/bin/bash
echo ""
echo " 1: ssh 访问 自动构建服务器  "
echo " 2: ssh 访问 UOS ISO存储服务器   "
echo " 3: 浏览器打开 UOS ISO存储服务器   "
echo " 4: 上传UOS ISO镜像到存储服务器"
echo " 5: 打开 虚拟机管理器virt-manager"
echo " 6: ssh 访问 my vps"

echo ""
read -p "Please input the choice:"  idx
##定义函数
sync_upload_iso(){
echo "input the iso url: "
read ISOURL
rsync -avz --progress $ISOURL -e ssh root@ip:/data/iso/uos/device/snapshots/
if [[ $ISOURL =~ "iso" ]]; then
#    sudo apt install rsync -y
    rsync -avz --progress $ISOURL -e ssh root@ip:/data/iso/uos/device/snapshots/
fi
}
#if [[ -z "$idx"]];then
#  echo "no choice,exit"
if [[ '1' = "$idx" ]];then
  eval "ssh root@ip -t 'cd /data/soft/isobuilder/; bash --login'"
elif [[ '2' = "$idx" ]];then
  eval "ssh root@ip -t 'cd /data/iso/uos/; bash --login'"
elif [[ '3' = "$idx" ]];then
  eval "xdg-open 'http://ip'"
elif [[ '4' = "$idx" ]];then
  eval "sync_upload_iso"
elif [[ '5' = "$idx" ]];then
  eval "virt-manager"
elif [[ '6' = "$idx" ]];then
  eval "ssh root@ip -p 22"
else
  echo "no choice,exit!"
fi
echo ""

info and error

#!/bin/bash

RED_COL="\033[41;1m"
RESET_COL="\033[0m"

function info() {
    echo "INFO: $1"
}

function error() {
    echo -e "${RED_COL}ERR:${RESET_COL} $1" >&2
}

function halt() {
    error "$1"
    exit 1
}

info "This message goes to STDOUT"
error "This message goes to STDERR"
halt "This message goes to STDERR, and script will halt"
info "This won't be executed"

变量替换

msg="I like scripting."
new_msg=${msg/scripting/Bash}
echo $new_msg
变量配置方式 说明
${变量#关键词} ${变量##关键词} 若变量内容从头开始的数据符合『关键词』,则将符合的最短数据删除 若变量内容从头开始的数据符合『关键词』,则将符合的最长数据删除
${变量%关键词} ${变量 %% 关键词} 若变量内容从尾向前的数据符合『关键词』,则将符合的最短数据删除 若变量内容从尾向前的数据符合『关键词』,则将符合的最长数据删除
${变量/旧字符串/新字符串} ${变量//旧字符串/新字符串} 若变量内容符合『旧字符串』则『第一个旧字符串会被新字符串取代』 若变量内容符合『旧字符串』则『全部的旧字符串会被新字符串取代』
$ image="library/nginx:1.19"

# 比如要获取镜像的 tag 常用的是 echo 然后 awk/cut 的方式

$ echo ${image} | awk -F ':' '{print $2}' 方式

# 可以直接使用 bash 内置的变量替换功能,截取特定字符串
$ image_name=${image%%:*}
$ image_tag=${image##*:}
$ image_repo=${image%%/*}

脚本中统计函数耗时

reset_global_timer() {
    export SEC0=$(date --utc +%s)
}

reset_function_timer(){
    export SEC1=$(date --utc +%s)
}

running_time()
{
    SEC2=$(date --utc +%s); DIFFSEC=$((${SEC2} - ${SEC1})); printf "\nSection Time: $(date +%H:%M:%S -ud @${DIFFSEC})\n"
    SEC2=$(date --utc +%s); DIFFSEC=$((${SEC2} - ${SEC0})); printf "Elapsed Time: $(date +%H:%M:%S -ud @${DIFFSEC})\n\n"
}

reset_global_timer
reset_function_timer
running_time

正确传递数组到函数中

function update() {
    declare -a apps_version=("${!1}")
    echo "${apps_version[@]}"
}

APPS_VERSION=("aaa" "bbb" "ccc")
update APPS_VERSION[@]

要特别注意的是,使用不当就造成只将数组的第一个数组到函数中

上面是正确的使用方法

bash中检测函数foo是否存在

fn_exists() {
  # appended double quote is an ugly trick to make sure we do get a string -- if $1 is not a known command, type does not output anything
  [ `type -t $1`"" == 'function' ]
}
if ! fn_exists $FN; then
    echo "Hey, $FN does not exist ! Duh."
    exit 2
fi

或者

fn_exists() { declare -F "$1" > /dev/null; }
fn_exists foo && echo yes || echo no

trap 脚本错误及退出

trap '>&2 echo Command failed: $(tail -n+$LINENO $(readlink -f $0) | head -n1)' ERR
trap 'rc=$?; echo $rc SIGINT; exit $rc' INT
trap 'rc=$?; echo $rc EXIT; exit $rc' EXIT

严格脚本语法

#bash strict mode
set -euo pipefail
#debug mode
set -x