目录
自动备份
-
归档配置文件
利用tar命令归档数据,可以将整个目录归档到单个文件夹中。一般tar命令会显示一条警告信息,表明它删除了路径名开头的斜线,将路径从绝对路径名变成相对路径名,这样就可以将tar归档文件解压到文件系统中的任何地方了。
由于tar归档文件会消耗大量的磁盘空间,为了压缩文件需要加一个-z选项。它会将tar归档文件压缩成gzip格式的tar文件用.tar.gz或.tgz都行
$ tar -cf archive.tar /home/Christine/Project/*.*
tar: Removing leading '/' from member names #想去除此警告,利用重定向
$ tar -cf archive.tar /home/Christine/Project/*.* 2>/dev/null
$ tar -zcf archive.tar.gz /home/Christine/Project/*.* 2>/dev/null
如果想备份多个目录可以利用一个配置文件解决,配置文件应该包含你希望进行归档的每个目录或文件。可以让脚本读取配置文件,然后将每个目录名加到归档列表中。
$ cat Files_To_Backup
/home/Christine/Project
/home/Christine/Downloads
/home/Does_not_exist
/home/Christine/Documents
$
要实现这一点,只需要使用read命令来读取该文件中的每一条记录就行了。不过不用像之前那样通过管道将cat命令的输出传给while循环(cat file | while read line),在这个脚本中我们使用exec命令来重定向标准输入(STDIN)。CONFIG_FILE(归档配置文件)
exec < $CONFIG_FILE #输入管道从cat命令改为文件名
read FILE_NAME #每次调用read都会从文件中读取一行文本
只要read命令在配置文件中发现还有记录可读,它就会在?变量中返回一个表示成功的退出状态码0。可以将它作为while循环的测试条件来读取配置文件中的所有记录。
while [ $? -eq 0 ]
do
[...]
read FILE_NAME
done
在while循环中,我们需要做两件事。首先,必须将目录名加到归档列表中。更重要的是要检查那个目录是否存在!。,否则就显示一条警告消息。
if [ -f $FILE_NAME -o -d $FILE_NAME ] #-f-d判断是文件还是目录;or选项-o
then
FILE_LIST="$FILE_LIST $FILE_NAME" # 如果目录存在,它会被加入要归档目录列表FILE_LIST中
else # 否则,显示一条警告消息
echo
echo "$FILE_NAME, does not exist."
echo "Obviously, I will not include it in this archive."
echo "It is listed on line $FILE_NO of the config file."
echo "Continuing to build archive list..."
echo
fi
FILE_NO=$[$FILE_NO + 1] #添加了变量FILE_NO。配置文件中哪行不正确或缺失
-
存放位置
如果要对多个目录进行备份,最好还是创建一个集中归档仓库目录
$ mkdir /archive
$ mv Files_To_Backup /archive/
可以通过sudo命令或者创建一个用户组的方式,为需要在集中归档目录中创建文件的用户授权。可以创建一个特殊的用户组Archivers。
-
创建按日归档的脚本
Daily_Archive脚本会自动在指定位置创建一个归档,使用当前日期来唯一标识该文件
DATE=$(date +%y%m%d)
FILE=archive$DATE.tar.gz
CONFIG_FILE=/archive/file_to_backup
DESTINATION=/archive/$FILE
将所有的内容结合在一起,Daily_Archive脚本内容如下
#!/bin/bash
DATE=$(date +%y%m%d)
FILE=archive$DATE.tar.gz
CONFIG_FILE=/archive/Files_To_Backup
DESTINATION=/archive/$FILE
######### Main Script #########################
if [ -f $CONFIG_FILE ] #确保配置文件存在
then
echo
else
echo
echo "$CONFIG_FILE does not exist."
echo "Backup not completed due to missing Configuration File"
echo
exit
fi
FILE_NO=1
exec < $CONFIG_FILE
read FILE_NAME
while [ $? -eq 0 ]
do
if [ -f $FILE_NAME -o -d $FILE_NAME ]
then
FILE_LIST="$FILE_LIST $FILE_NAME"
else
echo
echo "$FILE_NAME, does not exist."
echo "Obviously, I will not include it in this archive."
echo "It is listed on line $FILE_NO of the config file."
echo "Continuing to build archive list..."
echo
fi
FILE_NO=$[$FILE_NO + 1]
read FILE_NAME
done
echo "Starting archive..."
echo
tar -czf $DESTINATION $FILE_LIST 2> /dev/null
echo "Archive completed"
echo "Resulting archive file is: $DESTINATION"
echo
exit
-
创建按小时归档的脚本
在按小时备份文件时,使用date命令为每个tarball文件加入时间戳不再合适,不必将所有的归档文件都放到同一目录中,你可以为归档文件创建一个目录层级,如下所示,每月的目录中又包含与当月各天对应的目录
新目录设置好之后,将按小时归档的配置文件File_To_Backup移动到该目录中。mv Files_To_Backup /archive/hourly/
我们脚本必须自动创建对应每月和每天的目录,如果这些目录已经存在的话,脚本就会报错。而mkdir命令的-p选项,允许在单个命令中创建目录和子目录。并且就算目录已经存在,它也不会产生错误消息。
现在可以创建Hourly_Archive.sh脚本了,以下是前脚本的前半部分.一旦脚本Hourly_Archive.sh到了Main Script部分,就和Daily_Archive.sh脚本完全一样了。
#!/bin/bash
BASEDEST=/archive/hourly
DAY=$(date +%d)
MONTH=$(date +%m)
TIME=$(date +%k%m)
mkdir -p $BASEDEST/$MONTH/$DAY
CONFIG_FILE=/archive/hourly/Files_To_Backup
DESTINATION=$BASEDEST/$MONTH/$DAY/archive$TIME.tar.gz
######### Main Script #########################
管理用户账户
删除账户在管理账户工作中比较复杂。在删除账户时,至少需要4个步骤:
(1) 获得正确的待删除用户账户名;
(2) 杀死正在系统上运行的属于该账户的进程;
(3) 确认系统中属于该账户的所有文件;(4) 删除该用户账户
-
获取正确的账户名
获取待删除的用户账户的正确名称。可以用read命令获取账户名称
echo "Please enter the username of the user "
echo -e "account you wish to delete from system: \c"
read -t 60 ANSWER
应该给用户三次机会来回答问题。用一个while循环加-z选项来测试ANSWER变量是否为空。在脚本第一次进入while循环时,ANSWER变量的内容为空,用来给该变量赋值的提问位于循环的底部。
while [ -z "$ANSWER" ]
do
[...]
echo "Please enter the username of the user "
echo -e "account you wish to delete from system: \c"
read -t 60 ANSWER
done
利用case语句,通过给ASK_COUNT变量增值,可以设定不同的消息来回应脚本用户。
case $ASK_COUNT in
2)
echo
echo "Please answer the question."
echo
;;
3)
echo
echo "One last try...please answer the question."
echo
;;
4)
echo
echo "Since you refuse to answer the question..."
echo "exiting program."
echo
exit
;;
esac
-
创建函数获取正确的账户名
声明函数名get_answer。下一步,用unset命令清除脚本用户之前给出的答案
function get_answer {
unset ANSWER
这个脚本不会每次都问同一个问题,所以让我们创建两个新的变量LINE1和LINE2来处理问题,并不是每个问题都有两行要显示,有的只要一行。你可以用if结构解决这个问题。这个函数会测试LINE2是否为空,如果为空,则只用LINE1。
if [ -n "$LINE2" ]
then
echo $LINE1
echo -e $LINE2" \c"
else
echo -e $LINE1" \c"
fi
最终,我们的函数需要通过清空LINE1和LINE2变量来清除一下自己
function get_answer {
unset ANSWER
ASK_COUNT=0
while [ -z "$ANSWER" ]
do
ASK_COUNT=$[ $ASK_COUNT + 1 ]
case $ASK_COUNT in
2)
echo
[...]
esac
echo
if [ -n "$LINE2" ]
then
echo $LINE1
echo -e $LINE2" \c"
else
echo -e $LINE1" \c"
fi
read -t 60 ANSWER
done
unset LINE1
unset LINE2
}
要问脚本用户删除哪个账户,你需要设置一些变量,然后调用get_answer函数。使用新函数让脚本代码清爽了许多
LINE1="Please enter the username of the user "
LINE2="account you wish to delete from system:"
get_answer
USER_ACCOUNT=$ANSWER
-
验证输入的用户名
鉴于可能存在输入错误,应该验证一下输入的用户账户
LINE1="Is $USER_ACCOUNT the user account "
LINE2="you wish to delete from the system? [y/n]"
get_answer
在提出问题之后,脚本必须处理答案。变量ANSWER再次将脚本用户的回答带回问题中。如果用户回答了yes,就得到了要删除的正确用户账户,脚本也可以继续执行。
case $ANSWER in
y|Y|YES|yes|Yes|yEs|yeS|YEs|yES )
;;
*)
echo
echo "Because the account, $USER_ACCOUNT, is not "
echo "the one you wish to delete, we are leaving the script..."
echo
exit
;;
esac
创建一个函数来处理这个任务,还要给case语句中加两个变量, EXIT_LINE1 和EXIT_LINE2
function process_answer {
case $ANSWER in
y|Y|YES|yes|Yes|yEs|yeS|YEs|yES )
;;
*)
echo
echo $EXIT_LINE1
echo $EXIT_LINE2
echo
exit
;;
esac
unset EXIT_LINE1
unset EXIT_LINE2
}
EXIT_LINE1="Because the account, $USER_ACCOUNT, is not "
EXIT_LINE2="the one you wish to delete, we are leaving the script..."
process_answer
-
确定账户是否存在
核对一下这个用户账户在系统上是否真实存在。并将完整的账户记录显示给脚本用户,核对这是不是真的要删除的那个账户。要完成这些工作,需使用变量USER_ACCOUNT_RECORD,将它设成grep在/etc/passwd文件中查找该用户账户的输出。-w选项允许你对这个特定用户账户进行精确匹配。
USER_ACCOUNT_RECORD=$(cat /etc/passwd | grep -w $USER_ACCOUNT)
如果在/etc/passwd中没找到用户账户记录,必须通知脚本用户,然后退出脚本。grep命令的退出状态码可以在这里帮到我们。如果没找到这条账户记录,?变量会被设成1。
if [ $? -eq 1 ]
then
echo
echo "Account, $USER_ACCOUNT, not found. "
echo "Leaving the script..."
echo
exit
fi
如果找到了这条记录,你仍然需要验证这个脚本用户是不是正确的账户。我们先前建立的函数在这里就能发挥作用了!你要做的只是设置正确的变量并调用函数。
echo "I found this record:"
echo $USER_ACCOUNT_RECORD
echo
LINE1="Is this the correct User Account? [y/n]"
get_answer
EXIT_LINE1="Because the account, $USER_ACCOUNT, is not"
EXIT_LINE2="the one you wish to delete, we are leaving the script..."
process_answer
-
删除属于账户的进程
脚本可以用ps命令和-u选项来定位属于该账户的所有处于运行中的进程。可以将输出重定向到/dev/null,这样用户就看不到任何输出信息了。可以用ps命令的退出状态码和case结构来决定下一步做什么。
ps -u $USER_ACCOUNT >/dev/null
case $? in
1) #没有属于该用户账户的进程在运行
echo "There are no processes for this account currently running."
echo
;;
0) #问脚本用户是否要杀死这些进程
echo "$USER_ACCOUNT has the following processes running: "
echo
ps -u $USER_ACCOUNT
LINE1="Would you like me to kill the process(es)? [y/n]"
get_answer
[...]
esac
接下来的任务对process_answer来说太复杂了。你需要嵌入另一个case语句来处理脚本用户的答案。case语句的第一部分看起来和process_answer函数很像。xargs命令可以构建并执行来自标准输入STDIN的命令。它非常适合用在管道的末尾处。xargs命令负责杀死PID所对应的进程。
这三条命令通过管道串联在了一起。ps命令生成了处于运行状态的用户进程列表,其中包括每个进程的PID。gawk命令将ps命令的标准输出(STDOUT)作为自己的STDIN,然后从中只提取出PID。xargs命令将gawk命令生成的每个PID作为STDIN,创建并执行kill命令,杀死用户所有的运行进程。
case $ANSWER in
y|Y|YES|yes|Yes|yEs|yeS|YEs|yES )
echo
echo "Killing off process(es)..."
COMMAND_1="ps -u $USER_ACCOUNT --no-heading" #收集当前处于运行状态、属于该用户账户的进程ID(PID)。
COMMAND_3="xargs -d \\n /usr/bin/sudo /bin/kill -9"
$COMMAND_1 | gawk '{print $1}' | $COMMAND_3 #gawk命令可以从ps命令输出中提取第一个字段PID。
echo
echo "Process(es) killed."
;;
*)
echo
echo "Will not kill the process(es)"
echo
;;
esac
-
查找属于账户的文件
要找到用户文件,你可以用find命令。find命令用-u选项查找整个文件系统,它能够准确查找到属于该用户的所有文件。该命令如下:
find / -user $USER_ACCOUNT > $REPORT_FILE
-
删除账户
对删除系统中的用户账户慎之又慎总是好事。因此,你应该再问一次脚本用户是否真的想删除该账户:
LINE1="Remove $User_Account's account from system? [y/n]"
get_answer
#
EXIT_LINE1="Since you do not wish to remove the user account,"
EXIT_LINE2="$USER_ACCOUNT at this time, exiting the script..."
process_answer
userdel $USER_ACCOUNT
-
创建脚本
#!/bin/bash
function get_answer {
unset ANSWER
ASK_COUNT=0
while [ -z "$ANSWER" ]
do
ASK_COUNT=$[ $ASK_COUNT + 1 ]
case $ASK_COUNT in
2)
echo
echo "Please answer the question."
echo
;;
3)
echo
echo "One last try...please answer the question."
echo
;;
4)
echo
echo "Since you refuse to answer the question..."
echo "exiting program."
echo
exit
;;
esac
echo
if [ -n "$LINE2" ]
then
echo $LINE1
echo -e $LINE2" \c"
else
echo -e $LINE1" \c"
fi
read -t 60 ANSWER
done
unset LINE1
unset LINE2
}
function process_answer {
case $ANSWER in
y|Y|YES|yes|Yes|yEs|yeS|YEs|yES )
;;
*)
echo
echo $EXIT_LINE1
echo $EXIT_LINE2
echo
exit
;;
esac
unset EXIT_LINE1
unset EXIT_LINE2
}
############# Main Script ####################
# 首次获得删除账号
echo "Step #1 - Determine User Account name to Delete "
echo
LINE1="Please enter the username of the user "
LINE2="account you wish to delete from system:"
get_answer
USER_ACCOUNT=$ANSWER
# 重复确认
LINE1="Is $USER_ACCOUNT the user account "
LINE2="you wish to delete from the system? [y/n]"
get_answer
# 对重复确认的结果进行处理
EXIT_LINE1="Because the account, $USER_ACCOUNT, is not "
EXIT_LINE2="the one you wish to delete, we are leaving the script..."
process_answer
#确认用户存在并获取删除用户信息
USER_ACCOUNT_RECORD=$(cat /etc/passwd | grep -w $USER_ACCOUNT)
if [ $? -eq 1 ]
then
echo
echo "Account, $USER_ACCOUNT, not found. "
echo "Leaving the script..."
echo
exit
fi
echo
echo "I found this record:"
echo $USER_ACCOUNT_RECORD
LINE1="Is this the correct User Account? [y/n]"
get_answer
EXIT_LINE1="Because the account, $USER_ACCOUNT, is not "
EXIT_LINE2="the one you wish to delete, we are leaving the script..."
process_answer
# 用户进程处理
echo
echo "Step #2 - Find process on system belonging to user account"
echo
ps -u $USER_ACCOUNT >/dev/null #Are user processes running?
case $? in
1)
echo "There are no processes for this account currently running."
echo
;;
0)
echo "$USER_ACCOUNT has the following processes running: "
echo
ps -u $USER_ACCOUNT
LINE1="Would you like me to kill the process(es)? [y/n]"
get_answer
case $ANSWER in
y|Y|YES|yes|Yes|yEs|yeS|YEs|yES )
echo
echo "Killing off process(es)..."
COMMAND_1="ps -u $USER_ACCOUNT --no-heading"
COMMAND_3="xargs -d \\n /usr/bin/sudo /bin/kill -9"
$COMMAND_1 | gawk '{print $1}' | $COMMAND_3
echo
echo "Process(es) killed."
;;
*)
echo
echo "Will not kill the process(es)"
echo
;;
esac
;;
esac
# 产生用户所属文件报告
echo
echo "Step #3 - Find files on system belonging to user account"
echo
echo "Creating a report of all files owned by $USER_ACCOUNT."
echo
echo "It is recommended that you backup/archive these files,"
echo "and then do one of two things:"
echo " 1) Delete the files"
echo " 2) Change the files' ownership to a current user account."
echo
echo "Please wait. This may take a while..."
REPORT_DATE=$(date +%y%m%d)
REPORT_FILE=$USER_ACCOUNT"_Files_"$REPORT_DATE".rpt"
find / -user $USER_ACCOUNT > $REPORT_FILE 2>/dev/null
echo
echo "Report is complete."
echo "Name of report: $REPORT_FILE"
echo "Location of report: $(pwd)"
echo
# 移除用户
echo
echo "Step #4 - Remove user account"
echo
LINE1="Remove $USER_ACCOUNT's account from system? [y/n]"
get_answer
EXIT_LINE1="Since you do not wish to remove the user account,"
EXIT_LINE2="$USER_ACCOUNT at this time, exiting the script..."
process_answer
userdel $USER_ACCOUNT
echo
echo "User account, $USER_ACCOUNT, has been removed"
echo
exit
监测磁盘空间
找出指定目录中磁盘空间使用量位居前十名的用户。它会生成一个以日期命名的报告,使得磁盘空间使用量可以监测。
-
需要的功能
du命令能够显示出单个文件和目录的磁盘使用情况。-s选项用来总结目录一级的整体使用状况
$ du -s /home/*
4204 /home/Christine
56 /home/Consultant
52 /home/Development
4 /home/NoSuchUser
96 /home/Samantha
36 /home/Timothy
1024 /home/user1
$
-s选项能够很好地处理用户的$HOME目录,但如果我们要查看系统目录,这个列表很快就变得过于琐碎。这里,-S(大写的S)选项能更适合我们的目的,它为每个目录和子目录分别提供了总计信息
$ du -S /var/log/
4 /var/log/ppp
4 /var/log/sssd
3020 /var/log/sa
80 /var/log/prelink
4 /var/log/samba/old
4 /var/log/samba
4 /var/log/ntpstats
4 /var/log/cups
4392 /var/log/audit
420 /var/log/gdm
4 /var/log/httpd
152 /var/log/ConsoleKit
2976 /var/log/
$
由于我们感兴趣的是占用磁盘空间最多的目录,所以需要使用sort命令对du产生的输出进行排序
$ sudo du -S /var/log/ | sort -rn #-n选项允许按数字排序。-r选项会先列出最大数字(逆序)
4392 /var/log/audit
3020 /var/log/sa
2976 /var/log/
420 /var/log/gdm
152 /var/log/ConsoleKit
80 /var/log/prelink
4 /var/log/sssd
4 /var/log/samba/old
4 /var/log/samba
4 /var/log/ppp
4 /var/log/ntpstats
4 /var/log/httpd
4 /var/log/cups
$
我们要关注的是磁盘用量的前10名用户,所以当到了第11行时,sed编辑器会删除列表的剩余部分。下一步是给列表中的每行加一个行号。使用sed的等号命令(=)来加入行号。要让行号和磁盘空间文本位于同一行,可以用N命令将文本行合并在一起。
sed '{11,$D; =}' |
sed 'N; s/\n/ /' |
现在可以用gawk命令清理输出了。sed编辑器的输出会通过管道输出到gawk命令,然后用printf函数打印出来。
gawk '{printf $1 ":" "\t" $2 "\t" $3 "\n"}'
在行号后面,我们加了一个冒号(:),还给输出的每行文本的字段间放了一个制表符。这样就能得到一个格式精致的磁盘空间用量前10名的用户列表。
$ sudo du -S /var/log/ |
> sort -rn |
> sed '{11,$D; =}' |
> sed 'N; s/\n/ /' |
> gawk '{printf $1 ":" "\t" $2 "\t" $3 "\n"}'
[sudo] password for Christine:
1: 4396 /var/log/audit
2: 3024 /var/log/sa
3: 2976 /var/log/
4: 420 /var/log/gdm
5: 152 /var/log/ConsoleKit
6: 80 /var/log/prelink
7: 4 /var/log/sssd
8: 4 /var/log/samba/old
9: 4 /var/log/samba
10: 4 /var/log/ppp
$
-
创建脚本
我们用一个叫作CHECK_DIRECTORIES的变量来完成多个指定目录创建报告任务。
CHECK_DIRECTORIES=" /var/log /home"
脚本使用for循环来对变量中列出的每个目录执行du命令。每次for循环都会遍历变量CHECK_DIRECTORIES中的值列表,它会将列表中的下一个值赋给DIR_CHECK变量。
for DIR_CHECK in $CHECK_DIRECTORIES
do
[...]
du -S $DIR_CHECK
[...]
done
我们用date命令给报告的文件名加个日期戳。脚本用exec命令将它的输出重定向到加带日期戳的报告文件中。
DATE=$(date '+%m%d%y')
exec > disk_space_$DATE.rpt
#!/bin/bash
CHECK_DIRECTORIES=" /var/log /home"
############## Main Script #################################
DATE=$(date '+%m%d%y') #Date for report file
exec > disk_space_$DATE.rpt #Make report file STDOUT
echo "Top Ten Disk Space Usage"
echo "for $CHECK_DIRECTORIES Directories"
for DIR_CHECK in $CHECK_DIRECTORIES
do
echo ""
echo "The $DIR_CHECK Directory:"
du -S $DIR_CHECK 2>/dev/null |
sort -rn |
sed '{11,$D; =}' |
sed 'N; s/\n/ /' |
gawk '{printf $1 ":" "\t" $2 "\t" $3 "\n"}'
done
exit