编写脚本
vim script.sh
#!/bin/bash(env/pathon) shebang机制,
变量名=值
等号与值之间有空格,比较
等号与值之间无空格,赋值
调用:echo "...$变量名"
不加$,打印字符串
加$,打印值
加$,系统能够判断出这是个变量
chmod +x script.sh
bash -n script.sh
bash -x script.sh
./script.sh or /root/script.sh
绝对路径或者放入path变量路径
编程基础
程序:指令+数据
程序编程风格
过程式:指令为中心,数据服务于指令
对象式:数据为中心,指令服务于数据
shell程序:提供编程能力,解释执行
程序执行方式
计算机:二进制指令
编程语言 低级 高级
编译:高级语言 编译器 目标代码 java/c++
解释:高级语言 解释器 机器代码 shell/python/perl
编程逻辑处理方式
顺序执行
循环执行
选择执行
shell编程
过程式 解释执行
编程语言的基本结构
各种系统命令的组合
数据存储:变量/数组
表达式:a+b
语句:if
shell脚本
包含一些命令和声明,并符合一定格式的文本文件
格式要求:首行是shebang机制
#!/bin/bash
#!/usr/bin/python
#!/usr/bin/perl
用途
自动化常用命令
执行系统管理和故障排查
创建简单的应用程序
处理文本和文件
创建shell脚本
第一,文本编辑器来创建文件
vim hello.sh
#!/bin/bash 使用调用的语言
#filename:hello.sh
#version:2.0
#date:2017-10-20 更改后的时间
#author:fenggenqiang 作者信息
#discription:This is for ... 该程序的作用和注意事项
#各种版本的更新简要说明
脚本调试
bash -n /path/to/script.sh 检查脚本中的语法错误(systax error)
bash -x /path/to/script.sh 显示脚本执行cmd的结果
第二,运行脚本
给予执行权限,在命令行上指定脚本的绝对或相对路径(一般,都要加X权限)
chmod +x hello.sh
或者 bash hello.sh
或者 ./source hello.sh
hello.sh不在PATH变量里
/root/hello.sh 写绝对路径
mv hello.sh bin/ ; hello.sh (注意:cd /bin 和 cd bin/ 二者不同)
或者 mv /root/bin/hello.sh /usr/local/bin/ ,
移动之后,hello.sh找不到路径,hash -d hello.sh 删除缓存即可(hash -r 删除所有缓存)
不同的软链接,对应相同内容,但执行效果不同
a1.sh —— a.sh
a2.sh —— a.sh
#!/bin/bash
echo ...
xxx
if syntax error,阻止后续的执行
echo "continue"
#!/bin/bash
echo ...
xxx cmd error,可以向后继续执行
echo "continue"
编译中出现语法错误,就不会执行(转化为2进制)
解释器不同,打开子进程不同
echo $$ 显示当前进程编号
echo $PID 显示父进程,系统自带,非定义变量
pstree (-p) 显示全部进程编号
hello.sh
#!...
echo "..."
f2.sh(调用文件)
f2.sh
#!...
echo "..."
sleep 100(调用命令)
hello.sh调用f2.sh,f2.sh调用sleep
bash hello.sh 在bash进程中,打开脚本(hello.sh) bash——bash——f2.sh——sleep
./source hello.sh 在当前进程中,打开脚本 bash——f2.sh——sleep
hello.sh 在脚本进程中打开脚本 bash——hello.sh——f2.sh——sleep
bash+脚本(bash hello.sh)
or 脚本(hello.sh) bash或脚本执行调用,将以子进程方式运行
source+脚本 ./source 脚本,将在当前shell运行;恐更改当前工作环境
变量
命名的内存空间
数据存储方式
字符
数值 整型/浮点型(小数)
作用
数据存储格式
参与的运算
表示数据范围
类型
强类型
变量不经过强制转换,永远是个数据类型,不允许隐式的类型转换
定义变量——指定类型
参与运算——符合类型要求
调用未声明的变量——产生错误
JAVA C++
弱类型
语言运行——会隐式做数据类型转换
无须指定类型,默认为字符型;(数据也是字符,加“”,可显示数字)
参与运算——自动进行隐式类型转换
变量无需事先定义,可直接调用
php bash不支持浮点型
变量命名规则
不能使用程序中的保留字:if/for
只能使用数字,字母,下划线,且不能以数字开头 name1√ 1name×
见名知义
统一命名规则:驼峰命名法 第一个字母(单词)
大驼峰StudentNameFirst... JAVA类变量
小驼峰studentNameFirst...
bash中的变量种类(生效范围)
本地变量
生效范围:当前shell进程
变量赋值:name='value'
name="root" 字串
name="$USER" 变量引用
name=`cmd` 命令引用 ‘...’弱引用,不识别变量引用
set 显示已经定义的所有变量
unset name 删除变量,不用加$
环境变量
生效范围:当前shell进程及其子进程
变量声明/赋值:
export name=value
declare -x name=value
变量引用:
$name/${name}
env/printenv/export/declare -x 显示所有环境变量
unset name 删除变量,不用加$
bash内建的环境变量:
PATH/SHELL/USER/UID/HOME/PWD/SHLVL(显示层级)/LANG/MAIL/HOSTNAME/HISTSIZE
echo $变量名
sshd——bash——bash——bash
echo $SHELL 1 2 3
举例
name=father;echo $name
name="father god"; echo $name,有空格,需要加“”
userinfo=`who`;echo "$userinfo",cmd加``,引用cmd输出
father.sh
#!/bin/bash
title=boss(执行,报错:本地变量) |export title=boss(可以执行:环境变量)
echo "father.sh:name=$title"
son.sh
son.sh
#!/bin/bash
echo "son.sh:name=$title"
本地变量:子进程不能用父进程的变量
环境变量:子进程可以用父进程的变量;但是父进程不能用子进程的变量;
局部变量
生效范围:当前shell进程中的某代码片段(常指函数)
位置变量
在脚本代码中调用通过命令行传递给脚本的参数
$1,$2,...:对应第一,第二,...参数,shift[n]换位置,一脚踢几个
只读变量
只能声明,不能修改和删除
声明只读变量
readonly name
declare -r name
查看只读变量
readonly -p
PI=3.14159;退出恢复可修改状态
$0 命令本身
$* 传递给脚本的所有参数,全部参数合为一个字符串
$@ 传递给脚本的所有参数,每个参数做为独立字符串
$# 传递给脚本的参数个数
set -- 清空所有位置变量
$$ 当前进程编号
$PID 父进程,系统自带,非定义变量
练习
vim /root/bin/systeminfo.sh
#!/bin/bash
Hostname=`hostname`
echo "主机名是$Hostname"
Ip=`ifconfig |egrep -o "\<(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0=9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]\>)"`
echo "IP地址是$Ip"
Kernel=`uname -r`
echo "内核版本是$Kernel"
CPUinfo=`lscpu |grep -i "model name"`
echo "CPU信息是$CPUinfo"
Systemversion=`cat /etc/redhat-release`
echo "系统版本是$Systemversion"
Roomfree=`free -m|grep Mem|tr -s " " ":"|cut -d: -f4`
echo "剩余内存空间是$Roomfree"
Roomused=`df -h|grep "/dev/sd"|tr -s " " "%"|cut -d% -f5`
echo "已经使用的内存是$Roomused"
unset Hostname Ip Kernel CPUinfo Systemversion Roomfree Roomused
vim linkstate.sh
#!/bin/bash
Ipv4_linkstate=`netstat -nt |tr -s " " : |cut -d: -f6 |egrep "([0-9]+.{3}[0-9]+)"|sort|uniq -c`
echo "IPV4地址的链接状态是$Ipv4_linkstate"
unset Ipv4_linkstate
vim etcbak_day.sh
#!/bin/bash
today=`date +%F`
cp -av /etc/ /app/etc$today
unset today
vim diskused_max.sh
#!/bin/bash
Max_diskused=`df |grep "/dev/sd"|egrep -o "\<[[:digit:]]+%"|tr -d % |sort -n|tail -n 1`
echo "磁盘使用的最大值是$Max_diskused"
unset Max_diskused
vim position_1.sh
#!/bin/bash
echo $0 #表示脚本名称本身
echo $1 #表示第一个参数
shift #踢走1个参数
echo $2
echo $3
shift 2 #踢走2个参数
echo $4
echo $5
#set --- 表示下面的内容被清空
echo \$*:$*
echo \$@:$@
echo \$#:$#
echo "第十个参数是${10},而不是$10" #注意大于10的,加大括号{}
bash position_1.sh tom jerry obama xoxo gaga lele bibi coco
$0 $1 2 3 4 5 6 7 8
echo $? 判断在bash里执行的最近一条命令成功或者失败 0成功 1-255失败
exit [n] 自定义退出状态码
脚本一旦遇到exit命令,就会立即终止,后面的命令不会执行
退出状态取决于exit命令后面的数字
未给出数字,退出状态码取决于脚本中执行的最后一条命令的状态码
应用:满足不同的状态,返回不同的编码 如:404-找不到网页
$#与$*的区别
a.sh
#!/bin/bash
echo "$*"
bash b.sh "$*" #把$*改为$@,则结果不同(a.sh a b c),$*是abc和abc,$@是abc和a
b.sh
#!/bin/bash
echo "$1"
ping -c1 192.168.227.132 &> /dev/null || echo $?
shift与echo $?结合,判断”参数有无“,位置变量脚本+参数
shift n
n > $# 失败
n ≤ $# 成功
scp 远程主机文件传到本机文件
本机——>远程主机
scp f.sh root@192.168.227.133:/app 把本机当前文件传到远程主机192.168.227.133的/app下
scp -r dir root@192.168.227.133:/app 传送文件夹
远程主机——>本机
scp root@192.168.227.133:/app/f.sh /app/ 把远程主机192.168.227.133的/app/f.sh 传到本机/app下
vim scp.sh
#!/bin/bash
Remotehost="root@192.168.227.133:/app/"
scp $* $Remotehost #$*表示多个参数
unset Remotehost
bash /app/scp.sh /app/file1 /app/file2 把/app下的两个文件传到远程主机
实现自动化生成脚本模板
cd bin/
vim Creatscrp.sh
[[ -z "$1" ]] && read -p "请输入你要创建的脚本名称:"
read name
Scrpname=$name
cat > $name << EOF
#!/bin/bash 使用调用的语言
#filename:hello.sh
#version:2.0
#date:2017-10-20 更改后的时间
#author:fenggenqiang 作者信息
#discription:This is for ... 该程序的作用和注意事项
#各种版本的更新简要说明
#QQ:xxxxxx
#微信:xxxxxx
#手机号:xxxxxx
EOF
chmod +x "$name"
vim + $name #光标移到最后一行
运算
算术运算
help let
+、-、*、/、%(取余/取模)、**
*需要转义:\*
实现计算
let var=算术表达式
var=$[算术表达式]
var=$((算术表达式))
var=$(expr arg1 arg2 ...)
declare -i var=数值
echo '算术表达式' |bc
bash内置的随机数生成器
$RANDOM 0-32767
echo $[$RANDOM%50] 得出的结果是:0-49之间的随机数
赋值
增强型赋值 +=、-=、*=、/=、%=
let count+=3 自己加上3后,给自己赋值
let var+=1 等于 let var++
let var-=1 等于 let var--
练习
let var=2+5;echo $var
x=2;y=5;let z=x+y(或者 z=x*y);echo $z
var=$[2+4];echo $var
x=2;y=4;var=$[x+y](或者var=$[x*y]或者var=$[x**y]);echo $var
expr 2 + 3 注意空格,+、-、/、\*(乘法需要转义)
echo $[$RANDOM%6+1] 0-6之间的随机数,玩骰子
var=0;let var++;let j=var++;echo $j;echo $var 1 2 先赋值再加
var=0;let ++var;let j=++var;echo $j;echo $var 2 2 先加再赋值
用户的id之和 vim and_id.sh
#!/bin/bash
user10=`cut -d : -f 3 /etc/passwd | head -10 |tail -1`
user20=`cut -d : -f 3 /etc/passwd | head -20 |tail -1`
user_and=$[$user10+$user20] #此处的变量名不能为user_10,负责识别不出来
echo $user_and
空格之和 vim and_spaceline.sh 小bug
#!/bin/bash
spaceline1=`grep "^$*" $1 |wc -l`
spaceline2=`grep "^$*" $2 |wc -l`
spaceline_and=$[$spaceline1+$spaceline2]
echo "文件的空行之和为$spaceline_and"
and_dir.sh
#!/bin/bash
dir1=`ls -A /etc |wc -l`
dir2=`ls -A /var |wc -l`
dir3=`ls -A /usr |wc -l`
dirsum=$[$dir1+$dir2+$dir3]
echo $dirsum
逻辑运算
echo $?:0成功 非0失败
true 1 ,false 0 注意区别上面的信息
且/与/&&
1与1=1
1与0=0
0与1=0
0与0=0
或/||
1与1=1
1与0=1
0与1=1
0与0=0
非!
!1=0
!0=1
cmd1 && cmd2 || cmd3 cmd1为真,执行cmd2;cmd1为假,执行cmd3
练习
#!/bin/bash
user=$1
id $user &> /dev/null && echo "$user已经存在" && exit || (useradd $user && echo "$user创建成功") 括号必须要加
chmod +x *
测试
条件测试
测试命令
test expression
[ expression ]
[[ expression ]] expression前后必须要有空白字符
echo $? 若真为0,若假为1
变量加引号
数值测试
-gt 是否大于 -ge 是否大于等于
-eq 是否等于 -ne 是否不等
-lt 是否小于 -le 是否小于等于
== 是否等于 != 是否不等
> 是否大于 < 是否小于
=~ 左侧是否包含右侧
-z "string" 字符串是否为空,空则真
-n "string" 字符串是否不空,不空则真
字符串比较,操作数都用引号
["a" \> "c"] 1
["d" \> "c"] 0
x=abcd;[[ "$x" =~ "ab" ]] && echo yes ||echo no 注意[[ ]]内有空格
[[ "$num" =~ ^[0-9]+$ ]] && echo right ||echo wrong 是否为整数
[[ "$num" =~ ^-?[0-9]+$ ]] && echo right ||echo wrong 是否为正整数/负整数
[ -z "$var" ] && echo true || echo not true
[ -v var ] && echo true || echo not true 字符串是否被设置,被设置,则为真
文件测试 判断顺序:软连接——>其他类型,若是软连接,则判断其对应的文件本身,而不是软连接
-a/e file 是否存在,存在未真
-b file 是否存在,且为块设备
-c file 是否存在,且为字符设备
-d file 是否存在,且为目录
-f file 是否存在,且为普通文件
-p file 是否存在,且为命名管道
大写-S file 是否存在,且为套接字
-h/L file 是否存在,且为符号链接文件
-r file 是否存在,且为可读
-w file 是否存在,且为可写
-x file 是否存在,且为可执行
-u file 是否存在,且拥有suid权限
-g file 是否存在,且拥有sgid权限
-k file 是否存在,且拥有sticky权限
-s file 是否存在,且非空
-t fd 文件描述符是否已经打开
-N file 从上一次读取,是否被修改
-O file 当前用户是否为文件属主
-G file 当前用户是否为文件属组
file1 -ef file2 || echo $? 两个文件是否指向同一个设备上的inode
file1 -nt file2 || echo $? file1是否新于file2
file1 -ot file2 || echo $? file1是否旧于file2
cmd1 && cmd2 expression1 -a expression2
cmd1 || cmd2 expression1 -o expression2
!cmd !expression
[ -L "/bin" ] && echo "软连接"
[ !r $1 -a !w $1 ] && echo "不可读写" || echo "可读写"
[ -f $1 -a "$1" =~ ".sh$" ] && chmod +x $1 && echo "有权限" || echo "无权限"
[ -e $1 ] && echo "你可以登录" || (rm -rf /home/`whoami`;echo "你不能登录") 允许登录
[ -e $1 ] && (rm -rf /home/`whoami`;echo "你不能登录") || echo "你可以登录" 禁止登录
[ -f /etc/nologin ] && (echo "普通用户不能登录") || (touch /etc/nologin)
/etc/nologin存在,普通用户不能登录,删除它,普通用户可以登录 /run/nologin与/etc/nologin具有相同的效果
判断主机是否为空,或等于主机名,如果是,则改为
[ -z "$HOSTNAME" -o "$HOSTNAME"==\"localhost.localdomain" ] && hostname www.fgq.com
判断文件是否有执行权限
判断文件是否存在,判断文件是否有x权限
写脚本,别名无效,不能在脚本中用
练习
参数小于1,提示用户usage:createuser.sh username,并退回脚本,返回状态码100,
判断用户是否存在,若存在,提示用户已经存在,若不存在,创建用户,并提示创建成功
vim createuser.sh
#!/bin/bash
[ $# -lt 1 ] && echo "usage:createuser.sh username" && exit 100
id $1 &> /dev/null && echo "$1已经存在" || (useradd $1 && echo $1创建成功)
vim hostping.sh
#!/bin/bash
ping -c1 -w1 $1 &> /dev/null && [[ $? -eq 0 ]] && echo "可以访问" || echo "不可以访问"
vim checkdisk.sh
#!/bin/bash
disk=$(df |tr -s " " |cut -d " " -f5 |cut -d % -f1 |sort -n |tail -1)
inode=$(df -i|tr -s " " |cut -d " " -f5 |cut -d % -f1 |sort -n |tail -1)
[ $disk -ge 80 ] && (echo "80%"|mail -s dislover80% root) || echo "磁盘空间足够"
[ $inode -ge 80 ] && (echo "80%"|mail -s inodeover80% root) || echo "节点数足够"
vim note.sh
#!/bin/bash
true && (echo true;exist) 注意此处的exist退出,只是退出子程序,并未退出脚本,立即退出脚本:{echo true;exist;}函数(有点错误)
echo not exist
read 把输入的值分配给一个和多个变量
-p 指定显示的提示
-s 不显示输入的信息
-n N 指定输入的字符长度
-d '字符' 输入结束符
-t N 几秒退出
练习
read name sex age
zhao mail 18
echo $name:zhao
read x y z <<< aa bb cc dd
echo $z:cc dd
vim read.sh
#!/bin/bash
echo -n "who do you like" 或者 echo -e "who do you like\c"
read name
echo "you like $name"
vim read1.sh
#!/bin/bash
echo -e "请输入密码:"
stty -echo #不显示输入内容,但可执行此命令
read password
stty echo #显示输入内容,执行此命令
echo "你的密码是$password"
read -s -p "who do you like?" name 静默模式
read -d a example 类似于多行重定向
fff
dfff
fa a独立/不独立都可以
echo $example
read命令不支持管道(|)
配置用户的环境
配置文件
都在/etc下
全局生效
/etc/profile 不建议更改
/etc/profile.d/*.sh 可更改
/etc/bashrc 不建议更改
个人配置
~/.bash_profile 用户家目录下
~/.bashrc
交互式登录
profile类
定义环境变量 运行命令和脚本
终端:用户名+密码
切换:su - 用户名
顺序:/etc/profile ——> /etc/profile.d/*.sh ——> ~/.bash_profile ——> ~/.bashrc ——> /etc/bashrc
非交互式登录
bashrc类
定义命令别名和函数
定义本地变量
顺序:~/.bashrc ——> /etc/bashrc ——> /etc/profile.d/*.sh
修改配置文件后,生效
.或source ~/.bashrc
bash退出任务
保存在~/.bash_logout文件中
在退出登录shell时运行
自动备份 清除临时文件
vim ~/.bash_logout
#~/.bash_logout
rm -rf /app/*
退出登录时,可自动删除/app下的文件内容
黑客删除登录执行cmd信息
同一个变量配置,在多个文件中赋值,最后生效的是最后一次赋值(最后一个文件)
定于对所有用户都生效的别名:/etc/bashrc
所有用户登录,都发出提醒信息:/etc/profile/*.sh,建一个以.sh结尾的文件
在原有的基础上,新增加子串
president=obama
president="tony $obama"
echo $president
echo $PATH
export PATH="$PATH:/usr/local/apache/bin" 路径放在后面
declare -x PATH="/usr/local/apache/bin:$PATH" 路径放在前面,优先找到
echo $PATH 发现路径增加
个人生效
vim ~/.bash_profile
export PATH=$PATH:/usr/local/apache/bin
. ~/.bash_profile
全局配置
vim /etc/profile.d/apache.sh
export PATH=$PATH:/usr/local/apache/bin
. ~/.bash_profile
$-
himBH
h hash缓存
i 交互式
m
B {}展开
H history
set
+/-h +增加功能 -去除功能
+/-i
+/-m
+/-B
+/-H
$_ 缓存,相当于hash,一种功能
面试题
当前进程1283
n=100;echo $$;(echo $$;echo $n;n=200;echo $n;echo $$);echo $n
1283 1283 100 200 1283 100(开启子进程,执行完成后即退出,所以不是200)
vim /etc/profile.d/test.sh
^[[31m hello,dangerous!^[[0m
^[ 用ctrl+v打出来 ^[ 用ctrl+v打出来的 显示的颜色不同,按下ctrl+v后,出现^,在按向右的箭头即可
用户环境的初始化脚本
需要后期的积累