Linux shell脚本的学习之旅
shell脚本的概念
shell脚本全称为shell script,即是程序化脚本,是利用shell功能所写的一个程序,这个程序是使用纯文本文件,将一些shell的语法和命令写在里面,并搭配正则表达式、管道命令与数据流重定向等功能,达到所需要处理的目的。
学习shell脚本的意义
学习shell脚本,主要有以下几大好处:
- 是自动化管理的重要根据
- 方便跟踪和管理系统的重要工作
- 可以做出简单入侵检测功能
- 连续的命令单一化
- 简易的数据处理
- 跨平台的支持和学习历程较短
脚本的编写与执行
在编写shell脚本时,需要注意以下的事项:
- 命令是从上到下,从左到右的分析与执行的;
- 命令的执行过程中,会将命令、选项和参数间的多个空格忽略掉;
- 空白行也将被忽略,并且[Tab]按键所产生的空白同样也被视为空格键;
- 读取到第一个[Enter]符号,就会尝试开始执行该行或该串的命令;
- 如果一行的命令内容太多,可以利用【[\Enter]】来扩展至下一行;
- [#]作为注释标志,任何加在#后面的数据都将被视为注释文字而忽略;
在了解以上的shell编写注意事项后,开始了解shell脚本的执行流程,以程序文件名/home/ZFL/shell.sh为 例,其可以通过以下的方法执行:
- 直接命令执行:shell.sh文件必须要具备可读和可执行(rx)的权限,然后:
- 绝对路径:使用/home/ZFL/shell.sh来执行命令;
- 相对路径:如果工作目录在/home/ZFL/,则使用./shell.sh来执行;
- 变量【PATH】功能:可以将shell.sh放置在PATH指定的工作目录中,例如:~/bin/;
- 以bash程序执行:通过【bash shell.sh】或者【sh shell.sh】来执行。
注意,通过bash程序执行脚本时,shell.sh只要有r的权限即可,而通过其它方式,则要求shell.sh需要具备可读和可执行的权限!
#我的第一个shell脚本程序
#!/bin/bash
#Program:
# This is my frist shell,and This program shows "Hello world" in your screen!
#Histroy:
#2020/8/19 ZFL First release
PATH=/bin/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Hello World! \a \n"
exit 0
:wq!
zfl@localhost ~]$ mkdir bin; cd bin
[zfl@localhost bin]$ vim hello.sh
[zfl@localhost bin]$ sh hello.sh
Hello World!
程序的说明如下所示:
1、第一行 #!/bin/bash 在声明这个脚本所使用的的shell名称
#!/bin/bash 注意,这个是在声明这个脚本所使用的shell名称,为bash,当这个脚本执行时,加载bash的相关配置文件,并且执行bash来使下面的命令能够被执行!
2、程序内容的说明
除了第一的【#!】是用来声明shell之外,其他的#均作为注释的用途,其说明了该脚本的:1、内容与功能;2、版本的信息;3、作者与联系方式;4、所建文件时间;5、历史记录等
3、主要环境变量的声明
通过PATH与LANG(如果使用到输出相关信息),将一些重要的环境变量配置好,以方便直接执行一些外部命令,而不用写绝对路劲
4、主要程序部分
5、执行的结果
通过exit这个命令,以使程序中断,并且返回一个数值给系统
建立编写shell脚本的良好习惯
在编写脚本时,我们可以通过在每个脚本的文件头处记录程序设计过程,所配置的环境设置,及其历史记录,以方便后期的维护。一般情况下,可以记录以下内容:
- 脚本的功能;
- 脚本的版本信息;
- 脚本的作者与联系方式;
- 脚本的版权声明;
- 脚本的历史记录;
- 脚本内的特殊命令,使用【绝对路径】方式执行;
- 脚本运行所需的环境变量预先声明与设置;
shell脚本的练习
简单范例
1、交互式脚本:变量的内容由用户决定
#!/bin/bash
#Program:
# User input his first name and last name,the program will shows his full name.
#History:
#2020/8/19 ZFL First release
PATH=/bin/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PAHT
read -p "Please input your first name:" firstname
read -p "Please input you last name:" lastname
echo -e "\n Your name is :${firstname} ${lastname}"
:wq!
执行脚本程序:
[zfl@localhost bin]$ vim showname.sh
[zfl@localhost bin]$ sh showname.sh
Please input your first name:zh
Please input you last name:fl
Your name is :zh fl
2、随日期变化:利用date建立文件
#!/bin/bash
#Program:
# program creates three files,which named by user's input and date command.
#History:
#2020/08/19 ZFL First release
PATH=/bin/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "I will use 'touch' command to create three files."
read -p "Please input your filename:" fileuser
filename=${fileuser:-"filename"}
date1=$(date --date='2 days ago' +%Y%m%d)
date2=$(date --date='1 days ago' +%Y%m%d)
date3=$(date +%Y%m%d)
file1=${filename}${date1}
file2=${filename}${date2}
file3=${filename}${date3}
touch "${file1}"
touch "${file2}"
touch "${file3}"
执行脚本程序:
[zfl@localhost bin]$ vim create_3_filename.sh
[zfl@localhost bin]$ sh create_3_filename.sh
I will use 'touch' command to create three files.
Please input your filename:create_deom
[zfl@localhost bin]$
脚本的执行方式差异
不同的脚本执行方式会造成不一样的结果,尤其对bash环境影响更大,除了以上提及到的执行方式,还可以利用source或者小数点(.)来执行。
利用直接执行的方式来执行脚本:使用直接执行的方式来执行脚本的时候(【绝对路径】、【相对路径】、【${PATH}】或者利用bash命令),脚本都是在子进程的bash内执行,当子进程完成后,在子进程内的各项变量或者操作将会结束而不会传回到父进程中,而利用source方式来执行脚本程序,则会在父进程之中执行。
善用判断式
利用test命令的测试功能
利用test命令可以帮助我们检测系统上面的某些文件或者是相关的属性是否存在,但需要注意的是,该命令的执行结果并不会显示任何信息,需要借助【$?】或者【&&】与【||】来展示结果。
[zfl@localhost bin]$ test -e hello.sh #执行的结果并没有显示。
[zfl@localhost bin]$
#通过借助【&&】和【||】命令显示执行结果
[zfl@localhost bin]$ test -e hello.sh && echo "exist" || echo "not exist"
exist
test命令有以下的参数,列表如下:
1、对某个文件名的【文件类型】判断:
2、对某个文件的权限检测:
3、两个文件之间的比较:
4、关于两个整数之间的判定:
5、判定字符串的数据:
6、多重条件判定:
举个小栗子:
#!/bin/bash
#Program:
# User input a filename,program will check the flowing:
# 1.) exist? 2.)file/directory? 3.)file permissions
#History:
#2020/08/19 ZFL First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo -e "Please input a filename,I will check the filename'tpye and permission. \n\n"
read -p "Input a filename :" filename
test -z ${filename} && echo "You must input a filename." && exit 0
test ! -e ${filename} && echo "the filename '${filename}' DO NOT exist " && exit 0
test -f ${fileanme} && filetype="regulare file"
test -d ${filename} && filetype="directory"
test -r ${filename} && perm="readable"
test -w ${filename} && perm="${perm} writable"
test -x ${filename} && perm="${perm} executable"
echo "the filename:${filename} is a ${filetype}
echo "and the permission for you are :${perm}
~
:wq!
执行shell脚本程序:
[zfl@localhost bin]$ sh file_perm.sh
Please input a filename,I will check the filename'tpye and permission.
Input a filename :file_perm.sh
the filename:file_perm.sh is a regulare file
echo and the permission for you are :readable writable
[zfl@localhost bin]$
利用判断符号[ ]
除了使用test命令之外,还可以利用【[ ]】这个判断符号进行数据的判断,举个小栗子:
[zfl@localhost bin]$ [ "$HOME" == "$MAIL" ];echo $?
1
#注意,因为中括号使用在很多地方,包括通配符以及正则表达式,所以,在bash语法当中,如果使用中括号,作为shell的判断式,那么,就必须注意中括号两端需要有空格符来分隔。
#在使用【[ ]】时,注意的内容如下:
# 1、在中括号[]内的每个组件都需要空格来分隔;
# 2、在中括号的变量,最好都以双引号括号起来;
# 3、在钟括号的常量,最好都以单或者双引号括号起来;
shell脚本的默认变量
#!/bin/bash
#Program:
# Program shows the script name,parameters...
#History:
#2020/08/19 ZFL First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "The script name is ==>${0}"
echo "Total paramter number is ==>$#"
[ "$#" -lt 2 ] && echo "The number of paramter is less than 2. Stop here." && exit 0
echo "Your whole paramter is ==>'$@' "
echo "The 1st paramter ==>${1}"
echo "The 2st paramter ==>${2}"
:wq!
[zfl@localhost bin]$ vim how_paras.sh
[zfl@localhost bin]$ sh how_paras.sh theone haha quot
The script name is ==>how_paras.sh
Total paramter number is ==>3
Your whole paramter is ==>'theone haha quot'
The 1st paramter ==>theone
The 2st paramter ==>haha
#默认变量:
# ${0},${1}...
# 其中$#:代表后接的参数【个数】,$@:代表【"$1" "$2"】而$*:代表【"$1"c"$2"c】,其中c为分隔字符,默认为空格。
#除此之外,还可以用shift(偏移)参数号码,shift之后可接数字,代表拿掉最前面的几个参数。
条件判断式
利用if … then
1、单层、简单条件判断式:如果只有一个判断式进行,可以这样看:
if [ 条件判断式 ] ; then
当条件判断式成立时,可以进行的命令的工作内容;
fi (<==将if反过来写,就成了fi,意味着结束if,注意,此时只是注释内容)
2、多重、复杂条件判断式:在同一数据的判断中,该数据需要进行多种不同的判断时可采取以下方法:
if [ 条件判断式 ] ;then
当条件判断式成立时,可执行的命令。
else
当条件判断式不成立时,执行该命令。
fi
如果遇到更复杂的情况,则可以使用下列的语法处理问题:
if [ 条件判断式一 ] ;then
当条件判断式一成立时,可执行的命令。
elif [ 条件判断式二 ];then
当条件判断式二成立时,可执行的命令。
else
当条件判断式一和二均不成立时,执行该命令。
fi
举个小栗子:
#通过netstat命令,可以查询目前主机开启的网络端口(service ports)
#以下是借助netstat -tuln获取目前主机启动的服务
[zfl@localhost ~]$ netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN
tcp6 0 0 :::111 :::* LISTEN
tcp6 0 0 :::22 :::* LISTEN
tcp6 0 0 ::1:631 :::* LISTEN
tcp6 0 0 ::1:25 :::* LISTEN
udp 0 0 127.0.0.1:323 0.0.0.0:*
udp 0 0 0.0.0.0:46143 0.0.0.0:*
udp 0 0 0.0.0.0:67 0.0.0.0:*
udp 0 0 0.0.0.0:68 0.0.0.0:*
udp 0 0 0.0.0.0:111 0.0.0.0:*
udp 0 0 0.0.0.0:5353 0.0.0.0:*
udp 0 0 0.0.0.0:751 0.0.0.0:*
udp6 0 0 ::1:323 :::*
udp6 0 0 :::111 :::*
udp6 0 0 :::751 :::*
[zfl@localhost ~]$
#其中的重点在于Local Address(本地主机IP与端口对应)这个字段,其中127.0.0.1代表针对本机开发,而0.0.0.0或者:::则为对整个Internet开放。以下是几个常见的端口与相关的网络服务的关系:
# 80:www
# 22:ssh
# 21:ftp
# 25:mail
# 111:RPC(远程过程调用)
# 631:CUPS(打印服务功能)
以下案例,是netstat.sh脚本程序实现了通过netstat命令去检测主机是否开启21、22、25及80等常见的网络端口的功能。
#!/bin/bash
#Program:
# Using netstat and grep to detect www,ssh,ftp and mail services.
#History:
#2020/08/19 ZFL First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
echo "Now ,I will detect your linux server's services!"
echo -e "The www, ssh, ftp, mail(smtp) will be detect! \n"
testfile=/dev/shm/netstat_checking.txt
netstat -tuln > ${testfile} #先将数据转存到内存之中,不用一直执行netstat
testing=$(grep ":80" ${testfile})
if [ "${testing}" != "" ]; then
echo "www is running in your system"
fi
testing=$(grep ":22" ${testfile})
if [ "${testing}" != "" ]; then
echo "ssh is running in your system"
fi
testing=$(grep ":21" ${testfile})
if [ "${testing}" != "" ]; then
echo "ftp is running in your system"
fi
testing=$(grep ":25" ${testfile})
if [ "${testing}" != "" ]; then
echo "mail is running in your system"
fi
:wq!
执行该shell脚本程序:
[zfl@localhost ~]$ sh netstat.sh
Now ,I will detect your linux server's services!
The www, ssh, ftp, mail(smtp) will be detect!
ssh is running in your system
mail is running in your system
[zfl@localhost ~]$
利用case … esac判断
当需要为多个既定的变量内容设置状态时,可以利用case…esac设计,其语法格式如下:
case $变量名称 in <==关键字为case,还有变量前有美元符号
“第一个变量内容”) <==每个变量内容建议用双引号括起来,关键字则为右圆括号。
程序段
;; <==每个类别结尾使用两个连续的分号处理(注意这里的分号实在英文条件下输入)
“第二个变量内容” )
程序段
;;
*) <==最后一个变量内容都会用 * 来代表其他所有的值。
不包含第一个变量和第二个变量内容的其他程序执行段
exit 1
;;
esac <==最终的case结尾。
举个小栗子:
#!/bin/bash
#Program:
# Show "Hello" from $1 ... by using case ... esac
#History:
#2020/08/19 ZFL First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
case ${1} in
"hello")
echo "Hello ,how are you ?"
;;
"")
echo "You Must input paramters,ex>${${0} someword}"
;;
*)
echo "Usage ${0} {hello}"
;;
esac
一般来说,使用【case $变量 in】这个语法的时候,当中的那个【$变量】大致上有两种获取方式:
- 直接执行式:例如利用【script.sh variable】直接给与${1}这个变量的内容,这是在/etc/init.d目录下大多数程序的设计方式。
- 交互式:通过read命令,让用户输入变量的内容。
利用function功能
简单来说function功能就是利用在shell脚本程序中,做出一个类似自定义执行命令,简化程序代码的东西。语法如下:
function filename ( ) {
程序段
}
其中,filename就是自定义的执行命令名称,程序段为执行内容,注意:在shell脚本中,function的设置一定要在程序的最前面,另外,function也内置变量,函数名称代表$0,而后续的变量也是以$1、$2…替换。
循环(loop)
循环可以不间断的执行某个程序段落,直到用户设置的条件完成为止。
while do done
while [ condition ]
do
程序段落
done
until do done
until [ condition ]
do
程序段落
done
两者区别主要在于:
while do done 是指,当condition满足时,就进行循环,直到condition条件不再满足,才退出循环体,而
until do done 刚好与前者相反,意指当condition条件满足时退出循环体。
注意:这两者均为不定循环,没有固定需要跑多少次。
for …do …done
for variable in con1 con2 con3 …
do
程序段
done
举个小栗子:
[zfl@localhost bin]$ vim show_aminal.sh
#!/bin/bash
#Program:
# Using for ... loop to print three amimal
#History:
#2020/08/19 ZFL First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
for amimal in dog cat elephant
do
echo "There are ${amimal}s ..."
done
:wq!
[zfl@localhost bin]$ sh show_aminal.sh
There are dogs ...
There are cats ...
There are elephants ...
for …do …done的数值处理
for( (初始值;限制值;赋值运算))
do
程序段
done
其中,初始值为某个变量在循环当中的起始值;限制值则是在变量的值在这个限制范围内,则继续循环;而赋值运算,是每一次循环时,变量的变化赋值。
shell脚本的跟踪与调试
在脚本文件执行前,如果出现语法错误问题,可以直接利用bash的相关参数来进行判断。详细参数说明如下:
[zfl@localhost bin]$ sh -n show_aminal.sh #语法没出现错误,不会显示任何信息
[zfl@localhost bin]$ sh -x show_aminal.sh #将使用到的脚本内容显示到屏幕上
+ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/home/zfl/bin
+ export PATH
+ for amimal in dog cat elephant
+ echo 'There are dogs ...'
There are dogs ...
+ for amimal in dog cat elephant
+ echo 'There are cats ...'
There are cats ...
+ for amimal in dog cat elephant
+ echo 'There are elephants ...'
There are elephants ...
[zfl@localhost bin]$ sh -v show_aminal.sh #在执行脚本之前,先将脚本的内容显示输出到屏幕上
#!/bin/bash
#Program:
# Using for ... loop to print three amimal
#History:
#2020/08/19 ZFL First release
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
for amimal in dog cat elephant
do
echo "There are ${amimal}s ..."
done
There are dogs ...
There are cats ...
There are elephants ...
在最后,我们可以通过查看/etc/init.d/netconsole,来查看脚本内容,增强自己对shell脚本的了解。
#!/bin/bash
#
# netconsole This loads the netconsole module with the configured parameters.
#
# chkconfig: - 50 50
# description: Initializes network console logging
# config: /etc/sysconfig/netconsole
#
### BEGIN INIT INFO
# Provides: netconsole
# Required-Start: $network
# Short-Description: Initializes network console logging
# Description: Initializes network console logging of kernel messages.
### END INIT INFO
# Copyright 2002 Red Hat, Inc.
#
# Based in part on a shell script by
# Andreas Dilger <adilger@turbolinux.com> Sep 26, 2001
PATH=/sbin:/usr/sbin:$PATH
RETVAL=0
SERVER_ADDRESS_RESOLUTION=
# Check that networking is up.
. /etc/sysconfig/network
# Source function library.
. /etc/rc.d/init.d/functions
# Default values
LOCALPORT=6666
DEV=
SYSLOGADDR=
SYSLOGPORT=514
SYSLOGMACADDR=
kernel=$(uname -r | cut -d. -f1-2)
usage ()
{
echo $"Usage: $0 {start|stop|status|restart|condrestart}" 1>&2
RETVAL=2
}
print_address_info ()
{
local host=$1
local route via target
route=$(LANG=C ip -o route get to $host/32)
[ -z "$DEV" ] && DEV=$(echo $route | sed "s|.* dev \([^ ]*\).*|\1|")
echo "DEV=$DEV"
echo "LOCALADDR=$(echo $route | sed "s|.* src \([^ ]*\).*|\1|")"
if [[ $route == *" via "* ]] ; then
via=$(echo $route | sed "s|.* via \([^ ]*\).*|\1|")
target=$via
else
target=$host
fi
if [ -z "$SYSLOGMACADDR" ]; then
arp=$(LANG=C /sbin/arping -f -c 1 -I $DEV $target 2>/dev/null | awk '/ reply from .*[.*]/ { print gensub(".* reply from .* \\[(.*)\\].*","\\1","G"); exit }')
[ -n "$arp" ] && echo "SYSLOGMACADDR=$arp"
fi
}
start ()
{
[ -f /etc/sysconfig/netconsole ] || exit 6
. /etc/sysconfig/netconsole
SYSLOGOPTS=
# syslogd server, if any
if [ -n "$SYSLOGADDR" ]; then
# IPv6 regex also covers 4to6, zero-compressed, and link-local addresses with zone-index addresses.
# It should also cover IPv4-embedded, IPv4-mapped, and IPv4-translated IPv6 addresses.
# Taken from: http://stackoverflow.com/a/17871737/3481531
IPv4_regex="^([0-9]{1,3}\.){3}[0-9]{1,3}$"
IPv6_regex="^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$"
if ! [[ "$SYSLOGADDR" =~ $IPv4_regex ]] && ! [[ "$SYSLOGADDR" =~ $IPv6_regex ]]; then
# Use IPv4 by default:
SYSLOGADDR="$(LANG=C getent ahostsv4 $SYSLOGADDR 2> /dev/null)"
# Try IPv6 in case IPv4 resolution has failed:
if [[ $? -eq 2 ]]; then
SYSLOGADDR="$(LANG=C getent ahostsv6 $SYSLOGADDR 2> /dev/null)"
fi
if [[ $? -ne 0 ]]; then
echo $"Unable to resolve IP address specified in /etc/sysconfig/netconsole" 1>&2
exit 6
fi
SYSLOGADDR="$(echo "$SYSLOGADDR" | head -1 | cut --delimiter=' ' --fields=1)"
fi
fi
if [ -z "$SYSLOGADDR" ] ; then
echo $"Server address not specified in /etc/sysconfig/netconsole" 1>&2
exit 6
fi
eval $(print_address_info $SYSLOGADDR)
if [ -z "$SYSLOGMACADDR" ]; then
echo $"netconsole: can't resolve MAC address of $SYSLOGADDR" 1>&2
exit 1
fi
SYSLOGOPTS="netconsole=$LOCALPORT@$LOCALADDR/$DEV,$SYSLOGPORT@$SYSLOGADDR/$SYSLOGMACADDR "
/usr/bin/logger -p daemon.info -t netconsole: inserting netconsole module with arguments \
$SYSLOGOPTS
if [ -n "$SYSLOGOPTS" ]; then
action $"Initializing netconsole" modprobe netconsole \
$SYSLOGOPTS
[ "$?" != "0" ] && RETVAL=1
fi
touch /var/lock/subsys/netconsole
}
stop ()
{
if /sbin/lsmod | grep netconsole >/dev/null 2>&1 ; then
action $"Disabling netconsole" rmmod netconsole;
[ "$?" != "0" ] && RETVAL=1
fi
rm -f /var/lock/subsys/netconsole
}
status ()
{
if /sbin/lsmod | grep netconsole >/dev/null 2>&1 ; then
echo $"netconsole module loaded"
RETVAL=0
else
echo $"netconsole module not loaded"
RETVAL=3
fi
}
restart ()
{
stop
start
}
condrestart ()
{
[ -e /var/lock/subsys/netconsole ] && restart
}
case "$1" in
stop) stop ;;
status) status ;;
start|restart|reload|force-reload) restart ;;
condrestart) condrestart ;;
*) usage ;;
esac
exit $RETVAL