shell编程-8

shell学习第八天

  • shell学习第八天
    • 1.shift命令
      • 1.基本用法
      • 2.例子
      • 3.注意事项
      • 4.跑一个脚本,一目了然
    • 2.间接引用
    • 3.切片
    • 4.搞明白network脚本
    • 5.计划任务
      • 1.什么是计划任务?
      • 2.基本规则
      • 3.写个备份脚本
      • 4.计划任务背后的规则
      • 5.我们创建的计划任务放到哪里的?crond如果知道的?
      • 6.如果知道crond是否执行计划任务?
      • 7.黑客是否在你的机器里增加了计划任务,你如何知道?
      • 8.要知道几个文件夹 在/etc/下
    • 6.练习
    • 7.忘记的小知识点
      • 1.bash执行的4种方式
      • 2.date的用法
      • 3.tar的用法
      • 4.find的用法
      • 5.小建议

shell学习第八天

1.shift命令

在Linux中,shift 是一个用于处理命令行参数(位置参数)的Shell内建命令。它的主要作用是移动命令行参数的位置。

1.基本用法

shift [n]
  • 如果没有提供参数 n,默认为 1
  • n 表示将命令行参数向左移动的位置数。

2.例子

让我们以一个简单的脚本为例,演示 shift 的用法:

#!/bin/bash

echo "Before shift: \$1=$1, \$2=$2, \$3=$3"

# 使用 shift 移动两个位置
shift 2

echo "After shift: \$1=$1, \$2=$2, \$3=$3"

如果你运行这个脚本并提供三个参数:

./example.sh one two three

输出将是:

Before shift: $1=one, $2=two, $3=three
After shift: $1=three, $2=, $3=

在这个例子中,shift 2 将命令行参数向左移动了两个位置,因此 $1 变成了原来的 $3,而 $2$3 变成了空白。

3.注意事项

  1. shift 在脚本中通常用于处理可变数量的命令行参数。通过反复使用 shift,你可以逐步处理所有的命令行参数。

  2. 如果 shift 移动的位置数大于当前剩余的命令行参数数,剩余的参数会变成空。

  3. shift 不会改变 $0(脚本名称)和 $#(命令行参数的总数)。

  4. shift 在循环中使用时,可以用来处理可变数量的参数。例如,一个脚本需要处理不同数量的参数,可以使用循环结构和 shift 动态地处理这些参数。

while [ "$#" -gt 0 ]; do
  case "$1" in
    -a)
      # 处理参数 -a
      shift
      ;;
    -b)
      # 处理参数 -b
      shift
      ;;
    *)
      # 处理其他参数
      shift
      ;;
  esac
done

这样,shift 可以在循环中帮助我们逐步处理不同的命令行参数。

4.跑一个脚本,一目了然

[root@gh-shell 1-27] vim run.sh
[root@gh-shell 1-27] cat run.sh 
#!/bin/bash

while(( $# !=0 ))
do
	echo "第一个参数为 $1,参数的个数是 $# ,所有的位置变量内容是 $@"
	#删除第一个位置变量,将所有的位置变量左移
	shift
done
[root@gh-shell 1-27]# bash run.sh gaohui gaofei huya junxue tangrong changai
第一个参数为 gaohui,参数的个数是 6 ,所有的位置变量内容是 gaohui gaofei huya junxue tangrong changai
第一个参数为 gaofei,参数的个数是 5 ,所有的位置变量内容是 gaofei huya junxue tangrong changai
第一个参数为 huya,参数的个数是 4 ,所有的位置变量内容是 huya junxue tangrong changai
第一个参数为 junxue,参数的个数是 3 ,所有的位置变量内容是 junxue tangrong changai
第一个参数为 tangrong,参数的个数是 2 ,所有的位置变量内容是 tangrong changai
第一个参数为 changai,参数的个数是 1 ,所有的位置变量内容是 changai
[root@gh-shell 1-27]# 

2.间接引用

执行了两遍,取了两次值

[root@gh-shell 1-27] a=b
[root@gh-shell 1-27] b=10
[root@gh-shell 1-27] echo $a
b
[root@gh-shell 1-27] python3
Python 3.6.8 (default, Nov 14 2023, 16:29:52) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a=10
>>> b=a
>>> b
10
>>> 
>>> exit()

总体来说,这个例子展示了在Shell和Python中如何创建变量、赋值以及查看变量的值。在Python中,变量的类型可以动态变化,而在Shell中,变量的类型通常由赋值的内容决定。

[root@gh-shell 1-27] echo ${!a}
10
[root@gh-shell 1-27]# 

!a表示 先取b的值

再把$b取值 相当于取了两次值---->间接引用

想到eval命令—>让bash解释2次,这样可以执行2次命令

[root@gh-shell 1-27] a='cat cc.txt'
[root@gh-shell 1-27] vim cc.txt
[root@gh-shell 1-27] cat cc.txt 
sanchuang
[root@gh-shell 1-27] echo $a
cat cc.txt
[root@gh-shell 1-27] eval $a
sanchuang
[root@gh-shell 1-27]# 

3.切片

bash自带的处理字符串
echo ${#a} $a中字符串的个数
echo ${a:3} $a中从第三个开始取直到结束
echo ${a:3:2}$a中从第三个开始去两个字符
echo ${a: -1}取最后一个字符
echo ${a:-}  如果a变量不存在就输出空值
echo ${a:-"sanchuang"}  如果a变量不存在就输出sanchuang,如果a变量有值就输出a的值
[root@web1 init.d]# a=123
[root@web1 init.d]# b=${a:-"sanchuang"}
[root@web1 init.d]# echo $b
123
[root@web1 init.d]# 
[root@web1 init.d]# a=
[root@web1 init.d]# b=${a:-"sanchuang"}
[root@web1 init.d]# echo $b
sanchuang
[root@web1 init.d]#
echo ${a#*.} 从左边开始删除直到遇到.为止
echo ${a##*.} 从左边开始删除直到遇到最后一个.为止
echo ${a%.*}从右面开始删除直到遇到.为止
echo ${a%%.*}从右面开始删除直到遇到最后一个.为止
echo ${a/abc/111} 将变量a中的第一个abc替换成111
echo ${a//abc/xyz}将变量a中的所有的abc替换成xyz

4.搞明白network脚本

cd /etc/init.d/ '里边有一个network脚本'
. /etc/init.d/functions		'执行 /etc/init.d/functions 这个脚本 在当前shell中执行'

if [ ! -f /etc/sysconfig/network ]; then  '-f是判断文件存不存在,如果/etc/sysconfig/network 不存在 就退出脚本返回6'
    exit 6
fi

. /etc/sysconfig/network '执行 /etc/sysconfig/network 脚本'

if [ -f /etc/sysconfig/pcmcia ]; then '如果/etc/sysconfig/pcmcia 存在 就执行. /etc/sysconfig/pcmcia'
    . /etc/sysconfig/pcmcia
fi
# Check that networking is up.  '检查网络是否正常'
[ "${NETWORKING}" = "no" ] && exit 6  '判断NETWORKING这个变量是否等于no,并且返回值是否为6'

# if the ip configuration utility isn't around we can't function. '如果没有IP配置工具,我们就无法正常工作。'
[ -x /sbin/ip ] || exit 1  '判断/sbin/ip是否可以执行 或者 返回值是否为1'


CWD=$(pwd) '这个命令用于获取当前工作目录(Current Working Directory)并将其保存在变量 CWD 中。'
cd /etc/sysconfig/network-scripts '进入/etc/sysconfig/network-scripts 这个目录'

. ./network-functions '执行当前目录下的network-functions脚本'

# find all the interfaces besides loopback. '查找除loopback以外的所有接口。'
# ignore aliases, alternative configurations, and editor backup files '忽略别名、可选配置和编辑器备份文件'
interfaces=$(ls ifcfg-* | \
        LC_ALL=C sed -e "$__sed_discard_ignored_files" \
               -e '/\(ifcfg-lo$\|:\|ifcfg-.*-range\)/d' \
               -e '{ s/^ifcfg-//g;s/[0-9]/ &/}' | \
        LC_ALL=C sort -k 1,1 -k 2n | \
        LC_ALL=C sed 's/ //')
rc=0

解释一下:interfaces

每一行后边的\是单纯的续航

ls ifcfg-* 查找当前文件夹下ifcfg-开头的文件

LC_ALL=C 这是设置环境变量 LC_ALL 的部分。LC_ALL 是用来设置所有语言环境的环境变量。在这里,通过将其设置为 C,强制使用默认的 “C” 或 “POSIX” 语言环境。这通常用于确保在不同的系统上得到一致的结果,以避免与本地化相关的问题。

sed -e "$__sed_discard_ignored_files"  -e '/\(ifcfg-lo$\|:\|ifcfg-.*-range\)/d' -e '{ s/^ifcfg-//g;s/[0-9]/ &/}'

-e "$__sed_discard_ignored_files":  其实就是传进来一个变量

这部分使用 -e 选项指定了 sed 的一个编辑脚本。
$__sed_discard_ignored_files 是一个变量,其中包含了一些 sed 模式匹配规则,用于指定要排除(丢弃)的文件名模式。
例如,如果 $__sed_discard_ignored_files 的内容是 "/pattern1/d;/pattern2/d;...",那么这部分的作用是根据定义的模式规则丢弃匹配的行(文件名)。
-e '/\(ifcfg-lo$\|:\|ifcfg-.*-range\)/d':

这是另一个 -e 选项,指定了另一个 sed 的编辑脚本。
/\(ifcfg-lo$\|:\|ifcfg-.*-range\)/d 是一个模式,它使用正则表达式匹配文件名。具体:
\(ifcfg-lo$\|:\|ifcfg-.*-range\):这是一个正则表达式组,它匹配三种模式之一:
ifcfg-lo$:匹配以 "ifcfg-lo" 结尾的文件名。
::匹配包含 ":" 的文件名。
ifcfg-.*-range:匹配以 "ifcfg-" 开头,以 "-range" 结尾的文件名。
/d:这是 sed 的删除命令,它删除与模式匹配的行(文件名)。
-e '{ s/^ifcfg-//g;s/[0-9]/ &/}':

这是第三个 -e 选项,指定了另一个 sed 的编辑脚本。
这部分主要包含两个 sed 替换命令:
s/^ifcfg-//g:将行中开头的 "ifcfg-" 替换为空字符串,即删除 "ifcfg-" 前缀。
s/[0-9]/ &/:将行中的每个数字前插入一个空格,即在数字前添加一个空格。

LC_ALL=C sort -k 1,1 -k 2n

  1. sort
    • sort 命令用于对文本进行排序。
  2. -k 1,1 -k 2n
    • -k 1,1:表示按照第一个字段进行排序。这里 1,1 意味着只使用第一个字段进行排序。
    • -k 2n:表示按照第二个字段进行数值排序。这里 2n 意味着使用第二个字段,并将其解释为数字进行排序。

综合起来,LC_ALL=C sort -k 1,1 -k 2n 的作用是使用 sort 对文本进行排序:

  • 首先按照第一个字段进行字母顺序排序。
  • 如果两行的第一个字段相同,则按照第二个字段进行数值顺序排序。

sed 's/ //' 这是一个 sed 替换命令,其模式为 s/ //,表示将每一行中的第一个空格替换为空字符串。

# See how we were called. '看看我们是怎么被称呼的'
case "$1" in     'case选项通过位置变量1'
start) 								'如果输入的是start'
    [ "$EUID" != "0" ] && exit 4	   '如果变量EUID不等于0并且返回值为4'
    rc=0							 'rc是return code 等于0'
    # IPv6 hook (pre IPv4 start)	''
    if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then   '如果这个文件有可执行权限init.ipv6-global'
        /etc/sysconfig/network-scripts/init.ipv6-global start pre     'start和pre是两个参数 相当于$1$2'
    fi

    apply_sysctl   'apply_sysctl 可能是一个用于应用系统参数(sysctl)的函数或脚本。'

    #tell NM to reload its configuration '告诉NM重新加载它的配置'
    if [ "$(LANG=C nmcli -t --fields running general status 2>/dev/null)" = "running" ]; then 
        nmcli connection reload  '重新加载网络连接配置'
    fi

    # bring up loopback interface '启动环回接口'
    action $"Bringing up loopback interface: " ./ifup ifcfg-lo 

    case "$VLAN" in '如果$VLAN的值为yes'
    yes)
        if [ ! -d /proc/net/vlan ] && ! modprobe 8021q >/dev/null 2>&1 ; then
            net_log $"No 802.1Q VLAN support available in kernel."
        fi
        ;;
    esac

    vlaninterfaces=""
    interfaces=""
    xdslinterfaces=""
    bridgeinterfaces=""

“$(LANG=C nmcli -t --fields running general status 2>/dev/null)” = “running”

这是一个 Bash Shell 中的条件表达式,用于判断 NetworkManager 服务是否正在运行。让我解释这个表达式的不同部分:

  1. LANG=C

    • 这是一个环境变量设置,将语言环境设置为 “C” 或 “POSIX”。
    • 在这里使用的目的是确保 nmcli 命令输出的文本是在一个固定的、不受本地化影响的语言环境下产生的。这是为了避免在不同系统环境下输出格式发生变化。
  2. nmcli -t --fields running general status

    • 这是一个 nmcli 命令,用于获取 NetworkManager 的运行状态。
    • -t 选项表示使用制表符分隔的输出格式。
    • --fields running 表示仅显示 “running” 字段的值。
    • general statusnmcli 命令的参数,指定要获取的信息类型。
  3. 2>/dev/null

    • 这是一个重定向操作符,将标准错误(stderr)输出重定向到 /dev/null,以忽略任何错误消息。这样做是为了避免在检查 NetworkManager 运行状态时输出错误消息到标准错误。
  4. "$(...) = "running"

    • $() 是命令替换语法,用于执行括号中的命令,并将其输出作为字符串返回。
    • "$(LANG=C nmcli -t --fields running general status 2>/dev/null)" 表示执行 nmcli 命令,获取 NetworkManager 运行状态,并将其作为字符串进行比较。
    • = 是字符串比较运算符,判断命令输出的字符串是否等于 “running”。

综合起来,整个表达式的作用是判断 NetworkManager 服务是否正在运行。如果运行状态为 “running”,则整个条件表达式为真。这通常用于在脚本中进行条件性的操作,例如重新加载配置或执行其他操作,以确保 NetworkManager 处于正常运行状态。

action $"Bringing up loopback interface: " ./ifup ifcfg-lo

  1. action $"Bringing up loopback interface: "
    • 这一部分通常是调用系统的 init 脚本中的 action 函数,用于在控制台上显示一个带有动作标签的消息。
    • $"Bringing up loopback interface: " 是一个用于国际化的字符串,$ 是用于本地化的标记,"Bringing up loopback interface: " 是带有动作标签的消息。
  2. ./ifup ifcfg-lo
    • 这是一个相对路径的命令,调用 ifup 程序并传递了一个参数 ifcfg-lo
    • ifup 通常是一个用于启动网络接口的工具,而 ifcfg-lo 可能是一个配置文件,指定了 loopback 接口的相关配置信息。

综合起来,整个命令的意义可能是:

  • 使用 action 函数显示带有动作标签的消息,该消息是 "Bringing up loopback interface: "。
  • 调用 ifup 工具,以启动 loopback 接口,并且可能通过 ifcfg-lo 参数指定了相关的配置文件。
case "$VLAN" in '如果$VLAN的值为yes'
    yes)
        if [ ! -d /proc/net/vlan ] && ! modprobe 8021q >/dev/null 2>&1 ; then
            net_log $"No 802.1Q VLAN support available in kernel."
        fi
        ;;
    esac

    vlaninterfaces=""
    interfaces=""
    xdslinterfaces=""
    bridgeinterfaces=""

if [ ! -d /proc/net/vlan ] && ! modprobe 8021q >/dev/null 2>&1 ; then

  • 这是在 $VLAN 的值为 “yes” 时执行的代码块。

    ​ if语句用于检查以下两个条件:

    • ! -d /proc/net/vlan:检查 /proc/net/vlan 目录是否不存在。
    • ! modprobe 8021q >/dev/null 2>&1:尝试加载内核模块 8021q,并检查是否成功。>/dev/null 2>&1 用于将输出和错误重定向到 /dev/null,即忽略输出和错误。
  • 如果任一条件为真(即目录不存在或加载模块失败),则执行 net_log $"No 802.1Q VLAN support available in kernel."

  • net_log 函数可能用于记录一些网络相关的消息。

vlaninterfaces, interfaces, xdslinterfaces,bridgeinterfaces 定义这四个变量为空。

综合起来,整个代码片段的作用可能是:

  • 如果 $VLAN 的值是 “yes”,则检查是否存在 VLAN 支持的必要条件(特定目录和加载模块),并在条件不满足时记录相关消息。
  • 无论 $VLAN 的值是什么,都将一些变量赋值为空字符串。这可能是为了确保这些变量在后续的脚本中处于已定义的状态,即使在不需要 VLAN 的情况下也是如此。
 # bring up all other interfaces configured to come up at boot time '调出配置为在引导时启动的所有其他接口'
    for i in $interfaces; do	'for循环变量i去取interfaces的值'
        unset DEVICE TYPE SLAVE NM_CONTROLLED	'取消变量的值'
        eval $(LANG=C grep -F "DEVICE=" ifcfg-$i)	'执行$()最后生成的命令 -F是固定的字符串/正则,用于从 ifcfg-$i 文件中读取网络接口的配置信息,并将其设置为对应的变量。下同'
        eval $(LANG=C grep -F "TYPE=" ifcfg-$i)
        eval $(LANG=C grep -F "SLAVE=" ifcfg-$i)
        eval $(LANG=C grep -F "NM_CONTROLLED=" ifcfg-$i)

        if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi

        if [ "$SLAVE" = "yes" ] && ( ! is_nm_running || is_false $NM_CONTROLLED ) ; then
            continue
        fi

        if [ "${DEVICE##cipcb}" != "$DEVICE" ] ; then
            interfaces="$interfaces $i"
            continue
        fi
        if [ "$TYPE" = "xDSL"  -o  "$TYPE" = "Modem" ]; then
            xdslinterfaces="$xdslinterfaces $i"
            continue
        fi

        if [ "$TYPE" = "Bridge" ]; then
            bridgeinterfaces="$bridgeinterfaces $i"
            continue
        fi
        if [ "$TYPE" = "IPSEC" ] || [ "$TYPE" = "IPIP" ] || [ "$TYPE" = "GRE" ]; then
            interfaces="$interfaces $i"
            continue
        fi

        if [ "${DEVICE%%.*}" != "$DEVICE"  -o  "${DEVICE##vlan}" != "$DEVICE" ] ; then
			vlaninterfaces="$vlaninterfaces $i"
            continue
        fi

        if LANG=C grep -EL "^ONBOOT=['\"]?[Nn][Oo]['\"]?" ifcfg-$i > /dev/null ; then
            # this loads the module, to preserve ordering
            is_available $i
            continue
        fi
        action $"Bringing up interface $i: " ./ifup $i boot
        [ $? -ne 0 ] && rc=1
    done

条件判断和处理不同类型的网络接口:

  • 如果 SLAVE 为 “yes” 且 NetworkManager 没有运行或者 NM_CONTROLLED 为假,则跳过当前循环。
  • 如果接口的名称以 “cipcb” 开头,将其添加到 interfaces 中。
  • 如果接口的类型为 “xDSL” 或 “Modem”,将其添加到 xdslinterfaces 中。
  • 如果接口的类型为 “Bridge”,将其添加到 bridgeinterfaces 中。
  • 如果接口的类型为 “IPSEC”、“IPIP” 或 “GRE”,将其添加到 interfaces 中。
  • 如果接口的名称包含 “.” 或以 “vlan” 开头,将其添加到 vlaninterfaces 中。
  • 如果配置文件中指定接口在启动时不启用(ONBOOT 为 “NO”),则加载相应的模块。
  1. action $"Bringing up interface $i: " ./ifup $i boot
    • 在最后的条件判断后,如果没有满足上述特殊类型的接口,将接口启动,并通过 action 函数显示带有动作标签的消息。
    • ./ifup $i boot 是执行启动接口的命令。
  2. [ $? -ne 0 ] && rc=1
    • 如果上述启动接口的命令返回值不为 0,将 rc 变量设置为 1,表示有错误发生。
 # Bring up xDSL and VPN interfaces
    for i in $vlaninterfaces $bridgeinterfaces $xdslinterfaces $interfaces ; do
        if ! LANG=C grep -EL "^ONBOOT=['\"]?[Nn][Oo]['\"]?" ifcfg-$i >/dev/null 2>&1 ; then
            action $"Bringing up interface $i: " ./ifup $i boot
            [ $? -ne 0 ] && rc=1
        fi
    done

    # Add non interface-specific static-routes.
    if [ -f /etc/sysconfig/static-routes ]; then
        if [ -x /sbin/route ]; then
            grep "^any" /etc/sysconfig/static-routes | while read ignore args ; do
                /sbin/route add -$args
            done
        else
            net_log $"Legacy static-route support not available: /sbin/route not found"
        fi
    fi

  • 通过for循环迭代处理不同类型的网络接口,包括VLAN、桥接、xDSL和VPN。

  • 使用grep命令检查对应接口的配置文件(ifcfg-$i)中是否设置了ONBOOT参数为"NO"。LANG=C设置环境语言为英文,确保在检查文件时不受语言环境的影响。

  • 如果未找到包含"ONBOOT=‘NO’"的行,则执行./ifup $i boot命令以启动该接口。action命令用于记录日志。

  • 如果接口启动不成功([ $? -ne 0 ]),将rc变量设置为1,表示发生错误。

  • 检查是否存在静态路由配置文件/etc/sysconfig/static-routes

  • 如果文件存在,并且/sbin/route(route命令)可执行,使用grep命令找到以"any"开头的行。

  • 对于每一行,使用/sbin/route add命令添加静态路由,参数为从配置文件中读取的args

  • 如果/sbin/route不可执行,记录一条消息指示"Legacy static-route support not available: /sbin/route not found"。

    # IPv6 hook (post IPv4 start)
    if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then   '如果这个文件有可执行权限'
        /etc/sysconfig/network-scripts/init.ipv6-global start post	  '执行这个脚本,并且传递参数start post'
    fi
    # Run this again to catch any interface-specific actions
    apply_sysctl	'运行这个函数'

    touch /var/lock/subsys/network	'创建空文件'

    [ -n "${NETWORKDELAY}" ] && /bin/sleep ${NETWORKDELAY}	'判断字符串${NETWORKDELAY}是否为空,如果环境变量 NETWORKDELAY 非空,那么执行 sleep 命令'
    ;;
stop)
    [ "$EUID" != "0" ] && exit 4 '如果EUID不为0,就返回4'
    # Don't shut the network down if root or /usr is on NFS or a network
    # block device.
    root_fstype=$(gawk '{ if ($1 !~ /^[ \t]*#/ && $2 == "/"    && $3 != "rootfs") { print $3; }}' /proc/mounts)
    usr_fstype=$(gawk  '{ if ($1 !~ /^[ \t]*#/ && $2 == "/usr" && $3 != "rootfs") { print $3; }}' /proc/mounts)
	'获取根目录和 /usr 目录当前所使用的文件系统类型。'
	
    if [[ "${root_fstype}" == nfs* || "${usr_fstype}" == nfs* ]] || systemctl show --property=RequiredBy -- -.mount usr.mount | grep -q 'remote-fs.target' ; then
        net_log $"rootfs or /usr is on network filesystem, leaving network up"
        exit 1
    fi

    unset root_fstype usr_fstype

 if [[ "${root_fstype}" == nfs* || "${usr_fstype}" == nfs* ]] || systemctl show --property=RequiredBy -- -.mount usr.mount | grep -q 'remote-fs.target' ; then
        net_log $"rootfs or /usr is on network filesystem, leaving network up"
        exit 1
    fi

检测这个两个变量root_fstype,usr_fstype是否以nfs开头或者通过 systemctl show 命令检查 .mountusr.mount 单元之间的依赖关系,并使用 grep 命令检查是否存在 remote-fs.target

  • 如果条件成立,即根文件系统或 /usr 文件系统是网络文件系统,或者存在与 remote-fs.target 相关的 systemd 单元依赖,那么记录一条消息表示 “rootfs or /usr is on network filesystem, leaving network up”。
  • 然后,通过 exit 1 终止脚本并返回退出码 1,这通常表示脚本运行失败或中止。
  • 最后,通过 unset 命令清除变量 root_fstypeusr_fstype
    # Don't shut the network down when shutting down the system if configured
    # as such in sysconfig
    if is_false "$IFDOWN_ON_SHUTDOWN"; then '如果这个变量是假的'
      if systemctl is-system-running | grep -q 'stopping'; then '使用 systemctl is-system-running 命令检查系统当前的运行状态。如果系统正在关机过程中(状态包含 'stopping'),则执行以下操作:'
        net_log $"system is shutting down, leaving interfaces up as requested" info  '记录一条信息日志,表示系统正在关机,而网络接口将被保持启动状态。'
        exit 0	'使用 exit 0 终止脚本并返回退出码 0,表示成功执行。'
      fi
    fi

    vlaninterfaces=""
    interfaces=""
    xdslinterfaces=""
    bridgeinterfaces=""
    remaining=""
    rc=0
'重置一些变量,return code 设置为0'
# get list of bonding, , and xdsl interfaces
    for i in $interfaces; do
        unset DEVICE TYPE 	'删除这两个变量'
        eval $(LANG=C grep -F "DEVICE=" ifcfg-$i)	'执行DEVICE=的值'
        eval $(LANG=C grep -F "TYPE=" ifcfg-$i)		'执行TYPE=的值'
		'使用 grep 命令从接口配置文件 ifcfg-$i 中提取 DEVICE 和 TYPE 的值,并使用 eval 命令将它们赋值给相应的变量。'
		
        if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi	'如果这个字符串为空,设置DEVICE=$i'

        if [ "${DEVICE##cipcb}" != "$DEVICE" ] ; then	
            interfaces="$interfaces $i"
            continue
        fi
        if [ "$TYPE" = "IPSEC" ] || [ "$TYPE" = "IPIP" ] || [ "$TYPE" = "GRE" ]; then
            interfaces="$interfaces $i"
            continue
        fi
        '如果接口名以 "cipcb" 开头,则将其添加到 VPN 接口列表中。如果接口的类型为 "IPSEC"、"IPIP" 或 "GRE",同样将其添加到 VPN 接口列表中。'
        
        if [ "$TYPE" = "Bridge" ]; then
            bridgeinterfaces="$bridgeinterfaces $i"
            continue
        fi
        '如果接口的类型为 "Bridge",则将其添加到桥接接口列表中。'
        
        if [ "$TYPE" = "xDSL"  -o  "$TYPE" = "Modem" ]; then
            xdslinterfaces="$xdslinterfaces $i"
            continue
        fi
		'如果接口的类型为 "xDSL" 或 "Modem",则将其添加到 xDSL 接口列表中。'

        if [ "${DEVICE%%.*}" != "$DEVICE"  -o  "${DEVICE##vlan}" != "$DEVICE" ] ; then
            vlaninterfaces="$vlaninterfaces $i"
            continue
        fi
        '如果 DEVICE 的第一个点之前的部分不等于整个 DEVICE,或者 DEVICE 以 "vlan" 开头,那么将其添加到 VLAN 接口列表中'
        remaining="$remaining $i"
        '如果接口不属于以上任何一类,则将其添加到 remaining 列表中。'
    done

    for i in $interfaces $xdslinterfaces $bridgeinterfaces $vlaninterfaces $remaining; do
    '使用 for 循环遍历 VPN 接口、xDSL 接口、桥接接口、VLAN 接口以及剩余接口的列表。'
        unset DEVICE TYPE  '在每次循环迭代开始时,使用 unset 命令重置 DEVICE 和 TYPE 变量。'
        (. ./ifcfg-$i '在子shell中执行 . 命令,从而在当前环境中加载接口配置文件 ifcfg-$i。'
        if [ -z "$DEVICE" ] ; then DEVICE="$i"; fi  '如果在配置文件中未找到 DEVICE,则将其设置为当前接口名。'

        if ! check_device_down $DEVICE; then '使用 check_device_down 函数检查接口是否已关闭。'
            action $"Shutting down interface $i: " ./ifdown $i boot '如果接口未关闭,使用 ./ifdown $i boot 命令关闭接口,并记录操作。'
            [ $? -ne 0 ] && rc=1 '如果关闭接口的命令执行失败([ $? -ne 0 ]),将 rc 变量设置为 1,表示发生错误。'
        fi
        )
    done

    action $"Shutting down loopback interface: " ./ifdown ifcfg-lo  '使用 ./ifdown ifcfg-lo 命令关闭回环接口,并记录操作。'

    sysctl -w net.ipv4.ip_forward=0 > /dev/null 2>&1 '使用 sysctl 命令将 IPv4 的 IP 转发设置为 0,表示禁用。通过 > /dev/null 2>&1 将输出重定向到空设备,以避免输出到终端。'

    # IPv6 hook (post IPv4 stop)
    if [ -x /etc/sysconfig/network-scripts/init.ipv6-global ]; then '如果这个文件有可执行权限'
        /etc/sysconfig/network-scripts/init.ipv6-global stop post	'执行这边脚本,并传递两个参数'
    fi

    rm -f /var/lock/subsys/network	'删除这个文件'
    ;;
status)
    echo $"Configured devices:"  
    echo lo $interfaces	
	'打印已配置的设备列表,包括回环接口 lo 和其他接口 $interfaces。'

    echo $"Currently active devices:"
    echo $(/sbin/ip -o link show up | awk -F ": " '{ print $2 }')
    '打印当前活动的设备列表,使用 /sbin/ip -o link show up 命令获取已启动的网络接口,然后使用 awk 命令提取接口名。'
    ;;
restart|force-reload) '如果脚本的参数是 restart 或 force-reload'
    cd "$CWD"  '切换到脚本所在的目录'
    $0 stop	   '使用 $0 stop 命令停止网络服务'
    $0 start   '使用 $0 start 命令启动网络服务'
    rc=$?	   '将结果保存在rc中'
    ;;
*)
    echo $"Usage: $0 {start|stop|status|restart|force-reload}" 
    exit 2
    '上如果脚本的参数不是上述提到的情况之一,则打印使用说明并以退出码 2 退出脚本。'
esac

exit $rc

5.计划任务

1.什么是计划任务?

规定一个时间去做某事

有个crond程序:到点帮助我们去执行脚本

任务:做的事情

计划:到时到点

相当于闹铃:生活里的计划任务

crond程序:一直在内存里运行,会每间隔1分钟去查看所有用户的计划任务,然后去帮助执行

[root@gh-shell 1-28] ps aux|grep crond '看进程'
root        741  0.0  0.0 126416  1720 ?        Ss   1月16   0:00 /usr/sbin/crond -n
root     104304  0.0  0.0 112824   976 pts/1    S+   08:42   0:00 grep --color=auto crond
[root@gh-shell 1-28] service crond stop '停止crond服务'
Redirecting to /bin/systemctl stop crond.service
[root@gh-shell 1-28] ps aux|grep crond 
root     104334  0.0  0.0 112824   976 pts/1    S+   08:43   0:00 grep --color=auto crond
[root@gh-shell 1-28] service crond start '开启crond服务'
Redirecting to /bin/systemctl start crond.service
[root@gh-shell 1-28] ps aux|grep crond
root     104354 21.0  0.0 126388  1684 ?        Ss   08:43   0:00 /usr/sbin/crond -n
root     104357  0.0  0.0 112824   972 pts/1    S+   08:43   0:00 grep --color=auto crond
[root@gh-shell 1-28]# 

2.基本规则

crond系统定时任务
crontab
-e: 编辑crontab定时任务
-l: 查询crontab任务
-r: 删除当前用户所有的crontab任务

第一个“*” 一小时当中的第几分钟 0-59
第二个“*” 一天当中的第几小时 0-23
第三个“*” 一个月当中的第几天 1-31
第四个“*” 一年当中的第几月 1-12
第五个“*” 一周当中的星期几 0-7(0和7都代表星期日)

* 代表任何时间。比如第一个“”就代表一小时中每分钟都执行一次的意思。
, 代表不连续的时间。比如“0 8,12,16 * * * 命令”,就代表在每天的8点0分,12点0分,16点0分都执行一次命令
- 代表连续的时间范围。比如“0 5 * * 1-6命令”,代表在周一到周六的凌晨5点0分执行命令
*/n 代表每隔多久执行一次。比如“
/10 * * * * 命令”,代表每隔10分钟就执行一遍命令

3.写个备份脚本

'编写一个脚本backup 1og.sh实现备份/var/1og目录下的所有文件到/scbackup目录下,要求文件名是包含当天日期精确到秒,文件名例如:20220412208401-1og.tar.gz。同时要求删除七天前的备份文件,只保留最近7天的文件。'

'执行backup_log.sh 每天的晚上2:30执行----->利用计划任务去执行'

[root@gh-shell 1-28] vim backup_log.sh
#!/bin/bash

#获得当前的日期
ctime=$(date "+%Y%m%d%H%M%S")

#备份/var/log目录到/scbackup
mkdir -p /scbackup

tar -czf /scbackup/${ctime}-log.tar.gz /var/log

if (( $? ==0 ));then
	echo "${ctime} backup ok!" >>/var/log/backup_log.log
else
	echo "${ctime} backup failed!" >>/var/log/backup_log.log
fi

#删除七天前的备份文件
find /scbackup -mtime +7 -type f -name "*log.tar.gz" -exec rm -rf {} \;

[root@gh-shell 1-28]# 

'执行backup_log.sh 每天的晚上2:30执行----->利用计划任务去执行'
[root@gh-shell 1-28] crontab -e
30 2 * * * bash /shell/1-28/backup_log.sh
no crontab for root - using an empty one
crontab: installing new crontab

[root@gh-shell 1-28] crontab -l
30 2 * * * bash /shell/1-28/backup_log.sh
[root@gh-shell 1-28]# 

4.计划任务背后的规则

[root@gh-shell 1-28] cat /etc/crontab 
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root

# For details see man 4 crontabs

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name  command to be executed

[root@gh-shell 1-28]# 

20 2 3 2 * bash a.sh 2月3日的2点20分去执行

20 2 * * * bash a.sh 每天的2点20去执行

20 2 * * 1-5 bash a.sh 周1到周5去执行

*/5 * * * * bash a.sh 每隔5分钟执行一次

0 */5 * * * bash a.sh 没间隔5小时执行一次

20 2 * * 1,3,5 bash a.sh 每周的周1,周3,周5的2点20去执行

/ 间隔

- 连续

, 不连续的

5.我们创建的计划任务放到哪里的?crond如果知道的?

(个人用户)新增的计划任务放在了: /var/spool/cron/ 这个目录下边,一个用户对应一个文件

[root@gh-shell 1-28] cd /var/spool/cron/
[root@gh-shell cron] ls
root
[root@gh-shell cron] cat root 
30 2 * * * bash /shell/1-28/backup_log.sh
[root@gh-shell cron]# 

(操作系统)会放在 /etc/cron下边

6.如果知道crond是否执行计划任务?

1.看日志!:/var/log/cron

[root@gh-shell cron] cd /var/log/
[root@gh-shell log] cat cron
Jan 18 09:26:01 gh-shell run-parts(/etc/cron.daily)[106457]: finished man-db.cron
Jan 18 09:26:01 gh-shell anacron[105228]: Job `cron.daily' terminated (produced output)
[root@gh-shell log]# 

2.看效果!

7.黑客是否在你的机器里增加了计划任务,你如何知道?

去/var/spool/cron/下边看,是谁创建的计划任务

8.要知道几个文件夹 在/etc/下

image-20240128094757053

操作系统要完成的计划任务

6.练习

写计划任务执行的时间,定时去执行 /shell/sc.sh

1.12月3日5:50执行 脚本

50 5 3 12 * bash /shell/sc.sh

2.每间隔18分钟执行

*/18 * * * * bash /shell/sc.sh

3.每间隔2小时执行

0 */2 * * * * bash /shell/sc.sh

4.每周的1,3,5的5点30执行

30 5 * * * 1,3,5 bash /shell/sc.sh

5.每天的6点,8点,11点执行

0 6,8,11 * * * bash /shell/sc.sh

6.12月5号到28号的每天8点30执行

30 8 5-28 12 * bash /shell/sc.sh

7.忘记的小知识点

1.bash执行的4种方式

shell编程-8_第1张图片

2.date的用法

date 是 Linux 中用于显示或设置系统日期和时间的命令。以下是 date 命令的一些详细用法:

  1. 显示当前日期和时间:

    date
    
    • 示例输出:
      Fri Jan 28 09:30:00 UTC 2024
      
  2. 显示特定格式的日期和时间:

    date "+%Y-%m-%d %H:%M:%S"
    
    • 示例输出:
      2024-01-28 09:30:00
      
    • 上述例子中,+%Y-%m-%d %H:%M:%S 是指定的日期时间格式,可以根据需要进行修改。
  3. 设置系统日期和时间:

    sudo date MMDDhhmm[[CC]YY][.ss]
    
    • 示例:
      sudo date 012809302024.00
      
    • 上述例子设置系统日期为 2024 年 01 月 28 日,时间为 09:30:00。
  4. 从文件中读取日期和时间:

    date -s "$(cat /path/to/file)"
    
    • 将文件中的日期和时间字符串作为参数传递给 date 命令。
  5. 计算未来或过去的日期:

    date -d "2 days ago"
    
    • 示例输出:
      Wed Jan 26 09:30:00 UTC 2024
      
    • 上述例子中,-d 选项用于指定相对日期。其他可能的值包括 2 days agonext Monday 等。
  6. 显示纪元时间戳:

    date +%s
    
    • 输出从 1970 年 1 月 1 日 00:00:00 UTC 到现在的秒数。
  7. 显示本地时区的日期和时间:

    date +"%Y-%m-%d %H:%M:%S %Z"
    
    • 示例输出:
      2024-01-28 09:30:00 UTC
      
    • 上述例子中,%Z 表示显示时区信息。
  8. 显示 UTC 时间:

    date -u
    
    • 显示协调世界时(UTC)时间。

这只是 date 命令的一些基本用法,还有其他选项和格式可以根据具体需求进行使用。你可以使用 man date 命令来查看 date 的手册页,以获取更多详细信息。

  1. 显示格式为20220412208401

    date "+%Y%m%d%H%M%S"
    
  2. 更改系统时间

    date -s "2024-2-11"
    

3.tar的用法

  • -c 产生.tar打包文件
  • -v 显示详细信息
  • -f 指定压缩后的文件名
  • -z 打包同时压缩
  • -x 解包.tar文件

压缩:tar -zcvf XXX.tar.gz XXX
解压:tar -zxvf XXX.tar.gz -C 解压目的地

[root@server lianxi] tar -zcvf 课外作业.docx.tar.gz 课外作业.docx  //压缩文件
课外作业.docx
[root@server lianxi] ls
课外作业.docx  课外作业.docx.tar.gz  上机作业.doc

[root@server lianxi] tar -zxvf 课外作业.docx.tar.gz -C /test  //解压到指定文件夹
[root@server lianxi] ls /test
192.168.209.145  192.168.209.146  backup.sh  create_file.sh  which.sh  课外作业.docx

文件打包
tar
文件压缩的好处:
方便文件在网上的传输,节约存储空间.

文件打包:方便移动
tar 文件打包
tar -cf 打包文件
tar -tvf 列出所有文件
tar -xf 解包文件

tar -cvf xxx.tar /boot/ 将boot目录打包、
c create 创建
f filename 文件名称
v 显示执行过程的详细信息

file 文件名 查看一个文件的类型
file a.txt

一次打包两个目录
tar -cvf xx.tar /boot/ /etc/test/

查看包中的内容
tar -tvf xxx.tar

将包解压到指定的目录中
tar -xvf xxx.tar -C /tmp/

查看一个目录的大小
du -sh /boot/
查看一个文件的大小
ll -h xxx.tar

tar 中的参数
-z gzip 工具压缩 压缩后缀是.gz

解压缩包
tar -zxvf xxx.tar.gz -C /tmp/

bzip2 压缩工具,后缀是.tar.bz2
压缩:tar cjvf xxx.tar.bz2
-j 表示使用的是bzip2压缩工具
解压:tar xjvf xxx.tar.bz2 -C /tmp/

bzip2压缩的文件比 gzip压缩的文件小

zip 压缩工具:zip -r xxx.zip /etc/passwd
解压缩zip unzip xxx.zip -d /tmp/

gzip 压缩工具:压缩后原文件会消失
gzip 1.txt 压缩后的文件是 1.txt.gz
gzip 解压缩:
gzip -d 1.txt.gz

4.find的用法

作用:查找文件目录

参数:``-name 按名称查找;-user 按用户查找;-group 按组查找;-empty 查找空目录空文件;-perm 按权限查找;-mtime 按修改时间查找;-size 按容量大小查找;-exec 对找到的内容执行命令;-type` 按类型查找,f-文件 d-目录 b c-设备 l-链接;-o 或者条件;-a 并且条件。

示例:

[root@centos7 ~]# find -name test.txt    #查找名称为test.py的文件
  [root@centos7 ~]# find -iname tesT.txt     #查找test.py文件,不区分大小写
  [root@centos7 ~]# find / -user game     #查找用户名为game的文件或目录
  [root@centos7 ~]# find ./ -group game     #查找组名为game的文件或目录
  [root@centos7 ~]# find ./ empty       #查找空的目录或文件
  [root@centos7 ~]# find ./ -perm 644     #查找当前目录下权限为644的文件
  [root@centos7 ~]# find /home -mtime -3   #查找home下所有3天内修改过的文件或目录
  [root@centos7 ~]# find /home -mtime +5   #查找home下所有5天前被修改过的文件或目录
  [root@centos7 ~]# find /home -mtime 6   #查找6天前当天修改的文件或目录
  [root@centos7 ~]# find /data -size +20M   #查找data目录下大于20M的文件或目录
  [root@centos7 ~]# find /home -size +2M -exec ls -l {} ;  #列出home下大于2M的文件或目录
  [root@centos7 ~]# find /home -size +3M -a -type f -exec rm -rf {} ;  #删除home下大于3M的文件
  [root@centos7 ~]# find /home -type f -name “*.log”  #查找日志文件
  [root@centos7 ~]# find /home -amin n        #最后n分钟
  [root@centos7 ~]# find /home -atime n      #最后n天
  [root@centos7 ~]# find /home -cmin n      #最后n分钟改变状态
  [root@centos7 ~]# find /home -ctime n       #最后n天改变状态
  [root@centos7 ~]# find -name *.txt |xargs -i cp {} /tmp/   #把TXT文件复制到/tmp/目录下
  [root@centos7 ~]# find -name *.txt -exec cp ‘{}’ /tmp/ ;  #把TXT文件复制到/tmp/目录下

提示:find命令功能强大应用广泛,常用于查找并执行后续命令,注意exec和xargs的用法。

5.小建议

在计划任务的脚本中,尽量使用绝对路径去执行命令,因为crond程序会在指定的地方去寻找命令

你可能感兴趣的:(shell,运维,linux,计划任务,shift)