美文网首页
SHELL 规范

SHELL 规范

作者: 大帅的大帅 | 来源:发表于2020-05-19 12:12 被阅读0次

SHELL 规范

在总结过程中,发现更牛逼的大神总结。

开头

开头是设置错误、异常,管道退出时的命令内容
set -e 等同于set -o errexit

配置脚本错误规范

# Exit on error. Append "|| true" if you expect an error.
set -o errexit
# Exit on error inside any functions or subshells.
set -o errtrace
# Do not allow use of undefined vars. Use ${VAR:-} to use an undefined VAR
set -o nounset
# Catch the error in case mysqldump fails (but gzip succeeds) in `mysqldump |gzip`
set -o pipefail
# Turn on traces, useful while debugging but commented out by default
# set -o xtrace

路径调整

  • 切换到脚本的当前路径

    cd $(dirname "${BASH_SOURCE}")

    获取当前路径的正确写法

    pushd `dirname $0` > /dev/null
    SCRIPT_PATH=`pwd -P`
    popd > /dev/null
    
  • 切换到脚本所在的上级目录

    cd $(dirname "${BASH_SOURCE}")/..

  • 切换到脚本所在目录的上级目录并把绝对路径给参数_PATH

    _PATH=$(cd $(dirname "${BASH_SOURCE}")/.. && pwd)

  • 目录绝对路径和脚本所在目录的绝对路径
_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" #看见$符号要有引号
_FILEPATH="${_DIR}/$(basename "${BASH_SOURCE[0]}")" #basename script.sh 获取脚本本身
_SCRIPTNO_SUFFIX="$(basename "${_FILEPATH}" .sh)"    #拆分
  • 相对路径和绝对路径取其一
_ABSOLUTE_PATH=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/
_UABPATH="${_ABSOLUTE_PATH:-.}/conf/fun.conf" 
  • 经典样式
set -e
pushd `dirname $0` > /dev/null
SCRIPTPATH=`pwd -P`
popd > /dev/null
SCRIPTFILE=`basename $0`

日志及显示格式

模块版本

日志格式是为了显示日志来写的,可以直接使用。
可以把本地代码写成文件的方式logconfig.sh,通过
source ./logconfig.sh进行加载。
使用的方式: 直接调用函数 info "content string"

##############################################################################
# Define the environment variables (and their defaults) that this script depends on
LOG_LEVEL=7 #兼容日志的最高级别,级别7以下的都会显示。
LOG_LEVEL="${LOG_LEVEL:-6}" # 7 = debug -> 0 = emergency 默认
NO_COLOR="${NO_COLOR:-}"    # true = disable color. otherwise autodetected


### Functions

function __b3bp_log () {

  ## 这里读取的字段来自于 以下函数里的 第一个字段,例如:
  ## [[ "${LOG_LEVEL:-0}" -ge 1 ]] && __b3bp_log alert "${@}"; true; 
  ## 获取的是  “alert” 的字段。
  local log_level="${1}" 

  shift

  # shellcheck disable=SC2034
  local color_debug="\x1b[35m"
  # shellcheck disable=SC2034
  local color_info="\x1b[32m"
  # shellcheck disable=SC2034
  local color_notice="\x1b[34m"
  # shellcheck disable=SC2034
  local color_warning="\x1b[33m"
  # shellcheck disable=SC2034
  local color_error="\x1b[31m"
  # shellcheck disable=SC2034
  local color_critical="\x1b[1;31m"
  # shellcheck disable=SC2034
  local color_alert="\x1b[1;33;41m"
  # shellcheck disable=SC2034
  local color_emergency="\x1b[1;4;5;33;41m"“”

  local colorvar="color_${log_level}" ##拼接上面是字符例如“color_critical”


  local color="${!colorvar:-${color_error}}" ##没有日志级别,默认是error级别。
  local color_reset="\x1b[0m"

  if [[ "${NO_COLOR:-}" = "true" ]] || ( [[ "${TERM:-}" != "xterm"* ]] && [[ "${TERM:-}" != "screen"* ]] ) || [[ ! -t 2 ]]; then
    if [[ "${NO_COLOR:-}" != "false" ]]; then
      # Don't use colors on pipes or non-recognized terminals
      color=""; color_reset=""
    fi
  fi

  # all remaining arguments are to be printed
  local log_line=""

  while IFS=$'\n' read -r log_line; do
    echo -e "$(date -u +"%Y-%m-%d %H:%M:%S UTC") ${color}$(printf "[%9s]" "${log_level}")${color_reset} ${log_line}" 1>&2
  done <<< "${@:-}"
}
##支持的函数包含这么几种
function emergency () {                                __b3bp_log emergency "${@}"; exit 1; }
function alert ()     { [[ "${LOG_LEVEL:-0}" -ge 1 ]] && __b3bp_log alert "${@}"; true; }
function critical ()  { [[ "${LOG_LEVEL:-0}" -ge 2 ]] && __b3bp_log critical "${@}"; true; }
function error ()     { [[ "${LOG_LEVEL:-0}" -ge 3 ]] && __b3bp_log error "${@}"; true; }
function warning ()   { [[ "${LOG_LEVEL:-0}" -ge 4 ]] && __b3bp_log warning "${@}"; true; }
function notice ()    { [[ "${LOG_LEVEL:-0}" -ge 5 ]] && __b3bp_log notice "${@}"; true; }
function info ()      { [[ "${LOG_LEVEL:-0}" -ge 6 ]] && __b3bp_log info "${@}"; true; }
function debug ()     { [[ "${LOG_LEVEL:-0}" -ge 7 ]] && __b3bp_log debug "${@}"; true; }

简单版本

定义几个脚本颜色块,然后直接引用就好了。

COLOR_NONE='\033[0m'
COLOR_INFO='\033[0;36m'
COLOR_ERROR='\033[1;31m'
##直接覆盖加引用就可以了
echo -e "${COLOR_ERROR}Invalid option. Please input a number.${COLOR_NONE}"

交互式Parse 读取命令行参数里内容 method1

#这里是把所有的所有的 内容读取到文件里,如果没读成功,那么就显示'',如果读取成功,就显示‘true’,这样给用户显示 参数用法。
#[[ "${__usage+x}" ]]  高级用法 ${__usage} 是这个读取到这个内容的这个文件的临时函数,f [[ -n x  ]]
# exits non-zero when EOF encountered

[[ "${__usage+x}" ]] || read -r -d '' __usage <<-'EOF' || true 

  -f --file  [arg] Filename to process. Required.
  -t --temp  [arg] Location of tempfile. Default="/tmp/bar"
  -v               Enable verbose mode, print script as it is executed
  -d --debug       Enables debug mode
  -h --help        This page
  -n --no-color    Disable color output
  -1 --one         Do just one thing
EOF


## 可以通过 调用。
echo ${__usage}


###也可以根据
while read -r _temp_line; do

 echo "print line ${_temp_line}" #逐行打印


if [[ "${_temp_line}" =~ ^- ]]; then  #读取每行的,正则匹配,开头是“-”,
# fetch single character version of option string
_temp_line="${_temp_line%% *}" #模式匹配,匹配以空格为分解符的第一个所有的。
_temp_line="${_temp_line:1}" #截取模式,截取第一个字符后的其它字符,把“-” 去掉。

__b3bp_tmp_long_opt=""
if [[ "${_temp_line}"  = "*__*" ]];then
    __b3bp_tmp_long_opt=${_temp_line#*--}#拿出匹配的字符"debug       Enables debug mode"
   __b3bp_tmp_long_opt=${_temp_line%% *} #拿出 “debug”
fi


done <<< "${__usage:-}"

#done <<< ${parse_info} #这样表示整块读进去

EOF用法二

新建文件,并写入整块内容。可以执行整块的文本操作


cat <<EOF | sudo tee /root/influxdb.repo
#写入如下内容
[influxdb]
name = InfluxDB Repository - RHEL \$releasever
baseurl = https://repos.influxdata.com/rhel/\$releasever/\$basearch/stable
enabled = 1
gpgcheck = 1
gpgkey = https://repos.influxdata.com/influxdb.key
EOF

交互式Parse 读取命令行参数里内容 method2 简单版本

这个版本不能提取参数后定义的变量
方法一:

function usage() {
   echo -e "Usage:"
   echo -e "\t -a | --all         :  run all steps"
   echo -e "\t -c | --config      :  choose a cluster.cfg file."
   echo -e "\t -h | --help        :  this help usage."
}


while [ "${1}" != ""  ]; do
  case $1 in 
     -a | -all )
        usage
        ;;
     -h | -help )
        usage
        break
        ;;
     -n | -number)
        echo `cd $(dirname "${BASH_SOURCE}")/ && PWD`
        ;;
     -f | -file )
        exit 1
        ;;
  esac
  shift
done

方法二:



function usage(){
   echo -e "Usage:"
   echo -e "\t -a | --all         :  run all steps"
   echo -e "\t -c | --config      :  choose a cluster.cfg file."
   echo -e "\t -h | --help        :  this help usage."
}

while getopts  'h::v:n:' flag; do
    case "${flag}" in
        h) showUsage; exit ;;
        n) name=${OPTARG} ;;
        v) IMAGE_VERSION="${OPTARG}";;
        n) usage;;
    esac
done


获取脚本里的参数

#!/usr/bin/env bash

usage() { echo "$0 usage:" && grep " .)\ #" $0; exit 0; } # usage 函数。非常有意思,非常精巧。 $0 是函数名称,然后 grep 这个脚本里的 匹配“.)\ #” 行,正好为 一下case 里,写#的关键行。

[ $# -eq 0 ] && usage #没有任何参数时执行。


#h  -检查 -h 不带参数
#h: -检查 -h 带参数,不给参数就报错
#abc -分别检查 -a -b -c 不给参数就报错
#:abc 分别检查 -a -b -c, 不给参数,就显示静默错误。
#$OPTARG 用来获取参数里的字段 例如 sh test.sh -p teststring -s 50; 其中 teststring 和 50 都会被获取

while getopts ":hs:p:" arg; do #获取关键字 
  case $arg in
    p) # Specify p value.
      echo "p is ${OPTARG}"
      ;;
    s) # Specify strength, either 45 or 90.
      strength=${OPTARG}
      [[ "${strength}" -eq 45 -o "${strength}" -eq 90 ]] \
        && echo "Strength is $strength." \
        || echo "Strength needs to be either 45 or 90, $strength found instead."
      ;;
    h ) # Display help.
      usage
      exit 0
      ;;
    \?) # nothing to match 
      echo "Invalid option: -${OPTARG}" >&2
      exit 0
      ;;
    :) #匹配静默错误
      echo "Option ${OPTARG} requires an argument." >&2
      exit 0
      ;;
  esac
done

shell 数组的表示

shell 数组非常方面,中间是空格就可以了

Fruits=('Apple' 'Banana' 'Orange')
echo ${Fruits[0]}           # 第一个元素
echo ${Fruits[@]}           # 所有元素的,空格分割 All elements, space-separated
echo ${#Fruits[@]}          # 元素的个数 Number of elements
echo ${#Fruits}             # 第一个元素字符串长度 String length of the 1st element
echo ${#Fruits[3]}          # 第N个元素字符串长度 String length of the Nth element
echo ${Fruits[@]:3:2}       # 第三个开始,长度为2 的数组 Range (from position 3, length 2)

数组的操作方法:

Fruits=("${Fruits[@]}" "Watermelon")    # 增加一个元素 Push
Fruits+=('Watermelon')                  # 第二中增加方法 Also Push
Fruits=( ${Fruits[@]/Ap*/} )            # 移除一个匹配的 Remove by regex match
unset Fruits[2]                         # 移除指定元素 Remove one item
Fruits=("${Fruits[@]}")                 # 赋值一份 Duplicate
Fruits=("${Fruits[@]}" "${Veggies[@]}") # 连接一份 Concatenate
lines=(`cat "logfile"`)                 # Read from files's

shell SELECT 交互式,非常有技巧

获取用户交互的用read

echo "Enter your name"
read name
if [[ ! ${name} -z  ]];then
   echo "---"
fi

技巧大法例子

options=( $(find ${SCRIPTPATH} -maxdepth 1 -name "filename" -print0 | xargs -0) )
echo ${options[@] } # 获取匹配的所以是字段;当然options 可以是数组,定义 options=("agrument1" "agrusment2")

PS3="Please select a filename file: " ##专业写法,必须为PS3才有提示。默认为(#?)

re='^[0-9]+$' #匹配正则表达式,字母开头

select opt in "${options[@]}" "Quit" ; do  #以匹配的每个文件,自动排序生成序号,并且新增的“Quit” 字符串也自动的添加序号。
    if ! [[ $REPLY =~ $re ]] ; then    # REPLY默认读取 read 里的参数。 
        echo -e "${COLOR_ERROR}Invalid option. Please input a number.${COLOR_NONE}"
    elif (( REPLY == 1 + ${#options[@]} )) ; then #输入序号大于参数个数,${#options[@]}  值得是options 里参数的个数
        exit
    elif (( REPLY > 0 && REPLY <= ${#options[@]} )) ; then #输入的小于0
        break
    else
        echo -e "${COLOR_ERROR}Invalid option. Try another one.${COLOR_NONE}" #其它情况
    fi
done 
  echo -e -n "Are you sure the \033[4;31m${opt} is the right config file? (y/n) " #获取用户选择的结果
  read -n 1 -r #读取用户结果
  echo
  if [[ ! ${REPLY} =~ ^[Yy]$ ]] ; then  #匹配
      # handle exits from shell or function but don't exit interactive shell
      echo "Are you enter ${REPLY}?"
  fi


##### 例子二
```shell
set -e
PS3=" Please select login host:"
_INPUT='^[0-9]+$'
#select opt in "${Host[@]}" "quit" ;do
select opt in  "node1" "node2" "node3" "admin" "quit" ;do
    if ! [[ "$REPLY" =~ ${_INPUT}  ]];then
        echo "Invalid option, please in put number "
    elif (( REPLY == 5 )) ; then #输入序号大于参数个数
        echo "Match 'quit', bye~"
        exit 
    elif (( REPLY > 0 && REPLY <= 5  )) ;then #匹配规则
        echo "OK,Wait for a moment~ "
        break
    else 
        echo "Invalid option. Try another one."
    fi
done

function MatchIP() {
   if [[ ! -z "${HOSTNAME}"  ]];then
        #echo ${HOSTNAME} "==========="   
        if [[ ${HOSTNAME}  =~ "NODE1" ]];then
            IP="172.16.97.218"
        fi
        ssh root@${IP}
    else
        echo "HostName not found!"
   fi

}

case ${opt} in 
        node1 | 1 )
            HOSTNAME="NODE1"
            MatchIP #调用函数
        ;;
esac

技巧

判断值, 直接用[[ "${_var:-} "]] 判断值有否,如果有值则相当于 -n ,不为0 返回值为false; 参数无值,并替换为空值,则返回 true,等式成立,继续执行continue 。

${__variable:-} 如果变量存在且不为空则返回值,否则返回空;并判断 [[ -n ""]]是否为空。

[[ "${__variable:-}" ]] || continue

__variable=""
if [[  -n ${__variable} ]];then
    continue
fi

case shout

${variable:-"argus"}  
#这种不能单独使用,必须在表达式里或者传给其它变量值。
#因为是return 返回的,直接使用argus必须可执行才行。
#i.
_temp_name=${variable:-"argus"}
echo "${_temp_name}"
#ii.
[[ "${variable:-"argus"}" ]] && exit 

find 的技巧
-print0 被NUL终结的字符串. ${argus[@]} 取出所有的值

_all_file="($(find ./ -maxdepth 1 -name "filename" -print0  | xargs -0))"
echo  "${_all_file[@]}"

相关文章

  • Llinux03--shell脚本

    [TOC] 八、shell语言 1、简单的shell 代码规范: 文件命名规范:文件名.sh 使用流程: 创建sh...

  • Shell编程-14-Shell脚本规范及调试

    Shell脚本规范     良好的代码规范不仅方便阅读,也利于维护和提升开发效率。因此建议大家在编写Shell脚本...

  • SHELL 规范

    SHELL 规范 在总结过程中,发现更更牛逼的大神总结。 开头 开头是设置错误、异常,管道退出时的命令内容set ...

  • shell编程(一)

    shell语法规范 一般shell代码前面放置信息如下: #! /usr/bin/bsh #定义shell脚本运行...

  • Shell 脚本入门: 编写格式和执行方式

    本文要点 Shell 脚本的编写规范 执行Shell 脚本文件的 3 种方式 与 区别 1. Shell 脚本的编...

  • Shell编程规范

    原文地址Shell是用户与Linux或Unix内核通信的工具,shell编程指的并不是编写这个工具,而是指利用现有...

  • shell开发规范

    一、 命名规范 1、 版本和运行参数 脚本开始之前以注释形式说明版本号;(推荐) 如果调用其他工具,还需说明工具的...

  • Ansible教程 第五章 Playbook使用详解

    playbook书写规范 shell脚本转换成yaml文本总结规范 代码清单4-1,详细解剖代码 playbook...

  • Shell规范_语法.md

    对于一个shell脚本,上述三行是常见规范.set -e 表示异常返回退出shell.set -o pipefai...

  • BASH Shell编程规范

    [转自]http://www.jb51.net/article/52586.htm做了小幅修改 本文列举BASH ...

网友评论

      本文标题:SHELL 规范

      本文链接:https://www.haomeiwen.com/subject/yczvohtx.html