SHELL脚本编程-函数
SHELL脚本编程-函数
作者:尹正杰
版权声明:原创作品,谢绝转载!否则将追究法律责任。
一.函数介绍
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分
函数和shell程序比较相似,区别在于
Shell程序在子Shell中运行
而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改
二.定义函数
[root@node101.yinzhengjie.org.cn ~]# help function #函数由两部分组成:函数名和函数体 function: function name { COMMANDS ; } or name () { COMMANDS ; } Define shell function. Create a shell function named NAME. When invoked as a simple command, NAME runs COMMANDs in the calling shell's context. When NAME is invoked, the arguments are passed to the function as $1...$n, and the function's name is in $FUNCNAME. Exit Status: Returns success unless NAME is readonly. [root@node101.yinzhengjie.org.cn ~]#
1>.语法格式一
f_name (){
...函数体...
}
2>.语法格式二
function f_name { ...函数体... }
3>.语法格式三
function f_name () { ...函数体... }
4>.查看操作系统中定义的函数
[root@node101.yinzhengjie.org.cn ~/shell]# wc -l /etc/init.d/functions 712 /etc/init.d/functions [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# cat /etc/init.d/functions #查看操作系统中定义的函数 # -*-Shell-script-*- # # functions This file contains functions to be used by most or all # shell scripts in the /etc/init.d directory. # TEXTDOMAIN=initscripts # Make sure umask is sane umask 022 # Set up a default search path. PATH="/sbin:/usr/sbin:/bin:/usr/bin" export PATH if [ $PPID -ne 1 -a -z "$SYSTEMCTL_SKIP_REDIRECT" ] && \ [ -d /run/systemd/system ] ; then case "$0" in /etc/init.d/*|/etc/rc.d/init.d/*) _use_systemctl=1 ;; esac fi systemctl_redirect () { local s local prog=${1##*/} local command=$2 local options="" case "$command" in start) s=$"Starting $prog (via systemctl): " ;; stop) s=$"Stopping $prog (via systemctl): " ;; reload|try-reload) s=$"Reloading $prog configuration (via systemctl): " ;; restart|try-restart|condrestart) s=$"Restarting $prog (via systemctl): " ;; esac if [ -n "$SYSTEMCTL_IGNORE_DEPENDENCIES" ] ; then options="--ignore-dependencies" fi if ! systemctl show "$prog.service" > /dev/null 2>&1 || \ systemctl show -p LoadState "$prog.service" | grep -q 'not-found' ; then action $"Reloading systemd: " /bin/systemctl daemon-reload fi action "$s" /bin/systemctl $options $command "$prog.service" } # Get a sane screen width [ -z "${COLUMNS:-}" ] && COLUMNS=80 if [ -z "${CONSOLETYPE:-}" ]; then if [ -c "/dev/stderr" -a -r "/dev/stderr" ]; then CONSOLETYPE="$(/sbin/consoletype < /dev/stderr 2>/dev/null)" else CONSOLETYPE="serial" fi fi if [ -z "${NOLOCALE:-}" ] && [ -z "${LANGSH_SOURCED:-}" ] && \ [ -f /etc/sysconfig/i18n -o -f /etc/locale.conf ] ; then . /etc/profile.d/lang.sh 2>/dev/null # avoid propagating LANGSH_SOURCED any further unset LANGSH_SOURCED fi # Read in our configuration if [ -z "${BOOTUP:-}" ]; then if [ -f /etc/sysconfig/init ]; then . /etc/sysconfig/init else # This all seem confusing? Look in /etc/sysconfig/init, # or in /usr/share/doc/initscripts-*/sysconfig.txt BOOTUP=color RES_COL=60 MOVE_TO_COL="echo -en \\033[${RES_COL}G" SETCOLOR_SUCCESS="echo -en \\033[1;32m" SETCOLOR_FAILURE="echo -en \\033[1;31m" SETCOLOR_WARNING="echo -en \\033[1;33m" SETCOLOR_NORMAL="echo -en \\033[0;39m" LOGLEVEL=1 fi if [ "$CONSOLETYPE" = "serial" ]; then BOOTUP=serial MOVE_TO_COL= SETCOLOR_SUCCESS= SETCOLOR_FAILURE= SETCOLOR_WARNING= SETCOLOR_NORMAL= fi fi # Check if any of $pid (could be plural) are running checkpid() { local i for i in $* ; do [ -d "/proc/$i" ] && return 0 done return 1 } __kill_pids_term_kill_checkpids() { local base_stime=$1 shift 1 local pid= local pids=$* local remaining= local stat= local stime= for pid in $pids ; do [ ! -e "/proc/$pid" ] && continue read -r line < "/proc/$pid/stat" 2> /dev/null stat=($line) stime=${stat[21]} [ -n "$stime" ] && [ "$base_stime" -lt "$stime" ] && continue remaining+="$pid " done echo "$remaining" [ -n "$remaining" ] && return 1 return 0 } __kill_pids_term_kill() { local try=0 local delay=3; local pid= local stat= local base_stime= # We can't initialize stat & base_stime on the same line where 'local' # keyword is, otherwise the sourcing of this file will fail for ksh... stat=($(< /proc/self/stat)) base_stime=${stat[21]} if [ "$1" = "-d" ]; then delay=$2 shift 2 fi local kill_list=$* kill_list=$(__kill_pids_term_kill_checkpids $base_stime $kill_list) [ -z "$kill_list" ] && return 0 kill -TERM $kill_list >/dev/null 2>&1 sleep 0.1 kill_list=$(__kill_pids_term_kill_checkpids $base_stime $kill_list) if [ -n "$kill_list" ] ; then while [ $try -lt $delay ] ; do sleep 1 kill_list=$(__kill_pids_term_kill_checkpids $base_stime $kill_list) [ -z "$kill_list" ] && break let try+=1 done if [ -n "$kill_list" ] ; then kill -KILL $kill_list >/dev/null 2>&1 sleep 0.1 kill_list=$(__kill_pids_term_kill_checkpids $base_stime $kill_list) fi fi [ -n "$kill_list" ] && return 1 return 0 } # __proc_pids {program} [pidfile] # Set $pid to pids from /var/run* for {program}. $pid should be declared # local in the caller. # Returns LSB exit code for the 'status' action. __pids_var_run() { local base=${1##*/} local pid_file=${2:-/var/run/$base.pid} local pid_dir=$(/usr/bin/dirname $pid_file > /dev/null) local binary=$3 [ -d "$pid_dir" -a ! -r "$pid_dir" ] && return 4 pid= if [ -f "$pid_file" ] ; then local line p [ ! -r "$pid_file" ] && return 4 # "user had insufficient privilege" while : ; do read line [ -z "$line" ] && break for p in $line ; do if [ -z "${p//[0-9]/}" ] && [ -d "/proc/$p" ] ; then if [ -n "$binary" ] ; then local b=$(readlink /proc/$p/exe | sed -e 's/\s*(deleted)$//') [ "$b" != "$binary" ] && continue fi pid="$pid $p" fi done done < "$pid_file" if [ -n "$pid" ]; then return 0 fi return 1 # "Program is dead and /var/run pid file exists" fi return 3 # "Program is not running" } # Output PIDs of matching processes, found using pidof __pids_pidof() { pidof -c -m -o $$ -o $PPID -o %PPID -x "$1" || \ pidof -c -m -o $$ -o $PPID -o %PPID -x "${1##*/}" } # A function to start a program. daemon() { # Test syntax. local gotbase= force= nicelevel corelimit local pid base= user= nice= bg= pid_file= local cgroup= nicelevel=0 while [ "$1" != "${1##[-+]}" ]; do case $1 in '') echo $"$0: Usage: daemon [+/-nicelevel] {program}" "[arg1]..." return 1 ;; --check) base=$2 gotbase="yes" shift 2 ;; --check=?*) base=${1#--check=} gotbase="yes" shift ;; --user) user=$2 shift 2 ;; --user=?*) user=${1#--user=} shift ;; --pidfile) pid_file=$2 shift 2 ;; --pidfile=?*) pid_file=${1#--pidfile=} shift ;; --force) force="force" shift ;; [-+][0-9]*) nice="nice -n $1" shift ;; *) echo $"$0: Usage: daemon [+/-nicelevel] {program}" "[arg1]..." return 1 ;; esac done # Save basename. [ -z "$gotbase" ] && base=${1##*/} # See if it's already running. Look *only* at the pid file. __pids_var_run "$base" "$pid_file" [ -n "$pid" -a -z "$force" ] && return # make sure it doesn't core dump anywhere unless requested corelimit="ulimit -S -c ${DAEMON_COREFILE_LIMIT:-0}" # if they set NICELEVEL in /etc/sysconfig/foo, honor it [ -n "${NICELEVEL:-}" ] && nice="nice -n $NICELEVEL" # if they set CGROUP_DAEMON in /etc/sysconfig/foo, honor it if [ -n "${CGROUP_DAEMON}" ]; then if [ ! -x /bin/cgexec ]; then echo -n "Cgroups not installed"; warning echo else cgroup="/bin/cgexec"; for i in $CGROUP_DAEMON; do cgroup="$cgroup -g $i"; done fi fi # Echo daemon [ "${BOOTUP:-}" = "verbose" -a -z "${LSB:-}" ] && echo -n " $base" # And start it up. if [ -z "$user" ]; then $cgroup $nice /bin/bash -c "$corelimit >/dev/null 2>&1 ; $*" else $cgroup $nice runuser -s /bin/bash $user -c "$corelimit >/dev/null 2>&1 ; $*" fi [ "$?" -eq 0 ] && success $"$base startup" || failure $"$base startup" } # A function to stop a program. killproc() { local RC killlevel= base pid pid_file= delay try binary= RC=0; delay=3; try=0 # Test syntax. if [ "$#" -eq 0 ]; then echo $"Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]" return 1 fi if [ "$1" = "-p" ]; then pid_file=$2 shift 2 fi if [ "$1" = "-b" ]; then if [ -z $pid_file ]; then echo $"-b option can be used only with -p" echo $"Usage: killproc -p pidfile -b binary program" return 1 fi binary=$2 shift 2 fi if [ "$1" = "-d" ]; then delay=$(echo $2 | awk -v RS=' ' -v IGNORECASE=1 '{if($1!~/^[0-9.]+[smhd]?$/) exit 1;d=$1~/s$|^[0-9.]*$/?1:$1~/m$/?60:$1~/h$/?60 *60:$1~/d$/?24*60*60:-1;if(d==-1) exit 1;delay+=d*$1} END {printf("%d",delay+0.5)}') if [ "$?" -eq 1 ]; then echo $"Usage: killproc [-p pidfile] [ -d delay] {program} [-signal]" return 1 fi shift 2 fi # check for second arg to be kill level [ -n "${2:-}" ] && killlevel=$2 # Save basename. base=${1##*/} # Find pid. __pids_var_run "$1" "$pid_file" "$binary" RC=$? if [ -z "$pid" ]; then if [ -z "$pid_file" ]; then pid="$(__pids_pidof "$1")" else [ "$RC" = "4" ] && { failure $"$base shutdown" ; return $RC ;} fi fi # Kill it. if [ -n "$pid" ] ; then [ "$BOOTUP" = "verbose" -a -z "${LSB:-}" ] && echo -n "$base " if [ -z "$killlevel" ] ; then __kill_pids_term_kill -d $delay $pid RC=$? [ "$RC" -eq 0 ] && success $"$base shutdown" || failure $"$base shutdown" # use specified level only else if checkpid $pid; then kill $killlevel $pid >/dev/null 2>&1 RC=$? [ "$RC" -eq 0 ] && success $"$base $killlevel" || failure $"$base $killlevel" elif [ -n "${LSB:-}" ]; then RC=7 # Program is not running fi fi else if [ -n "${LSB:-}" -a -n "$killlevel" ]; then RC=7 # Program is not running else failure $"$base shutdown" RC=0 fi fi # Remove pid file if any. if [ -z "$killlevel" ]; then rm -f "${pid_file:-/var/run/$base.pid}" fi return $RC } # A function to find the pid of a program. Looks *only* at the pidfile pidfileofproc() { local pid # Test syntax. if [ "$#" = 0 ] ; then echo $"Usage: pidfileofproc {program}" return 1 fi __pids_var_run "$1" [ -n "$pid" ] && echo $pid return 0 } # A function to find the pid of a program. pidofproc() { local RC pid pid_file= # Test syntax. if [ "$#" = 0 ]; then echo $"Usage: pidofproc [-p pidfile] {program}" return 1 fi if [ "$1" = "-p" ]; then pid_file=$2 shift 2 fi fail_code=3 # "Program is not running" # First try "/var/run/*.pid" files __pids_var_run "$1" "$pid_file" RC=$? if [ -n "$pid" ]; then echo $pid return 0 fi [ -n "$pid_file" ] && return $RC __pids_pidof "$1" || return $RC } status() { local base pid lock_file= pid_file= binary= # Test syntax. if [ "$#" = 0 ] ; then echo $"Usage: status [-p pidfile] {program}" return 1 fi if [ "$1" = "-p" ]; then pid_file=$2 shift 2 fi if [ "$1" = "-l" ]; then lock_file=$2 shift 2 fi if [ "$1" = "-b" ]; then if [ -z $pid_file ]; then echo $"-b option can be used only with -p" echo $"Usage: status -p pidfile -b binary program" return 1 fi binary=$2 shift 2 fi base=${1##*/} if [ "$_use_systemctl" = "1" ]; then systemctl status ${0##*/}.service ret=$? # LSB daemons that dies abnormally in systemd looks alive in systemd's eyes due to RemainAfterExit=yes # lets adjust the reality a little bit if systemctl show -p ActiveState ${0##*/}.service | grep -q '=active$' && \ systemctl show -p SubState ${0##*/}.service | grep -q '=exited$' ; then ret=3 fi return $ret fi # First try "pidof" __pids_var_run "$1" "$pid_file" "$binary" RC=$? if [ -z "$pid_file" -a -z "$pid" ]; then pid="$(__pids_pidof "$1")" fi if [ -n "$pid" ]; then echo $"${base} (pid $pid) is running..." return 0 fi case "$RC" in 0) echo $"${base} (pid $pid) is running..." return 0 ;; 1) echo $"${base} dead but pid file exists" return 1 ;; 4) echo $"${base} status unknown due to insufficient privileges." return 4 ;; esac if [ -z "${lock_file}" ]; then lock_file=${base} fi # See if /var/lock/subsys/${lock_file} exists if [ -f /var/lock/subsys/${lock_file} ]; then echo $"${base} dead but subsys locked" return 2 fi echo $"${base} is stopped" return 3 } echo_success() { [ "$BOOTUP" = "color" ] && $MOVE_TO_COL echo -n "[" [ "$BOOTUP" = "color" ] && $SETCOLOR_SUCCESS echo -n $" OK " [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL echo -n "]" echo -ne "\r" return 0 } echo_failure() { [ "$BOOTUP" = "color" ] && $MOVE_TO_COL echo -n "[" [ "$BOOTUP" = "color" ] && $SETCOLOR_FAILURE echo -n $"FAILED" [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL echo -n "]" echo -ne "\r" return 1 } echo_passed() { [ "$BOOTUP" = "color" ] && $MOVE_TO_COL echo -n "[" [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING echo -n $"PASSED" [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL echo -n "]" echo -ne "\r" return 1 } echo_warning() { [ "$BOOTUP" = "color" ] && $MOVE_TO_COL echo -n "[" [ "$BOOTUP" = "color" ] && $SETCOLOR_WARNING echo -n $"WARNING" [ "$BOOTUP" = "color" ] && $SETCOLOR_NORMAL echo -n "]" echo -ne "\r" return 1 } # Inform the graphical boot of our current state update_boot_stage() { if [ -x /bin/plymouth ]; then /bin/plymouth --update="$1" fi return 0 } # Log that something succeeded success() { [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_success return 0 } # Log that something failed failure() { local rc=$? [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_failure [ -x /bin/plymouth ] && /bin/plymouth --details return $rc } # Log that something passed, but may have had errors. Useful for fsck passed() { local rc=$? [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_passed return $rc } # Log a warning warning() { local rc=$? [ "$BOOTUP" != "verbose" -a -z "${LSB:-}" ] && echo_warning return $rc } # Run some action. Log its output. action() { local STRING rc STRING=$1 echo -n "$STRING " shift "$@" && success $"$STRING" || failure $"$STRING" rc=$? echo return $rc } # returns OK if $1 contains $2 strstr() { [ "${1#*$2*}" = "$1" ] && return 1 return 0 } # Check whether file $1 is a backup or rpm-generated file and should be ignored is_ignored_file() { case "$1" in *~ | *.bak | *.old | *.orig | *.rpmnew | *.rpmorig | *.rpmsave) return 0 ;; esac return 1 } # Convert the value ${1} of time unit ${2}-seconds into seconds: convert2sec() { local retval="" case "${2}" in deci) retval=$(awk "BEGIN {printf \"%.1f\", ${1} / 10}") ;; centi) retval=$(awk "BEGIN {printf \"%.2f\", ${1} / 100}") ;; mili) retval=$(awk "BEGIN {printf \"%.3f\", ${1} / 1000}") ;; micro) retval=$(awk "BEGIN {printf \"%.6f\", ${1} / 1000000}") ;; nano) retval=$(awk "BEGIN {printf \"%.9f\", ${1} / 1000000000}") ;; piko) retval=$(awk "BEGIN {printf \"%.12f\", ${1} / 1000000000000}") ;; esac echo "${retval}" } # Evaluate shvar-style booleans is_true() { case "$1" in [tT] | [yY] | [yY][eE][sS] | [oO][nN] | [tT][rR][uU][eE] | 1) return 0 ;; esac return 1 } # Evaluate shvar-style booleans is_false() { case "$1" in [fF] | [nN] | [nN][oO] | [oO][fF][fF] | [fF][aA][lL][sS][eE] | 0) return 0 ;; esac return 1 } # Apply sysctl settings, including files in /etc/sysctl.d apply_sysctl() { if [ -x /lib/systemd/systemd-sysctl ]; then /lib/systemd/systemd-sysctl else for file in /usr/lib/sysctl.d/*.conf ; do is_ignored_file "$file" && continue [ -f /run/sysctl.d/${file##*/} ] && continue [ -f /etc/sysctl.d/${file##*/} ] && continue test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1 done for file in /run/sysctl.d/*.conf ; do is_ignored_file "$file" && continue [ -f /etc/sysctl.d/${file##*/} ] && continue test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1 done for file in /etc/sysctl.d/*.conf ; do is_ignored_file "$file" && continue test -f "$file" && sysctl -e -p "$file" >/dev/null 2>&1 done sysctl -e -p /etc/sysctl.conf >/dev/null 2>&1 fi } # A sed expression to filter out the files that is_ignored_file recognizes __sed_discard_ignored_files='/\(~\|\.bak\|\.old\|\.orig\|\.rpmnew\|\.rpmorig\|\.rpmsave\)$/d' if [ "$_use_systemctl" = "1" ]; then if [ "x$1" = xstart -o \ "x$1" = xstop -o \ "x$1" = xrestart -o \ "x$1" = xreload -o \ "x$1" = xtry-restart -o \ "x$1" = xforce-reload -o \ "x$1" = xcondrestart ] ; then systemctl_redirect $0 $1 exit $? fi fi strstr "$(cat /proc/cmdline)" "rc.debug" && set -x return 0 [root@node101.yinzhengjie.org.cn ~/shell]#
5>.调用操作系统自带的函数
[root@node101.yinzhengjie.org.cn ~]# . /etc/init.d/functions #调用操作系统自带的函数 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# declare -f action #查看该函数的定义过程 action () { local STRING rc; STRING=$1; echo -n "$STRING "; shift; "$@" && success "$STRING" || failure "$STRING"; rc=$?; echo; return $rc } [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# action "start httpd:" true #使用过上面中的action函数,它可以帮我们实现以下效果 start httpd: [ OK ] [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# action "stopping httpd:" false stopping httpd: [FAILED] [root@node101.yinzhengjie.org.cn ~]#
三.函数使用
函数的定义和使用: 可在交互式环境下定义函数 可将函数放在脚本文件中作为它的一部分 可放在只包含函数的单独文件中 调用: 函数只有被调用才会执行 调用:给定函数名 函数名出现的地方,会被自动替换为函数代码 函数的生命周期: 被调用时创建,返回时终止 删除shell函数: 现在对函数做一些改动后,需要先删除函数,使其对shell不可用。使用unset命令完成删除函数
函数可以接受参数: 传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如:"testfunc arg1 arg2 ..." 在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量 函数变量作用域: 环境变量:当前shell和子shell有效 本地变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数 局部变量:函数的生命周期;函数结束时变量被自动销毁(注意,如果函数中有局部变量,如果其名称同本地变量,使用局部变量,在函数中定义局部变量的方法:"local NAME=VALUE") 函数递归: 函数直接或间接调用自身,注意递归层数 fork炸弹: 是一种恶意程序,它的内部是一个不断在fork进程的无限循环,实质是一个简单的递归程序。由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有资源。
1>.交互式环境下定义和使用函数
[root@node101.yinzhengjie.org.cn ~]# sysinfo(){ hostname;cat /etc/centos-release; } #在当前命令行中定义一个sysinfo的函数 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# sysinfo #调用函数 node101.yinzhengjie.org.cn CentOS Linux release 7.6.1810 (Core) [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# declare -f sysinfo #查看sysinfo这个函数的信息 sysinfo () { hostname; cat /etc/centos-release } [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# set | tail -5 #也可以使用set命令检查函数是否已经载入 sysinfo () { hostname; cat /etc/centos-release } [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# set | wc -l #查看当前行号 1261 [root@node101.yinzhengjie.org.cn ~# [root@node101.yinzhengjie.org.cn ~]# unset sysinfo #该sysinfo函数将一直保留到用户从系统退出,或执行了unset命令 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# set | wc -l 1256 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# sysinfo #由于我们已经删除函数啦,因此无法继续调用该函数 -bash: sysinfo: command not found [root@node101.yinzhengjie.org.cn ~]#
2>.在脚本中定义及使用函数
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用
调用函数仅使用其函数名即可
[root@node101.yinzhengjie.org.cn ~/shell]# cat sysinfo.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: sysinfo.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** sysinfo () { hostname; cat /etc/centos-release } sysinfo [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# bash sysinfo.sh node101.yinzhengjie.org.cn CentOS Linux release 7.6.1810 (Core) [root@node101.yinzhengjie.org.cn ~/shell]#
3>.使用函数文件
可以将经常使用的函数存入函数文件,然后将函数文件载入shell
文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件
[root@node101.yinzhengjie.org.cn ~/shell]# cat functions.sh #创建函数文件 #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: shell/test_function.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** version(){ sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/centos-release } sysinfo(){ echo Hostname is : `hostname` echo The OS version is : `version` } sum(){ local a b a=$1 b=$2 echo $(( $a + $b )) } sub(){ local a b a=$1 b=$2 echo $(( $a - $b )) } ipaddr(){ ifconfig $1 | sed -nr '2s/.*inet (addr:)?([^ ]+).*/\2/p' } [root@node101.yinzhengjie.org.cn ~/shell]#
[root@node101.yinzhengjie.org.cn ~/shell]# cat test_function.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: shell/test_function.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** #函数文件已创建好后,要将它载入shell,定位函数文件并载入shell的格式除了下面以"."的方式载入,还可以使用"source"命令载入 . functions.sh sum $1 $2 sub $1 $2 [root@node101.yinzhengjie.org.cn ~/shell]#
[root@node101.yinzhengjie.org.cn ~/shell]# bash test_function.sh 100 20 #在shell执行脚本 120 80 [root@node101.yinzhengjie.org.cn ~/shell]#
4>.局部变量关键字local(只在当前函数中生效的变量)案例
[root@node101.yinzhengjie.org.cn ~/shell]# cat functions.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: shell/test_function.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** version(){ sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/centos-release } sysinfo(){ echo Hostname is : `hostname` echo The OS version is : `version` } sum(){ local a b a=$1 b=$2 echo $(( $a + $b )) } sub(){ local a b a=$1 b=$2 echo $(( $a - $b )) } [root@node101.yinzhengjie.org.cn ~/shell]#
[root@node101.yinzhengjie.org.cn ~/shell]# cat test_function.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: shell/test_function.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** . functions.sh sum $1 $2 sub $1 $2 [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# bash test_function.sh 100 20 120 80 [root@node101.yinzhengjie.org.cn ~/shell]#
5>.判断一个脚本是否是数字(如果用户传入的第一个参数)
[root@node101.yinzhengjie.org.cn ~]# cat functions.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: shell/test_function.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** version() { sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/centos-release } sysinfo() { echo Hostname is : `hostname` echo The OS version is : `version` } sum() { local a b a=$1 b=$2 echo $(( $a + $b )) } sub() { local a b a=$1 b=$2 echo $(( $a - $b )) } ipaddr() { ifconfig $1 | sed -nr '2s/.*inet (addr:)?([^ ]+).*/\2/p' } is_digit() { [[ "$1" =~ ^[0-9]+$ ]] && true || false } [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# source functions.sh [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# is_digit 222 [root@node101.yinzhengjie.org.cn ~]# echo $? 0 [root@node101.yinzhengjie.org.cn ~]# is_digit abc [root@node101.yinzhengjie.org.cn ~]# echo $? 1 [root@node101.yinzhengjie.org.cn ~]#
6>.环境函数
[root@node101.yinzhengjie.org.cn ~/shell]# cat functions.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: shell/test_function.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** is_digit() { [[ "$1" =~ ^[0-9]+$ ]] && true || false } #使子进程也可以使用该函数,就可以使用"export -f function_name"(或 declare -xf)来声明为环境函数。 export -f is_digit [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]#
[root@node101.yinzhengjie.org.cn ~/shell]# cat f1.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: f1.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** . functions.sh is_digit "def" && echo true || echo false bash f2.sh [root@node101.yinzhengjie.org.cn ~/shell]#
[root@node101.yinzhengjie.org.cn ~/shell]# cat f2.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: f2.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** is_digit 100 && echo true || echo false [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]#
[root@node101.yinzhengjie.org.cn ~/shell]# bash f1.sh false true [root@node101.yinzhengjie.org.cn ~/shell]#
7>.递归案例
[root@node101.yinzhengjie.org.cn ~]# cat shell/factorial.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-28 #FileName: shell/factorial.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** #阶乘是基斯顿·卡曼于 1808 年发明的运算符号,是数学术语,一个正整数的阶乘(factorial)是所有小于及等于该数的正整数的积,并且有0的阶乘为1,自然数n的阶乘写作n! fact() { #注意这个"$1"是函数的第一个参数 if [ "$1" -eq 1 ] ;then echo 1 else echo $[$1*`fact $[$1-1]`] fi } #这个$1是用户传入的第一个位置参数 fact $1 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# bash -n shell/factorial.sh [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# bash -x shell/factorial.sh 5 + fact 5 + '[' 5 -eq 1 ']' ++ fact 4 ++ '[' 4 -eq 1 ']' +++ fact 3 +++ '[' 3 -eq 1 ']' ++++ fact 2 ++++ '[' 2 -eq 1 ']' +++++ fact 1 +++++ '[' 1 -eq 1 ']' +++++ echo 1 ++++ echo 2 +++ echo 6 ++ echo 24 + echo 120 120 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]#
[root@node101.yinzhengjie.org.cn ~]# vim shell/bomb.sh [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# cat shell/bomb.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-28 #FileName: shell/bomb.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** #fork炸弹是一种恶意程序,它的内部是一个不断在fork进程的无限循环,实质是一个简单的递归程序。由于程序是递归的,如果没有任何限制,这会导致这个简单的程序迅速耗尽系统里面的所有 资源#函数实现为如下: # ":(){ :|:& };:" #为了看起来方便,咱们可以改写上面的代码如下所示,相对要简单的多 #bomb() { # bomb | bomb & #} # #bomb #其实除了上面的写法还可以直接写成更简单的方式,一旦出现fork炸弹系统会迅速变得特别卡顿,因该重启操作系统,若重启操作系统后依旧无法解决就得进入到单用户模式排查是否有脚本开机启 动运行的情况。./$0|./$0& [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# bash shell/bomb.sh shell/bomb.sh: line 24: ./shell/bomb.sh: Permission denied shell/bomb.sh: line 24: ./shell/bomb.sh: Permission denied [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# chmod +x shell/bomb.sh [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# ./shell/bomb.sh [root@node101.yinzhengjie.org.cn ~]#
四.小试牛刀
1>.定义获取操作系统IP的函数
[root@node101.yinzhengjie.org.cn ~/shell]# cat functions.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: shell/test_function.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** ipaddr() { ifconfig $1 | sed -nr '2s/.*inet (addr:)?([^ ]+).*/\2/p' } ipaddr2() { ifconfig $1 | sed -nr '2s/[^0-9]+([.0-9]+).*/\1/p' } [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# . functions.sh [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# ipaddr2 10.0.2.15 [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# ipaddr 10.0.2.15 [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]#
2>.使用函数的形式打印国际象棋
[root@node101.yinzhengjie.org.cn ~]# cat shell/chess.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-25 #FileName: shell/chess.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** #40表示灰黑色,41表示红色,42表示绿色,43表示黄色,44表示蓝色,45表示紫,46表示浅蓝色,47表示白色 BLUE_COLOR="\033[1;46m" WHITE_COLOR="\033[1;47m" COLOREND="\033[0m" blue(){ echo -e "$BLUE_COLOR ${COLOREND}\c" } white(){ echo -e "$WHITE_COLOR ${COLOREND}\c" } blue_white(){ for ((i=1;i<=4;i++));do for ((j=1;j<=4;j++));do [ "$1" = "-v" ] && { blue;white; } || { white;blue; } done echo done } for ((line=1;line<=8;line++));do [ $[$line%2] -eq 0 ] && blue_white || blue_white -v done [root@node101.yinzhengjie.org.cn ~]#
3>.判断用户输入的是否是合法IP
[root@node101.yinzhengjie.org.cn ~/shell]# cat functions.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-27 #FileName: shell/test_function.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** version() { sed -nr 's/.* ([0-9]+)\..*/\1/p' /etc/centos-release } sysinfo() { echo Hostname is : `hostname` echo The OS version is : `version` } sum() { local a b a=$1 b=$2 echo $(( $a + $b )) } sub() { local a b a=$1 b=$2 echo $(( $a - $b )) } ipaddr() { ifconfig $1 | sed -nr '2s/.*inet (addr:)?([^ ]+).*/\2/p' } is_digit() { [[ "$1" =~ ^[0-9]+$ ]] && true || false } is_addr() { [[ "$1" =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]&& echo true || echo false } [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# . functions.sh [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# is_addr 127.0.0.1 true [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# is_addr 172.30.1.101 true [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# is_addr 1111.111.111 false [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]# is_addr 255.255.255.255 true [root@node101.yinzhengjie.org.cn ~/shell]# is_addr 255.255.255.256 false [root@node101.yinzhengjie.org.cn ~/shell]# [root@node101.yinzhengjie.org.cn ~/shell]#
4>.利用函数,求n阶斐波那契数列
[root@node101.yinzhengjie.org.cn ~]# cat shell/fib.sh #!/bin/bash # #******************************************************************** #Author: yinzhengjie #QQ: 1053419035 #Date: 2019-11-28 #FileName: shell/fib.sh #URL: http://www.cnblogs.com/yinzhengjie #Description: The test script #Copyright notice: original works, no reprint! Otherwise, legal liability will be investigated. #******************************************************************** #斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列: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)fib() { if [ "$1" -eq 0 ];then echo 0 elif [ "$1" -eq 1 ];then echo 1 else echo $[`fib $[$1-1]` + `fib $[$1-2]`] fi } fib $1 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# bash shell/fib.sh 3 2 [root@node101.yinzhengjie.org.cn ~]# bash shell/fib.sh 5 5 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# bash shell/fib.sh 10 55 [root@node101.yinzhengjie.org.cn ~]# [root@node101.yinzhengjie.org.cn ~]# bash shell/fib.sh 20 6765 [root@node101.yinzhengjie.org.cn ~]#
5>.编写函数,实现OS的版本判断
6>.编写函数,实现取出当前系统eth0的IP地址
7>.编写函数,实现打印绿色OK和红色FAILED
8>.编写函数,实现判断是否无位置参数,如无参数,提示错误
9>.编写服务脚本/root/bin/testsrv.sh
完成如下要求: (1)脚本可接受参数:start, stop, restart, status (2)如果参数非此四者之一,提示使用格式后报错退出 (3)如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功” 考虑:如果事先已经启动过一次,该如何处理? (4)如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成” 考虑:如果事先已然停止过了,该如何处理? (5)如是restart,则先stop, 再start 考虑:如果本来没有start,如何处理? (6)如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示“SCRIPT_NAME is running...”,如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...” (7)在所有模式下禁止启动该服务,可用chkconfig 和 service命令管理 说明:SCRIPT_NAME为当前脚本名
10>.编写脚本/root/bin/copycmd.sh
完成如下要求: (1)提示用户输入一个可执行命令名称 (2)获取此命令所依赖到的所有库文件列表 (3)复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下 如:/bin/bash ==> /mnt/sysroot/bin/bash /usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd (4)复制此命令依赖到的所有库文件至目标目录下的对应路径下: 如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ld-linux-x86-64.so.2 (5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出
11>.编写函数实现两个数字做为参数,返回最大值
12>.编写汉诺塔程序
汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘,利用函数,实现N片盘的汉诺塔的移动步骤
当你的才华还撑不起你的野心的时候,你就应该静下心来学习。当你的能力还驾驭不了你的目标的时候,你就应该沉下心来历练。问问自己,想要怎样的人生。
欢迎交流学习技术交流,个人微信: "JasonYin2020"(添加时请备注来源及意图备注)
作者: 尹正杰, 博客: https://www.cnblogs.com/yinzhengjie/p/9333212.html