打开文件:
# vim [option]... [FILE]...
ESC键:
编辑模式-->输入模式:
i: 在光标所在处的前方转换为输入模式
a: 在光标所在的后方转换为输入模式
o: 在光标所在行的下方新建一个空行并转换为输入模式
I: 行首
A:行尾
O: 光标所在行的上方新建一个空白行
输入模式-->编辑模式
ESC
编辑模式-->末行模式
:
末行模式-->编辑模式
ESC
输入-->编辑-->末行
退出文件:
:q! 不保存退出
:wq 保存退出
:x 保存退出
:wq! 强制保存退出
编辑模式保存退出:ZZ
编辑文本:
光标移动:
字符间移动:
h,j,k,l
#{h|j|k|l}: 跳#个字符
单词间移动:
w: 下一个单词词首
e: 当前单词或下一个单词词尾
b: 当前单词或前一个单词词首
#{w|e|b}:
行内移动:
^: 行首第一个非空白字符
0:绝对行首
$: 绝对行尾
句子间移动:
)
(
段落间移动:
}
{
行间移动:
#G: 直接跳转至第#行;
G:最后一行
编辑命令:
x: 删除光标所在处的字符
#x:
d: 删除命令
结合光标跳转字符使用,删除跳转范围内的字符
w, b, e,
$, 0, ^
#d
dd: 删除光标所在行
D: d$
注意:最后一次删除的内容会被保存至缓冲区
p: paste, 粘贴
行级别:
p: 粘贴于当前行下方
P: 上
小于行级别:
p: 粘贴于当前光标所在处的后方
P: 前
y: yank, 复制
结合光标跳转字符使用,复制跳转范围内的字符
w, b, e,
$, 0, ^
#y
Y: yy
c: change,修改
结合光标跳转字符使用,修改跳转范围内的字符
w, b, e,
$, 0, ^
先删除,再转换为输入模式
cc, C: 删除光标所在处的整行而后转换为输入
#c
撤消编辑:
u: undo,
#u: 撤消最近的#次操作
撤消此前的撤消操作:
Ctrl+r
重复前一条命令:
.
vimtutor
末行模式:
行间跳转
#
$: 最后一行
内容定界:
startpos,endpos
#: 第#行
.: 当前行
$: 最后一行
%: 全文,相当于1,$
10,$-1
c, d, y等命令可以直接附加在地址范围后使用
w /path/to/somefile: 将选定范围内的内容保存至某文件中
r /path/from/somefile: 将指定的文件中的内容读取到指定位置
s/查找模式/要替换成的内容/gi
查找模式:可以使用正则表达式
要替换成的内容:不能使用模式,仅能使用引用
s@@@gi
g: global, 全行替换
i: 不区分字符大小写
引用模式匹配到的所有内容,可以使用&符号
练习:复制/etc/rc.d/init.d/functions至/tmp目录
替换/tmp/functions文件中的/etc/sysconfig/init为/var/log
%s/\/etc\/sysconfig\/init/\/var\/log/gi
%s@/etc/sysconfig/init@/var/log@gi
练习:
1、复制/etc/grub.conf至/tmp目录,删除/tmp/grub.conf文件中的行首的空白字符;
%s@^[[:space:]]\{1,\}@@g
2、复制/etc/rc.d/rc.sysinit至/tmp目录,将/tmp/rc.sysinit文件中的以至少一个空白字符开头的行的行首加#号;
%s@^\([[:space:]]\{1,\}.*\)@#\1@
%s@^[[:space:]]\{1,\}.*@#&@
3、删除/tmp/rc.sysinit文件中以#开头,且后面跟了至少一个空白字符的行的行首的#号和空白字符;
%s@^#[[:space:]]\{1,\}@@
4、为/tmp/grub.conf文件中前三行的行首加#号;
1,3s@^@#@
5、将/etc/yum.repos.d/CentOS-Media.repo文件中的所有enable=0和gpgcheck=0两行最后的0改为1;
%s@enable=0@enable=1@
%s@\(enable\|gpgcheck\)=0@\1=1@g
vim可视化模式:
v:光标走过的字符
V:光标走过的行
文本查找:
用法同less命令
/pattern
?pattern
n
N
翻屏:
Ctrl+f: 向文件尾部翻一屏
Ctrl+b: 向文件首部翻一屏
Ctrl+d:
Ctrl+u:
j:
k:
多文件模式:
:next
:previous
:last
:first
:wqall
多窗口模式:
多文件:
vim
-o: 水平分割
-O: 垂直分割
Ctrl+w, Arrow
单文件:
Ctrl+w, s: 水平分割
Ctrl+w, v: 垂直分割
窗口属性定义:
:set nu
显示行号
:set nonu
:set ai
:set noai
:set ic
:set noic
:set sm
:set nosm
语法高亮:
:syntax on|off
搜索高亮:
:set hlsearch
:set nohlsearch
配置文件:
全局:/etc/vimrc
用户:~/.vimrc
vim回顾:
总结:编辑、输入、末行、可视(v, V)
编辑-->输入
i, I
a, A
o, O
c, C
跳转:
h,j,k,l
w,b,e
^, $, 0
G, #G
), (
}, {
d, y, c
u, Ctrl+r
多编辑器同时编辑同一个文件:
-o, -O;
vim +# FILE
vim, vi
bash编程:
bash进程:解释器
type
shell脚本:
第一行,顶格:
shebang
#!/bin/bash
#!/usr/bin/python
其它的以#开头的行均为注释,会被解释器忽略;
练习:
创建一个组newgroup,id号8008;
创建一个用户,名字为mageedu, id号为3306,附加组为newgroup
创建目录/tmp/hellobash
复制/etc/rc.d/init.d/functions至/tmp/hellobash目录中
过程式编程语言:
顺序执行
选择执行:测试条件,可能会多个测试条件,某条件满足时,则执行对应的分支
循环执行:将同一段代码反复执行多次;因此,循环必须有退出条件;否则,则陷入死循环;
bash
-n: 语法测试
-x: 模拟单步执行
变量类别:
本地变量
环境变量
export:导出
局部变量
位置变量
$1, ..., $n, ${10}
练习:写一个脚本,能接受一个参数作为用户名,此脚本可创建此用户,并且其密码同用户名;
shift [n]
特殊变量:
$?
$#: 传递给脚本参数的个数
$*
$@:引用传递给脚本的所有参数
bash的循环语句:
for:遍历有限的元素列表,
while:
until:
for语句的格式:
for VAR_NAME in LIST
do
循环体
done
LIST:列表,中间包括一个或多个元素
退出条件:遍历结束
练习:创建10个用户,user301, user310
列表:user301, user310
列表:301, 310
生成数值列表:
{start..end}
例如:{1..100}
seq
命令引用
for userNo in {301..310}
for userNo in $(seq 301 310); do
useradd user${userNo}
done
练习:创建目录/tmp/dir-当前日期时间;例如/tmp/dir-20140707-155503
在此目录中创建10个空文件,分别为file1-file10;
练习:写一个脚本
1、创建用户tuser1-tuser9;
2、创建目录/tmp/dir-当前日期时间;
3、在/tmp/dir-当前日期时间 目录中创建9个空文件file101-file109
4、将file101的属主改为tuser1,依次类推,一直将file109的属主改为tuser9;
练习:写一个脚本
1、脚本可以接受一个以上的文件路径作为参数;
2、显示每个文件所拥的行数;
3、显示本次共对多少个文件执行了行数统计;
!/bin/bash
#
for file in $*; do
lines=`wc -l $file | cut -d' ' -f1`
echo "$file has $lines lines."
done
echo "$# files."
练习:写一个脚本
1、显示/etc/passwd文件中位于文件的第偶数行的用户名;并显示共有多少个这样的用户;
#!/bin/bash
#
totalUsers=`wc -l /etc/passwd | cut -d' ' -f1`
for i in `seq 2 2 $totalUsers`; do
userName=`head -n $i /etc/passwd | tail -1 | cut -d: -f1`
echo $userName >> /tmp/passwd.tmp
echo $userName
done
users=`wc -l /tmp/passwd.tmp | cut -d' ' -f1`
echo "Total users: $users."
生成列表的方式:
1、手动给个列表:
for i in 1 2 3 4 5;
2、数值列表:
{start..end}
`seq [start [increment]] end`
3、$*, $@
4、命令生成列表
bash如何实现算术运算:
变量:弱类型
如何定义整型变量:
let VAR_NAME=INTEGER_VALUE
例如:let a=3
declare -i VAR_NAME=INTEGER_VALUE
例如:declare -i a=3
注意:即使没有定义为整型变量,字符型的数字依然可以参与算术运算;bash会执行变量类型的隐式类型转换;
实现算术运算的方式:
let VAR_NAME=ARITHMATIC_EXPRESSION
VAR_NAME=$[ARITHMATIC_EXRESSION]
VAR_NAME=$((EXPRESSION))
VAR_NAME=$(expr $num1 + $num2)
算术运算符:
+
-
*
/
%:取模,取余数
5%2=1,
**: 2**2
练习:计算100以内所有正整数之和
#!/bin/bash
#
declare -i sum=0
for i in {1..100}; do
sum=$[$sum+$i]
done
echo $sum
练习:分别计算100以内所有偶数之和和奇数之和;
#!/bin/bash
#
declare -i evensum=0
declare -i oddsum=0
for i in `seq 1 2 100`; do
oddsum=$[$oddsum+$i]
done
for j in `seq 2 2 100`; do
evensum=$[$evensum+$j]
done
echo "evensum: $evensum, oddsum: $oddsum."
练习:计算当前系统上所有用户的ID之和;
declare -i idsum=0
for i in `cut -d: -f3 /etc/passwd`; do
let idsum+=$i
done
echo $idsum
练习:写一个脚本
1、脚本可以接受一个以上的文件路径作为参数;
2、显示每个文件所拥有的行数;
3、显示本次共对多少个文件执行了行数统计;
4、显示所有文件的总行数;
#!/bin/bash
#
declare -i totalLines=0
declare -i noFiles=0
for file in $*; do
curFileLines=`wc -l $file | cut -d' ' -f1`
echo "$file has $curFileLines."
let noFiles++
let totalLines+=$curFileLines
done
echo "Total Files: $noFiles."
echo "Total Lines: $totalLines."
练习:新建10个用户tuser401-tuser410,并求他们的ID之和;
#!/bin/bash
#
declare -i idsum=0
for i in {401..410}; do
useradd tuser$i
userID=`id -u tuser$i`
let idsum+=$userID
done
echo "ID sum: $idsum."
练习:写一个脚本
1、创建用户tuser501-tuser510;
2、创建目录/tmp/dir-当前日期时间;
3、在/tmp/dir-当前日期时间 目录中创建9个空文件file101-file110
4、将file101的属主改为tuser501,依次类推,一直将file110的属主改为tuser510;
练习:写一个脚本
分别统计/etc/rc.d/rc.sysinit、/etc/rc.d/init.d/functions和/etc/inittab文件中以#开头的行的行数和空白行数;
#!/bin/bash
for file in /etc/rc.d/rc.sysinit /etc/rc.d/init.d/functions /etc/inittab; do
echo "The lines contain # in $file is `grep -E "^#" $file | wc -l`."
echo "The space lines in $file is `grep -E "^[[:space:]]*$" $file | wc -l`."
done
练习:写一个脚本
显示当前系统上所有默认shell为bash的用户的用户名、UID及其所有此类用户的UID之和;
#!/bin/bash
#
grep "/bin/bash$" /etc/passwd | cut -d: -f1,3
declare -i sum=0
for userID in `grep "/bin/bash$" /etc/passwd | cut -d: -f3`; do
let sum+=$userID
done
echo "$sum"
bash弱类型:
变量=值
任何无需事先声明,可直接使用
值默认都是字符型
a=abc, b=3
a=3
赋值:
a=4
增强型赋值:
+=, -=, *=, /=, %=
a=$[$a+1] 相当于 let a+=1
自加:var++, var--, ++var, --var
export PATH=$PATH:/usr/local/apache/bin
unset: 撤消
算术运算:bash会对数字执行隐式的类型转换
let VAR_NAME=Integer_Value
declare -i Var_Name=Integer_Value
操作符:
+, -, *, /, %, **
双目运算符:需要至少两个操作数
bash的算术运算的方式:
let Var_Name=EXPRESSION
$[EXPRESSION]
$((EXPRESSION))
命令:expr ARG1 OP ARG2
for循环:
新建10个用户:tuser601-tuser610
useradd $userName
for Var in LIST; do
for userName in tuser601 tuser602 tuser603; do
useradd $userName
done
for i in {601..610}; do
useradd tuser$i
done
遍历LIST元素,遍历结束,循环退出;
bash中的字串连接:
变量引用后方跟直接字串时,变量名要加{}
求100以内所有正整数的和:
declare -i sum=0
sum+=1, 0+1
sum+=2, 0+1+2
sum+=3
sum+=4
...
sum+=100
declare -i sum=0
for i in {1..100}; do
let sum+=$i
done
echo $sum
练习:写一个脚本,显示当前系统上有附加组的用户的用户名;并统计共有多少个此类用户;
for userName in `cut -d: -f1 /etc/passwd`; do
id $userName |
# egrep '[^:]$' /etc/group | cut -d: -f4 | sort -u | egrep -o '[[:alnum:]]*' | sort -u
写一个脚本,创建十个用户tuser401, tuser410
实现某种操作:总是 测试 前提是否满足
/tmp/test
10
逻辑运算:
布尔运算:真,假
与、或、非、异或
与运算:
真,假:
真 && 真 = 真
真 && 假 = 假
假 && 真 = 假
假 && 假 = 假
或运算:
真,假
真 || 真 = 真
真 || 假 = 真
假 || 真 = 真
假 || 假 = 假
非运算:
真,假
异或运算:
命令都有其状态返回值:
成功:0,真
失败:1-255, 假
bash条件测试:
命令执行成功与否即为条件测试
test EXPR
[ EXPR ]
[[ EXPR ]]
比较运算:
>, <, >=, <=, ==, !=
测试类型:根据比较时的操作数的类型
整型测试:整数比较
字符测试:字符串比较
文件测试:判断文件的存在性及属性等
注意:比较运算通常只在同一种类型间进行
整型测试:
-gt: 例如 [ $num1 -gt $num2 ]
-lt:
-ge:
-le:
-eq:
-ne:
字符串测试:
双目
>: [[ "$str1" > "$str2" ]]
<:
>=
<=
==
!=
单目:
-n String: 是否不空,不空则为真,空则为假
-z String: 是否为空,空则为真,不空则假
过程式编程:
顺序
选择
循环:for
选择:if和case
if: 三种使用格式
单分支的if语句:
if 测试条件; then
选择分支
fi
表示条件测试状态返回值为值,则执行选择分支;
if ! id $username &> /dev/null; then
useradd $username
fi
练习:写一个脚本,接受一个参数,这个参数是用户名;如果此用户存在,则显示其ID号;
双分支的if语句:
if 测试条件; then
选择分支1
else
选择分支2
fi
两个分支仅执行其中之一。
练习:通过命令行传递两个整数参数给脚本,脚本可以返回其大者。
练习:通过命令行传递任意个整数给脚本,脚本可以返回其大者。
练习:通过命令行给定一个文件路径,而后判断:
如果此文件中存在空白行,则显示其空白行的总数;
否则,则显示无空白行;
if grep "^[[:space]]*$" $1 &> /dev/null; then
echo "$1 has $(grep "^[[:space]]*$" $1 | wc -l) blank lines."
else
echo "No blank lines"
fi
注意:如果把命令执行成功与否当作条件,则if语句后必须只跟命令本身,而不能引用。
if [ $(grep "^[[:space:]]*$" $1 | wc -l) -lt 1 ]
多分支的if语句:
if 条件1; then
分支1
elif 条件2; then
分支2
elif 条件3; then
分支3
...
else
分支n
fi
练习:传递一个参数给脚本:
如果参数为quit,则显示说你要退出;
如果参数为yes,则显示说你要继续
其它任意参数,则说无法识别;
练习:传递一个用户名给脚本:
如果此用户的id号为0,则显示说这是管理员
如果此用户的id号大于等于500,则显示说这是普通用户
否则,则说这是系统用户;
#!/bin/bash
#
if [ $# -lt 1 ]; then
echo "Usage: `basename $0` username"
exit 1
fi
if ! id -u $1 &> /dev/null; then
echo "Usage: `basename $0` username"
echo "No this user $1."
exit 2
fi
if [ $(id -u $1) -eq 0 ]; then
echo "Admin"
elif [ $(id -u $1) -ge 500 ]; then
echo "Common user."
else
echo "System user."
fi
if 测试条件; then
测试条件:在bash中是命令 (test EXPR, [ EXPR ] ) 或由 [[ EXPR ]]
if 命令;
在bash运行至if时,其后的命令会被执行,其状态结果则作为判断标准:
0:表示真
1-255:表示假
如果条件包含比较之意,则必须使用
自定义shell进程的状态返回值:
exit [n]
回顾:
条件测试:
整型测试:数值间的大小比较
-gt, -lt, -eq, -ne, -ge, -le
字符串测试:字符串大小比较
>, <, ==, !=, =~, -n, -z
文件测试
例如:如果当前主机的主机名为localhost,则将其修改为www.magedu.com
比较时,
if [ `hostname` == 'localhost' ]; then
hostname www.magedu.com
fi
例如:如果当前主机的主机名为空,则将其修改为用户通过命令行参数传递过来的用户名
hostName=`hostname`
if [ -z "$hostName" ]; then
hostname $1
fi
组合条件测试:在多个条件间实现逻辑运算
与:[ condition1 -a condition2 ]
condition1 && condition2
或:[ condition1 -o condition2 ]
condition1 || condition2
非:[ -not condition ]
! condition
例如:如果当前主机的主机名为空,或者为"localhost",则将其修改为www.magedu.com
#!/bin/bash
#
hostName=`hostname`
if [ -z "$hostName" -o "$hostName" == 'localhost' ]; then
hostname www.magedu.com
fi
如果某用户存在,则显示id号:
if id $userName &> /dev/null; then
id -u $userName
fi
例如:如果某用户存在,且answer变量的值为“yes",则显示用户的ID号;否则,说用户选择了退出;
id $userName
retVal=$?
if [ $retval -eq 0 -a "$answer" == 'yes' ]; then
上述方式改为:
if id $userName &> /dev/null && [ "$answer" =='yes' ]; then
例如:如果answer不为"quit",也不为"q",则说用户选择了继续;
例如:如果answer不为quit或q,则说明用户选择了继续;
德 摩根定律:
练习:给定一个用户,如果其shell为/bin/bash且其ID号大于等于500,则说这是一个可登录普通用户;否则,则显示其为非登录用户或管理员。
#!/bin/bash
#
if ! id $1 &> /dev/null; then
echo "No this user."
exit 3
fi
userShell=$(grep "^$1\>" /etc/passwd | cut -d: -f7)
userID=$(id -u $1)
if [ "$userShell" == '/bin/bash' -a $userID -ge 500 ]; then
echo "Login user."
else
echo "not login user."
fi
练习:写一个脚本
如果某用户不存在,就添加之;
#!/bin/bash
#
if ! id $1 &> /dev/null; then
useradd $1
fi
练习:写一脚本
1、添加10个用户:tuser501-tuser510
如果用户不存在,才添加;如果存在,则显示已经有此用户
2、显示一共添加了多少个用户;
#!/bin/bash
#
declare -i count=0
for i in {501..510}; do
if id tuser$i &> /dev/null; then
echo "tuser$i exists."
else
useradd tuser$i
let count++
fi
done
echo "Total add $count users."
练习:写一脚本
1、添加10个用户:tuser601-tuser610
如果用户不存在,才添加,并以绿色显示添加成功;如果存在,则以红色显示已经有此用户;
2、显示一共添加了多少个用户;
#!/bin/bash
#
declare -i count=0
for i in {501..510}; do
if id tuser$i &> /dev/null; then
echo -e "\033[31mtuser$i\033[0m exists."
else
useradd tuser$i
echo -e "add user \033[32mtuser$i\033[0m successfully."
let count++
fi
done
echo "Total add $count users."
练习:写一个脚本
传递用户名给脚本
1、判断此用户的shell是否为/bin/bash,如果是,则显示此用户为basher
2、否则,则显示此用户为非basher
#!/bin/bash
#
userShell=`grep "^$1\>" /etc/passwd | cut -d: -f7`
if [ "$userShell" == '/bin/bash' ]; then
echo "basher"
else
echo "not basher"
fi
在剩下的三月里,你愿意与学习结为伴侣,无论贫穷还是富贵,无论电脑还是手机,无论多困或者多累,无论想吃还是想睡,都要把学习放在第一位,以不落后为目标,同甘共苦同舟共济永不言弃,爱惜她尊重她理解她保护她,你愿意这样做么?
Yes, I do.
bash条件测试之文件测试:
-a file
True if file exists.
-b file
True if file exists and is a block special file.
-c file
True if file exists and is a character special file.
-d file
True if file exists and is a directory.
-e file
True if file exists.
-f file
True if file exists and is a regular file.
-g file
True if file exists and is set-group-id.
-h file
True if file exists and is a symbolic link.
-k file
True if file exists and its ''sticky'' bit is set.
-p file
True if file exists and is a named pipe (FIFO).
-r file
True if file exists and is readable.
-s file
True if file exists and has a size greater than zero.
-t fd True if file descriptor fd is open and refers to a terminal.
-u file
True if file exists and its set-user-id bit is set.
-w file
True if file exists and is writable.
-x file
True if file exists and is executable.
-O file
True if file exists and is owned by the effective user id.
-G file
True if file exists and is owned by the effective group id.
-L file
True if file exists and is a symbolic link.
-S file
True if file exists and is a socket.
-N file
True if file exists and has been modified since it was last read.
file1 -nt file2
True if file1 is newer (according to modification date) than file2, or if file1 exists and file2 does not.
file1 -ot file2
True if file1 is older than file2, or if file2 exists and file1 does not.
file1 -ef file2
True if file1 and file2 refer to the same device and inode numbers.
-o optname
True if shell option optname is enabled. See the list of options under the description of the -o option to the set builtin
below.
-a FILE
-e FILE: 存在则为真;否则则为假;
-f FILE: 存在并且为普通文件,则为真;否则为假;
-d FILE: 存在并且为目录文件,则为真;否则为假;
-L/-h FILE: 存在并且为符号链接文件,则为真;否则为假;
-b: 块设备
-c: 字符设备
-S: 套接字文件
-p: 命名管道
-s FILE: 存在并且为非空文件则为值,否则为假;
-r FILE
-w FILE
-x FILE
file1 -nt file2: file1的mtime新于file2则为真,否则为假;
file1 -ot file2:file1的mtime旧于file2则为真,否则为假;
例如:如果wget命令对应的可执行文件存在且可执行,则使用它下载http://172.16.0.1/centos6.5.repo至当前目录中;
#!/bin/bash
#
downURL='http://172.16.0.1/centos6.5.repo'
downloader=`which wget`
if [ -x $downloader ]; then
$downloader $downURL
fi
练习:给定一个文件路径
1、判断此文件是否存在;不存在,则说明文件不存,并直接结束脚本;
2、如果文件是否普通文件,则显示为“regular file”;
如果文件是目录,则显示为“directory”;
如果文件是链接文件,则显示为“Symbolic file";
否则,则显示为“unknown type.”
#!/bin/bash
#
if [ ! -e $1 ]; then
echo "file not exist."
exit 8
fi
if [ -L $1 ]; then
echo "Symbolic file"
elif [ -d $1 ]; then
echo "Directory"
elif [ -f $1 ]; then
echo "regular file."
else
echo "unknown."
fi
练习:写一个脚本,完成如下任务:
1、分别复制/var/log下的文件至/tmp/logs/目录中;
2、复制目录时,才使用cp -r
3、复制文件时,使用cp
4、复制链接文件,使用cp -d
5、余下的类型,使用cp -a
写一个脚本,完成如下任务,其使用形式如下所示:
script.sh {start|stop|restart|status}
其中:
如果参数为空,则显示帮助信息,并退出脚本;
如果参数为start,则创建空文件/var/lock/subsys/script,并显示“starting script successfully.”
如果参数为stop,则删除文件/var/lock/subsys/script,并显示“Stop script successfully.”
如果参数为restart,则删除文件/var/locksubsys/script并重新创建,而后显示“Restarting script successfully.”
如果参数为status,那么:
如果文件/var/lock/subsys/script存在,则显示“Script is running...”,否则,则显示“Script is stopped.”
说明:script.sh是脚本文件名,在创建时,其名称可以自己随意定义,但如果其名称发生变量,上/var/lock/sussys/下的文件名也要随之而变;
bash编程之交互编程
read
-p "prompt"
-t timeout
例如:输入用户名,可返回其shell
#!/bin/bash
#
read -p "Plz input a username: " userName
if id $userName &> /dev/null; then
echo "The shell of $userName is `grep "^$userName\>" /etc/passwd | cut -d: -f7`."
else
echo "No such user. stupid."
fi
例子:显示一个如下菜单给用户:
cpu) show cpu infomation
mem) show memory infomation
*) quit
1、如果用户选择了cpu,则显示/proc/cpuinfo文件的内容;
2、如果用户选择了mem,则显示/proc/meminfo文件的内容;
3、退出
#!/bin/bash
#
echo "---------menu----------"
echo "cpu) show cpu infomation"
echo "mem) show memory infomation"
echo "*) quit"
echo "-------menu------------"
read -p "Plz give your choice: " choice
if [ "$choice" == 'cpu' ]; then
cat /proc/cpuinfo
elif [ "$choice" == 'mem' ]; then
cat /proc/meminfo
else
echo "Quit"
exit 3
fi
#!/bin/bash
#
cat << EOF
-------menu------------
cpu) show cpu infomation
mem) show memory infomation
*) quit
-------menu------------
EOF
read -p "Plz give your choice: " choice
if [ "$choice" == 'cpu' ]; then
cat /proc/cpuinfo
elif [ "$choice" == 'mem' ]; then
cat /proc/meminfo
else
echo "Quit"
exit 3
fi
字串测试中的模式匹配
[[ "$var" =~ PATTERN ]]
例如:让用户给定一个用户名,判断其是否拥有可登录shell;
/bin/sh, /bin/bash, /bin/zsh, /bin/tcsh, /sbin/nologin, /sbin/shutdown
#!/bin/bash
#
read -p "Plz input a username: " userName
userInfo=`grep "^$userName\>" /etc/passwd`
if [[ "$userInfo" =~ /bin/.*sh$ ]]; then
echo "can login"
else
echo "cannot login"
fi
练习:写一个脚本,完成如下功能
使用格式:
script.sh /path/to/somefile
1、可接受一个文件路径参数:
如果此文件不存在,则创建之,则自动为其生成前n行类似如下:
#!/bin/bash
# description:
# version:
# date:
# author: mageedu
# license: GPL
而后使用vim打开此文件,并让光标处在最后一行的行首
如果文件存在、且是bash脚本,则使用vim打开之,光标自动处行最后一行的行首;
否则,退出;
如果正常编辑保存,
判断,如果文件没有执行权限,则添加之;
判断,其是否有语法错误,如果有,提示;
文件查找:根据文件的各种属性去找到相对应文件
文本搜索:grep, egrep, fgrep
文件查找:locate, find
实时查找:遍历所有文件进行条件匹配
非实时查找:根据索引查找
locate: 非实时查找
依赖于索引,而索引构建相当占用资源;索引的创建是在系统空闲时由系统自动进行(每天任务);手动进行使用updatedb命令;
查找速度快
非精准查找
模糊查找
find: 实时查找
精准查找
精确查找
速度慢
find [option]... [查找路径] [查找条件] [处理动作]
查找路径:默认为当前目录
查找条件:默认为指定路径下的所有文件
处理动作:默认为显示至屏幕
查找条件:
-name "文件名称":支持使用globbing字符
*:
?:
[]:
[^]:
-iname "文件名称":查找时忽略字符大小写
-user USERNAME: 根据文件的属主查找
-group GRPNAME: 根据文件的属组查找
-uid UID
-gid GID
-nouser: 查找没有属主的文件
-nogroup: 查找没有属组的文件
组合条件查找:
与:-a, 同时满足
或:-o, 满足一个即可
非:-not, !,条件取反
-not A -a -not B = -not (A -o B)
-not A -o -not B = -not (A -a B)
例子:-not \( -iname "*r* -o -user gentoo \)
-type TYPE: 根据文件类型查找
f: 普通文件
d: 目录文件
l: 符号链接
b: 块设备
c: 字符设备
s: 套接字文件
p: 命名管道
-size [+|-]#UNIT
常用单位: k, M, G
#UNIT: #-1 < x <= #
-#UNIT: x <= #-1
+#UNIT: x > #
根据时间戳查找:
以“天”为单位
-atime [+|-]#
+#:x >= #+1
-#:x < #
#: # <= x < #+1
-mtime
-ctime
以“分钟”为单位
-amin
-mmin
-cmin
根据权限查找:
-perm [+|-]MODE
MODE: 与MODE精确匹配
find ./ -perm 644
+MODE: 任何一类用户的权限只要能包含对其指定的任何一位权限即可;以属主为例,
find ./ -perm +222
-MODE:每类用户指定的检查权限都匹配:
为三类用户所有指定的检查权限都能够被包含
find ./ -perm -222
处理动作:
-print: 默认处理动作,显示
-ls:类似于ls -l
-exec COMMAND {} \;
-ok COMMAND {} \;
find: 一次性查找符合条件的所有文件,并一同传递给给-exec或-ok后面指定的命令;但,有些命令不能接受过长的参数;此时使用另一种方式
find | xargs COMMAND
总结:find [查找路径] [查找条件] [处理动作]
查找条件:
-name, -iname, -user, -group, -uid, -gid, -nouser, -nogroup, -type, -size, -atime, -mtime, -ctime, -amin, -mmin, -cmin, -perm
组合:-a, -o, -not
处理动作:
练习:
1、查找/var/目录属主为root且属组为mail的所有文件;
find /var -user root -a -group mail
2、查找/usr目录下不属于root、bin或hadoop的所用文件;
find /usr -not -user root -a -not -user bin -a -not -user hadoop
find /usr -not (-user root -o -user bin -o -user hadoop)
3、查找/etc/目录下最近一周内其内容修改过的,且不属于root且不属于hadoop的文件;
find /etc -mtime -7 -a -not (-user root -o -user hadoop)
4、查找当前系统上没有属主或属组,且最近1个月内曾被访问过的文件;
find / (-nouser -o -nogroup) -a -atime -30
5、查找/etc/目录下大于1M且类型为普通文件的所有文件;
find /etc -size +1M -type f
6、查找/etc/目录所有用户都没有写权限的文件;
find /etc/ -not -perm +222
7、查找/etc/目录下至少有一类用户没有写权限;
find /etc/ -not -perm -222
8、查找/etc/init.d/目录下,所有用户都有执行权限且其它用户有写权限的文件;
find /etc/init.d/ -perm -113
find补充材料(摘自互联网):
find与xargs
在使用find命令的-exec选项处理匹配到的文件时, find命令将所有匹配到的文件一起传递给exec执行。但有些系统对能够传递给exec的命令长度有限制,这样在find命令运行几分钟之后,就会出现 溢出错误。错误信息通常是“参数列太长”或“参数列溢出”。这就是xargs命令的用处所在,特别是与find命令一起使用。
find命令把匹配到的文件传递给xargs命令,而xargs命令每次只获取一部分文件而不是全部,不像-exec选项那样。这样它可以先处理最先获取的一部分文件,然后是下一批,并如此继续下去。
在有些系统中,使用-exec选项会为处理每一个匹配到的文件而发起一个相应的进程,并非将匹配到的文件全部作为参数一次执行;这样在有些情况下就会出现进程过多,系统性能下降的问题,因而效率不高;
而使用xargs命令则只有一个进程。另外,在使用xargs命令时,究竟是一次获取所有的参数,还是分批取得参数,以及每一次获取参数的数目都会根据该命令的选项及系统内核中相应的可调参数来确定。
特殊权限:
mode:
ls -l
安全上下文
1、进程以某用户身份运行;进程是发起此进程的用户的代理,其用户身份进程发起者;
2、权限匹配模型:
(1) 进程的属主,是否与被访问的文件属主相同;
(2) 进程的属主所属于的组当中,是否有一个与被访问的文件的属组相同;
(3) 以other的权限进行访问;
suid: Set UID
前提:此类文件为有可执行权限的命令
任何用户运行此命令为一个进程时,此进程的有效身份不是发起者,而是命令文件自身的属主;
chmod u+s FILE...
使用ls -l查看时,此s可能显示为大写或小写两种形式之一;
属主原有执行权限时,显示为小写;
sgid: Set GID
前提:
常用方法:如果将目录的属组设置SGID权限之后,所有用户在此目录创建文件的属组不再是用户的基本组,而是目录的属组
chmod g+s FILE...
有那么一个目录:
指定的用户都能够在其中创建文件,也能删除自己的文件;但不能删除别人的文件;
sticky: 沾滞位
chmod o+t FILE...
suidsgidsticky
000:
001:
010
011
100
101
110
111
chmod 7755
练习:
1、让普通用户使用/tmp/cat能查看管理员才有权限访问的文件;
2、新建目录/project/test,让普通用户hadoop和openstack对此目录都能创建文件,且创建的文件的属组为此目录的属组,而非用户自身的属组,此外还要求,每个用户不能删除其它人的文件;
回顾:
find 和 特殊权限
find:
1、-type b
2、-size +20M -a -atime +7
suid, sgid, sticky
任务计划和facl:
一次性任务执行:at, batch
周期性任务执行:crontab
一次性任务执行:
at TIME
at> ls /etc
at> ls -l /var
at> wc -l /etc/fstab
at>
Ctrl+d:提交任务
TIME:
模糊时间:
now+#UNIT
noon
midnight
teatime
tomorrow
hh:mm
at有作业队列:
使用单个字母表示
atq: 查看作业队列中的待执行任务
at -l
任务作业执行完成后:结果会通过邮件通知给作业提交者
CentOS: 默认会安装并启动邮件服务,仅服务于本地系统;
# mail
at -d = atrm
batch: 功能同at, 但无须为其指定时间,其会选择系统资源较空闲时执行指定的作业
周期性任务执行:cron, vixie-cron, cronie
crond: cron daemon, cron守护进程,一直工作于后台
# service crond status
crond (pid 1718) is running...
如果状态信息为“is stopped.”,则需要执行“service crond start; chkconfig crond on”;
cron任务分两类:
系统cron: 定义在/etc/crontab
用户cron: 定义在/var/spool/cron目录中,每个用户都有一个与用户名同名的文件,其功能类似于/etc/crontab
每行定义一个独立的任务:
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
HOME=/
# 环境变量
# 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
注意:如果定义在cron中的任务是脚本,此脚本中的各命令查找时遵循的是cron的PATH环境变量定义;因此,在命令行中测试无问题的脚本,在cron中未必;
解决方案:
命令使用绝对路径
在脚本中开始自己定义一个专用PATH环境变量
用户cron的每个作业由2部分组成
时间
分钟 小时 日 月 周几
命令
时间表示法:
1、每个时间位都应该使用其可用的有效取值范围内的值
3 * * * * /bin/echo "howdy"
17 3 * * * /bin/echo "howdy"
7 6 5 * * /bin/echo "howdy"
7 6 5 4 *
1 9 * * 3 /bin/echo "howdy"
2、某个时间位上的*表示对应时间位的所有有效取值;
3、*/#: 在对应的时间位的有效取值上每#一次;
*/3 * * * *
0 */3 * * *
*/8 * * * *: ?
0-59
0, 8, 16, 24, 32, 40, 48, 56,
4、某个时间位上的连续区间:-
10-50/3 * * * *
5、离散取值:,
1 7,9,13 * * *
丢弃邮件通知:
输出重定向:
> /dev/null
&> /dev/null
> /dev/null 2>&1
用户cron任务的定义方式:
crontab
-l: 列出已经定义的所有任务
-e: 打开编辑界面定义任务
-r: 移除所有任务
crontab -u USERNAME -e
注意:如果在crontab的用户命令中使用%,得转义,使用\%的格式;不过,如果使用单引号引起来,也可不用转义;
5 3 * * * /bin/touch /tmp/testfile-$(date +\%F-\%H-\%M-\%S)
6 4 * * * /bin/touch /tmp/testfile-$(date +'%F-%H-%M-%S')
练习:
1、每4小时执行一次对/etc/目录的备份,备份至/backup目录中,保存的目录名为etc-2014071004;
0 */4 * * * [ -d /backup ] || mkdir /backup; /bin/cp -a /etc /backup/etc-$(date +'%Y%m%d%H')
2、每周3,5,7备份/var/log/messages文件至/backup/message_logs/目录中,保存的文件名为messages-2014071004;
3 1 * * 3,5,7
3、每天每两小时取当前系统/proc/meminfo中的以S开头的信息保存至/stats/memory.txt中
2 */2 * * * grep -i "^S" /proc/meminfo >> /stats/memory.txt
4、工作日的工作时间内,每小时执行一次'echo "howdy"'
10 8-18 * * 1-5 echo "howdy"
bash编程中:
逻辑操作:
&&: condition1 && condition2
||: condition1 || condition2
如何实现秒级别的任务:
每10秒钟:
* * * * * for i in {1..5}; do echo "howdy"; sleep 10; done
0
10
20
30
50
0
anacron: crontab的补充机制
检查有没有过去一个有效周期未曾执行的任务,如果有,在开机后的指定时间点执行一次;
总结:
任务计划:
一次性:at, batch
周期性:cron
cron:
PATH环境变量
任务时间格式
facl: file acl
acl: access control list
chown: 普通用户能否改变文件的属主属组?
chmod: 普通用户是否有权限使用?
openstack用户,docker(读写)
通过facl机制,普通用户可以单独向某用户或某组设定对某文件访问权限;不通过修改属主或属组来实现的。
文件系统:
ext: extended
ext2, ext3, ext4,
xfs
lsattr, chattr
i
u
facl:
让普通用户透过文件的扩展属性,为其添加额外的用户访问授权机制而无须改变其属主、属组,也不用更改other的权限;
getfacl
setfacl {-m|-x} 目标:MODE FILE...
-m u:USERNAME:MODE
-m g:GROUPNAME:MODE
-x u:USERNAME
-x g:GROUPNAME
-R: 递归
启用facl之后权限应用模型:
属主:
用户级别的facl:
属组:
组级别的facl:
其它
bash编程之循环:
顺序
选择:if
循环:for
另一种循环:while 和 until
for i in {1..100}; do
done
while适用于循环次数未知,或不便用for直接生成较大的列表时;
while 测试条件; do
循环体
done
如测试结果为“真”,则进入循环;退出条件为,测试条件为假;
declare -i count=1
while $count <= 1000; do
循环体
let count++
done
until 测试条件; do
循环体
done
如果测试结果为“假”,则进入循环;退出条件为,测试条件为真;
练习:求100以内所有正整数之和
#!/bin/bash
#
declare -i count=1
declare -i sum=0
until [ $count -gt 100 ]; do
let sum+=$count
let count++
done
echo $sum
#!/bin/bash
#
declare -i count=1
declare -i sum=0
while [ $count -le 100 ]; do
let sum+=$count
let count++
done
echo $sum
练习:求100以内所有偶数之和;要求使用取模方法;
#!/bin/bash
#
declare -i counter=1
declare -i sum=0
while [ $counter -le 100 ]; do
if [ $[$counter%2] -eq 0 ]; then
let sum+=$counter
fi
let counter++
done
echo $sum
#!/bin/bash
#
declare -i counter=1
declare -i sum=0
while [ $counter -le 100 ]; do
[ $[$counter%2] -eq 0 ] && let sum+=$counter
let counter++
done
echo $sum
请用until实现上述过程;
练习:提示用户输入一个用户名,如果用户存在,就显示用户的ID号和shell;否则显示用户不存在;
显示完成之后不退出,再次重复前面的操作,直到用户输入q或quit为止;
read -p "Plz enter a username: " userName
while [ "$userName" != 'q' -a "$userName" != 'quit' ]; do
if id $userName &> /dev/null; then
grep "^$userName\>" /etc/passwd | cut -d: -f3,7
else
echo "No such user."
fi
read -p "Plz enter a username again: " userName
done
请用until实现上述过程;
回顾:facl, at, crontab, while, until