ubuntu 下的第一个自启动脚本——自动挂载Windows下的一个目录到Linux下

    由于最近找工作不是那么顺利,所以反思了一下可能是因为自己的知识也熟练程序还不够吧,所以决定一切从头再来,就像当初决定好好的学习一下Linux系统的基础知识一样。于是就将日常用的Windows换到Ubuntu下了,因为之前也已经在物理机上装了Linux系统,所以就 直接修改Grub就行了,这样就可以自动进入Linux系统了。

    刚开始时,还不习惯,因为不知道要干什么,所以就不知道自己该怎么做了。于是就像往常一样浏览网页,更新系统(sudo apt-get update && sudo apt-get upgrade)。后来就发现输入法不是很好用(ibus不习惯),就装上了Fcitx,后来发现Fcitx可以用了,但有时网页的右键与工具栏却不能用了,所以到网上查找,得知是Fcix与Firfox的兼容性问题,主要出现在使用中文,日语等地区等也不太清楚。

    刚开始时还是有点想回到Windows下,还在找一些Win7的主题(主要是看见Angle Beats 的主题太好了),所以就在网上下载了一些文件,可是这些文件下载后都得手动移动一下,当然了现在有GUI了,要方便些了,但我还是觉得这亲不是很方便,想自己写一个开机自启动的脚本文件,让我每次开机都可以自动挂载Windows下的一个经常用来存放下载文件的目录,到Linux下经常用来下载文件的、目录下的一个子目录中,于是就决心这样干了。。。。

     于是问题就来了:由于之前还没写过一个像样的shell脚本文件,所以不知道格式,于是就参照了一下/etc/init.d/rc.locale的写法,内容大致如下:(Windows下的下载目录在另一个扩展分区中,Ubuntu也安装扩展分区中)

#! /bin/sh

### BEGIN INIT INFO
# Provides:	filename
# Required-Start: 
# Required-Stop:
# Default-Start: 3 5
# Default-Stop:  0 1 2 4 6
# Short-Description: mount a directory to a new place in Linux
# Description:	mount / remount / umount the firectory. We mount the device firstly
#	and then mount --rbind  the directory ,the last we umount the device,so we can
#	mount the directory to the download foldery not the hole partion.
### END INIT INFO

# mount the windows formate parttion to the linux,this directory store the things
# we download form the internet in Linux not in windows,maybe sometimes we need
# do this.

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

. /lib/init/vars.sh
# . /lib/lsb/init-functions

# the vars we will use in the below functions
DEST_DEVICE=/mnt		# the destination directory in the linux,not a device
DEST_DIR=/home/username/download_files/windows

SCR_DEVICE=/dev/sda5
SCR_DIR="$DEST_DEVICE"/TDDOWNLOAD/Linux-download

do_mount()
{
#	echo "mounting ..../Linux_download to ~/download_files/windows" >&2
# test if the vars we defined correctly
#	echo "vars:dest_device = ($DEST_DEVICE) dest_dir = ($DEST_DIR)  \
#		scr_device = ($SCR_DEVICE) scr_dir = ($SCR_DIR)"

	mount -t ntfs "$SCR_DEVICE" "$DEST_DEVICE"
	mount --bind "$SCR_DIR" "$DEST_DIR"
	umount $DEST_DEVICE 2>/dev/null
}

do_remount()
{
#	echo "remount ..../Linux_download to ~/download_files/windows" >&2

	umount "$DEST_DIR" 2>/dev/null
	umount "$DEST_DEVICE" 2>/dev/null

	do_mount
}

do_unmount()
{
	umount "$DEST_DIR" >/dev/null 2>&1 
	umount "$DEST_DEVICE" >/dev/null 2>&1 
}

case $1 in
	start)
		do_mount
	# to test if this script running at setup
	#	echo "my_auto.... running" >/home/username/abc
		;;
	restart)
		do_remount
	#	echo "auto setup running again...." >/home/username/abc
		;;
	stop)
		do_unmount
	#	rm -rf /home/username/abc >/dev/null 2>&1
		exit 1
		;;
	reload|staue)
		echo "Usage: $0 not surpot $1...using (start | restart | stop)" >&2
		exit 2
		;;
	*)
		echo "Usage: $0 start|restart|stop" >&2
		exit 3
		;;

esac
脚本的大致内容就是这样了,其实基于Debian 的系统的Init Script 都有一个固定的格式,也可以说是一个标准吧,毕竟如果没有一个统一的标准大家写的不一亲,可能对于系统维护人员可就难了。标准格式在这:LSB Init Scripts。在这篇文章中也有几个链接,这些链接都是与LSB相关的,也可以去看了一下,顺便就把英文给学习了一下。

   上面代码中的一些注释可以用于测试用,如果遇到了一些不知道的问题,可以通过这些测试代码找到一些可能的原因,当初我就遇到脚本始终不运行的现象,后面会有原因说明与解决办法的。

    Init script 它的格式大概是这样的:第一行以:#! /bash/sh开始。然后以下面的形式开始一些必要的描述(LSB要求的):

### BEGIN INIT INFO
# Provides:	 filename
# Required-Start: 
# Required-Stop:
# Default-Start: start runlevel
# Default-Stop:  stop runlevel
# Short-Description: short description
# Description:	multiline description
#    more decription.
### END INIT INFO
除了 Description 行可以有多行外,其它的行LSB建议只用一行(Description从第二行注释开始要求以“#”开始,紧随其后至少有2个空格或1个TAB)。然后就是LSB要求该脚本至少要提供:start, stop, restart, force-reload, status 这几个行为。当然也可以根据自己需要减少一些,但一般建议不要这么做。如果要用到一些变量可以将 /lib/init/vars.sh 包含进来(用:. /lib/init/vars.sh ),如果要使用一些函数可以将 /lib/lsb/init-functions 包含进来。

/lib/init/vars.sh 的大致内容如下:

#
# Set rcS vars
#

[ -f /etc/default/rcS ] && . /etc/default/rcS || true

# check for bootoption 'noswap' and do not activate swap
# partitions/files when it is set.
if [ -r /proc/cmdline ] && grep -qw 'noswap' /proc/cmdline ; then
    NOSWAP=yes
else
    NOSWAP=no
fi

# Accept the same 'quiet' option as the kernel
if [ ! -e /proc/cmdline ] || egrep -qw 'quiet' /proc/cmdline ; then
    VERBOSE="no"
fi

# But allow both rcS and the kernel options 'quiet' to be overrided
# when INIT_VERBOSE=yes is used as well.
[ "$INIT_VERBOSE" ] && VERBOSE="$INIT_VERBOSE" || true

/lib/lsb/init-functions 的大致内容如下:

# /lib/lsb/init-functions for Debian -*- shell-script -*-
#
#Copyright (c) 2002-08 Chris Lawrence
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions
#are met:
#1. Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
#2. Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
#3. Neither the name of the author nor the names of other contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
#IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
#BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
#OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
#EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

start_daemon () {
    local force nice pidfile exec i args
    force=0
    nice=0
    pidfile=/dev/null

    OPTIND=1
    while getopts fn:p: opt ; do
        case "$opt" in
            f)  force=1;;
            n)  nice="$OPTARG";;
            p)  pidfile="$OPTARG";;
        esac
    done
    
    shift $(($OPTIND - 1))
    if [ "$1" = '--' ]; then
        shift
    fi

    exec="$1"; shift

    args="--start --nicelevel $nice --quiet --oknodo"
    if [ $force = 1 ]; then
        /sbin/start-stop-daemon $args --chdir "$PWD" --startas $exec --pidfile /dev/null -- "$@"
    elif [ $pidfile ]; then
        /sbin/start-stop-daemon $args --chdir "$PWD" --exec $exec --oknodo --pidfile "$pidfile" -- "$@"
    else
        /sbin/start-stop-daemon $args --chdir "$PWD" --exec $exec -- "$@"
    fi
}

pidofproc () {
    local pidfile line i pids= status specified pid
    pidfile=
    specified=
    
    OPTIND=1
    while getopts p: opt ; do
        case "$opt" in
            p)  pidfile="$OPTARG"; specified=1;;
        esac
    done
    shift $(($OPTIND - 1))

    base=${1##*/}
    if [ ! "$specified" ]; then
        pidfile="/var/run/$base.pid"
    fi

    if [ -n "${pidfile:-}" -a -r "$pidfile" ]; then
        read pid < "$pidfile"
        if [ -n "${pid:-}" ]; then
            if $(kill -0 "${pid:-}" 2> /dev/null); then
                echo "$pid"
                return 0
            elif ps "${pid:-}" >/dev/null 2>&1; then
                echo "$pid"
                return 0 # program is running, but not owned by this user
            else
                return 1 # program is dead and /var/run pid file exists
            fi
        fi
    fi
    if [ "$specified" = 1 ]; then
        if [ -e "$pidfile" -a ! -r "$pidfile" ]; then
            return 4 # pidfile exists, but unreadable, return unknown
        else
            return 3 # pidfile specified, but contains no PID to test
        fi
    fi
    if [ -x /bin/pidof ]; then
        status="0"
        /bin/pidof -o %PPID -x $1 || status="$?"
        if [ "$status" = 1 ]; then
            return 3 # program is not running
        fi
        return 0
    fi
    return 4 # Unable to determine status
}

# start-stop-daemon uses the same algorithm as "pidofproc" above.
killproc () {
    local pidfile sig status base i name_param is_term_sig
    pidfile=
    name_param=
    is_term_sig=no

    OPTIND=1
    while getopts p: opt ; do
        case "$opt" in
            p)  pidfile="$OPTARG";;
        esac
    done
    shift $(($OPTIND - 1))

    base=${1##*/}
    if [ ! $pidfile ]; then
        name_param="--name $base --pidfile /var/run/$base.pid"
    else
        name_param="--pidfile $pidfile"
    fi

    sig=$(echo ${2:-} | sed -e 's/^-\(.*\)/\1/')
    sig=$(echo $sig | sed -e 's/^SIG\(.*\)/\1/')
    if [ -z "$sig" -o "$sig" = 15 -o "$sig" = TERM ]; then
        is_term_sig=yes
    fi
    status=0
    if [ ! "$is_term_sig" = yes ]; then
        if [ -n "$sig" ]; then
            /sbin/start-stop-daemon --stop --signal "$sig" --quiet $name_param || status="$?"
        else
            /sbin/start-stop-daemon --stop --quiet $name_param || status="$?"
        fi
    else
        /sbin/start-stop-daemon --stop --quiet --oknodo $name_param || status="$?"
    fi
    if [ "$status" = 1 ]; then
        if [ -n "$sig" ]; then
            return 0
        fi
        return 3 # program is not running
    fi

    if [ "$status" = 0 -a "$is_term_sig" = yes -a "$pidfile" ]; then
        pidofproc -p "$pidfile" "$1" >/dev/null || rm -f "$pidfile"
    fi
    return 0
}

# Return LSB status
status_of_proc () {
    local pidfile daemon name status

    pidfile=
    OPTIND=1
    while getopts p: opt ; do
        case "$opt" in
            p)  pidfile="$OPTARG";;
        esac
    done
    shift $(($OPTIND - 1))

    if [ -n "$pidfile" ]; then
        pidfile="-p $pidfile"
    fi
    daemon="$1"
    name="$2"

    status="0"
    pidofproc $pidfile $daemon >/dev/null || status="$?"
    if [ "$status" = 0 ]; then
        log_success_msg "$name is running"
        return 0
    elif [ "$status" = 4 ]; then
        log_failure_msg "could not access PID file for $name"
        return $status
    else
        log_failure_msg "$name is not running"
        return $status
    fi
}

log_use_fancy_output () {
    TPUT=/usr/bin/tput
    EXPR=/usr/bin/expr
    if [ -t 1 ] && [ "x${TERM:-}" != "x" ] && [ "x${TERM:-}" != "xdumb" ] && [ -x $TPUT ] && [ -x $EXPR ] && $TPUT hpa 60 >/dev/null 2>&1 && $TPUT setaf 1 >/dev/null 2>&1; then
        [ -z $FANCYTTY ] && FANCYTTY=1 || true
    else
        FANCYTTY=0
    fi
    case "$FANCYTTY" in
        1|Y|yes|true)   true;;
        *)              false;;
    esac
}

log_success_msg () {
    if [ -n "${1:-}" ]; then
        log_begin_msg $@
    fi
    log_end_msg 0
}

log_failure_msg () {
    if [ -n "${1:-}" ]; then
        log_begin_msg $@ "..."
    fi
    log_end_msg 1 || true
}

log_warning_msg () {
    if [ -n "${1:-}" ]; then
        log_begin_msg $@ "..."
    fi
    log_end_msg 255 || true
}

#
# NON-LSB HELPER FUNCTIONS
#
# int get_lsb_header_val (char *scriptpathname, char *key)
get_lsb_header_val () {
        if [ ! -f "$1" ] || [ -z "${2:-}" ]; then
                return 1
        fi
        LSB_S="### BEGIN INIT INFO"
        LSB_E="### END INIT INFO"
        sed -n "/$LSB_S/,/$LSB_E/ s/# $2: \(.*\)/\1/p" $1
}

# int log_begin_message (char *message)
log_begin_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    echo -n "$@"
}

# Sample usage:
# log_daemon_msg "Starting GNOME Login Manager" "gdm"
#
# On Debian, would output "Starting GNOME Login Manager: gdm"
# On Ubuntu, would output " * Starting GNOME Login Manager..."
#
# If the second argument is omitted, logging suitable for use with
# log_progress_msg() is used:
#
# log_daemon_msg "Starting remote filesystem services"
#
# On Debian, would output "Starting remote filesystem services:"
# On Ubuntu, would output " * Starting remote filesystem services..."

log_daemon_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    log_daemon_msg_pre "$@"

    if [ -z "${2:-}" ]; then
        echo -n "$1:"
        return
    fi
    
    echo -n "$1: $2"
    log_daemon_msg_post "$@"
}

# #319739
#
# Per policy docs:
#
#     log_daemon_msg "Starting remote file system services"
#     log_progress_msg "nfsd"; start-stop-daemon --start --quiet nfsd
#     log_progress_msg "mountd"; start-stop-daemon --start --quiet mountd
#     log_progress_msg "ugidd"; start-stop-daemon --start --quiet ugidd
#     log_end_msg 0
#
# You could also do something fancy with log_end_msg here based on the
# return values of start-stop-daemon; this is left as an exercise for
# the reader...
#
# On Ubuntu, one would expect log_progress_msg to be a no-op.
log_progress_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    echo -n " $@"
}


# int log_end_message (int exitstatus)
log_end_msg () {
    # If no arguments were passed, return
    if [ -z "${1:-}" ]; then
        return 1
    fi

    retval=$1

    log_end_msg_pre "$@"

    # Only do the fancy stuff if we have an appropriate terminal
    # and if /usr is already mounted
    if log_use_fancy_output; then
        RED=`$TPUT setaf 1`
        YELLOW=`$TPUT setaf 3`
        NORMAL=`$TPUT op`
    else
        RED=''
        YELLOW=''
        NORMAL=''
    fi

    if [ $1 -eq 0 ]; then
        echo "."
    elif [ $1 -eq 255 ]; then
        /bin/echo -e " ${YELLOW}(warning).${NORMAL}"
    else
        /bin/echo -e " ${RED}failed!${NORMAL}"
    fi
    log_end_msg_post "$@"
    return $retval
}

log_action_msg () {
    echo "$@."
}

log_action_begin_msg () {
    echo -n "$@..."
}

log_action_cont_msg () {
    echo -n "$@..."
}

log_action_end_msg () {
    log_action_end_msg_pre "$@"
    if [ -z "${2:-}" ]; then
        end="."
    else
        end=" ($2)."
    fi

    if [ $1 -eq 0 ]; then
        echo "done${end}"
    else
        if log_use_fancy_output; then
            RED=`$TPUT setaf 1`
            NORMAL=`$TPUT op`
            /bin/echo -e "${RED}failed${end}${NORMAL}"
        else
            echo "failed${end}"
        fi
    fi
    log_action_end_msg_post "$@"
}

# Hooks for /etc/lsb-base-logging.sh
log_daemon_msg_pre () { :; }
log_daemon_msg_post () { :; }
log_end_msg_pre () { :; }
log_end_msg_post () { :; }
log_action_end_msg_pre () { :; }
log_action_end_msg_post () { :; }

FANCYTTY=
[ -e /etc/lsb-base-logging.sh ] && . /etc/lsb-base-logging.sh || true
如果用不到里面的东西可以不包含这两个文件,一般为了防止不知为什么的错误还是可以包含进来的,当然适合才是最好的。

    最后是最关键的就是写自己的实现过程中,这里面一般都是用 case ... esac 来实现功能,而在这个循环中,又一般的用前面我们已经定义好的一些诸如: do_start, process_options之类的函数进行实现,函数名一般都是:动作_动词 或动作_状态 等能说明目的的名称,就像C语言中的函数命名一样。

    一个Init script 大概的内容就是这样的了。当然复杂的脚本文件一般都是有安全性检查与文件存在性的测试等,我这个脚本只是实现一个简单的挂载目录功能,完全可以手动完成的。。^_^。所以就方便了。

    假设我们现在已经写好了一个 shell 脚本,保存,在终端中运行 :sh -n filename试运行一下,看一下有没有语法上的错误,如果没有,可以再次运行命令:shell -v filename 看一下脚本执行的一个大致过程与我们预想的是不是一样的。如果不一样那么就要修改了。

    假设现在我们的脚本文件没有语法上的问题,也满足我们的要求了,也就是一切都是正常的,那么现在就可以将它“安装”到系统中了。利用 ubuntu 下的 update-rc.d 命令可以将我们写的脚本文件“安装”到正确的位置(注:因为我不知道其它的系统下是否提供 update-rc.d 的命令,所以这里先这样做)。Ubuntu 系统下以 root 身份运行 "update-rc.d filename start NN runlevels . stop NN runlevels ."或运行:“sudo update-rc.d filename start NN runlevels . stop NN runlevels .”。注意里面的点号,前后都要有一个空格,一共有两个点号。其中NN为启动时的顺序,数字越小就越早运行。runlevels可以是多个运行级别,多个运行级时之间用空格分开。如果是在基于Debian 的系统,这里要特点注意一下:基于Debian 的系统运行级别:2~5都是一样的,都是有GUI的多用户的环境,这与Linux 标准不完全一样,所以在决定脚本在哪个运行级别下启动或停止时要特点注意运行级别。之前我的系统(ubuntu 10.04)的运行级别就是2,不管我怎么修改都不能实现脚本的功能,后来将其加入到 rc.local 中就可以,但也没有满足我的需求啊。(注:可以用:who -r命令查看系统的运行级别也可以用 runlevel)。于是我就将运行级别改为5(利用:sudo init 5 可以马上临时修改运行级别,如果要“永久性”的修改运行级别可以修改:/etc/init/rc-sysinit.conf中的第14行的:env DEFAULT_RUNLEVEL=2 将2改为5即可,其它系统可以先查看:/etc/inittab文件中是否有:id:3:initdefault:之类的描述,将3改为想运行的运行级别即可),然后呢就成功了。。^_^

    运行 update-rc.d 后会出现警告消息,大致意思是说:你的脚本文件与LSB的标准不是完全相同,主要就是在运行级别上。比如前面我的这个脚本只在运行级别为3与5的情况下运行,而不是2,3,4,5所以它会给出警告消息。不用管也不会存在什么 问题。

    如果系统中没有 update-rc.d 命令可以手动用 ln -s建立软链接到相应的目录(具体用法查看 man手册。)。比如:

cd /etc/rc5.d
sudo ln -s ../init.d/filename S99filename
这样也可以达到我们的目的(让系统帮我们启动脚本运行,^_^),只是现在我还不知道在其它系统上有没有 update-rc.d 的命令,如果没有除了前面用的那个命令外,我就不知道其它的比较方便的方法了。^@^。

    由于现在才开始学习 Linux 的一些基础的东西 ,所以难免文章里面有很多地方写的不是很好,希望知道的朋友斧正。。。^_^.……

    学习新东西并不难,难于在不愿意去尝试,只要愿意去尝试,就有可能自己解决问题,如果没有尝试就认为会失败或是 完成不了,那么就真的完成不了。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值