bash语言
常用命令
1.head
- 默认取文件前10行内容
2.tail
- 默认取文件末尾10行的内容
例:取文件8-14行的内容
head -14 /etc/passwd | tail -7
3.cut
- 取出文本指定的列
- 默认以空格或者tab键进行分割(不支持不规则的空格)
- 选项
- -d 指定分割符
- -f 指定获取的列号
获取第一列:
cut -f1 test.txt
获取1和2列:
cut -f1,2 test.txt
cut -f1-2 test.txt
获取第一列和第三列:
cut -f1,3 test.txt
以:
为分割符获取第一列:
cut -d":" -f1 test.txt
4.sort
- 对文本的内容进行排序
- 默认以字符的ASCII码数值从小到大排序
- 选项
- -n 以数值大小排序
- -r 倒序
- -t 指定分割符,默认为空格
- -knum 指定以某个字段来排序
- 以数字大小排序
sort -n num.txt
- 指定分割符为
:
且以第三列的数字大小排列:
sort -t":" -k3 -n /etc/passwd
指定分割符为:
且以第三列的数字大小逆序(从大到小)排列:
sort -t":" -k3 -n -r /etc/passwd
5.uniq
- 去重:必须有顺序后才去重
sort -n num.txt | uniq
- 选项
- -d 仅打印有重复的元素
- -c 打印元素重复的个数
6.wc(word count)
- 计算文本数量
- 选项
- wc -l 打印行数
wc -l /etc/passwd
- wc -w 打印单词数
- wc -c 打印字节数
- wc -L 打印最长行的字节数
变量
变量定义
- 方法一
- 变量名=变量值
- 变量值必须是一个整体,中间没有特殊字符,等号两侧不能有空格
- 方法二
- 变量名='变量值'
- 看到的内容,就输出什么内容
- 方法三
- 变量名="变量值"
- 方法四
- 变量名=$(linux命令)
- 变量名=$
linux命令
(反引号)
以上四种方法默认为本地变量
a=1
b=emilymei
c="hello world"
echo 123 #向屏幕输出内容
echo $a #1
echo $b #emilymei
echo $c #hello world
d='hello from china'
echo $d #hello from china
date_now=$(date)
echo $date_now # 打印当前时间
date_now2=`ls`
echo $date_now2. # 打印文件列表
单引号与双引号的区别:
a=123
echo 'this is $a' #this is $a ,单引号时,把 $a当成普通字符串打印出来
echo "this is $a'" #this is 123 ,使用双引号时,引用变量
echo $a #123
echo @a_1 #(空),认为a_1是变量,shell中不存在的变量不会报错
echo ${a}_1 #123_1
echo $a _1 #123 _1
预定义变量(全局变量)
env
:查看环境变量(只显示全局变量)
- 定义全局变量
- 方法一
变量=值
export 变量
- 方法二(最常用)
export 变量=值
全局变量只能在当前打开的终端中生效,关闭后不再生效,需将环境变量放到~/.bashrc
、~/.bash_profile
或/etc/profile
文件中去,才能永久生效,这三个文件都是打开终端就会执行的文件。其他文件中的都是临时全局变量。
echo $PWD #相当于pwd,返回的是当前的家目录
echo $USER #返回当前用户名
echo $HOME #返回当前用户的家目录,相当于echo ~
echo $PATH #返回当前的环境变量
ls #显示当前目录的文件
a=ls ;echo $a #ls
a=`ls`;echo $a #显示当前目录的文件,反引号(``)里的内容会被当做命令来执行
which python #查找Python的安装地址,只能在当前环境变量里找
export PATH=$PATH:$PWD #将当前目录加到环境变量里去
ll #ls -l的简写
chmod +x 1.sh #1.sh文件添加可执行权限
内置变量
符号 | 含义 |
---|---|
$0 | 获取当前执行的shell脚本文件名,包括脚本路径 |
$n | 获取当前执行的shell脚本的第n个参数值,n=1..9,如果n大于9就要用大括号括起来${10} |
$# | 获取当前shell命令行中参数的总个数 |
$? | 获取执行上一个指令的返回值(0为成功,非0为失败) |
$@ | 获取当前执行的shell脚本的所有参数 |
$# | 获取当前执行的shell脚本的参数总数 |
数组变量
a=1
b=2
c=3
array=(1 2 3 4 5)
echo $array #1,错误
echo ${array[@]} #1 2 3 4 5
echo ${array[*]} #1 2 3 4 5
echo ${#array[@]} #5,代表这个数组里有5个元素
echo ${#array[*]} #5,代表这个数组里有5个元素
echo ${array[2]} #3,取数组第3个元素
echo ${array[-1]} #3,取数组最后1个元素
array=(`ls`) #数组的表现形式是(),这个是数组
echo ${array[@]} #执行ls命令
array=`ls` #这个是字符串,因为没有()
#@和*的区别,*:当加上双引号时,会当成字符串来处理,不加双引号,当初数组处理@:不管加不加双引号,依然当做数组处理,例:
array=(1 2 3);for line in "${array[@]}";do echo $line;done
#1
#2
#3
array=(1 2 3);for line in "${array[*}";do echo $line;done #1 2 3,把array当成字符串处理
array=(1 2 3);for line in ${array[@]};do echo $line;done
#1
#2
#3
array=(1 2 3);for line in ${array[*]};do echo $line;done
#1
#2
#3
特殊符号的引用
- 双引号用于括起一段字符串,支持$var形式的变量替换
- 单引号也表示其内容是字符串值,不支持转义
- \反斜杠,某些情况下表示转义
- $(ls)表示执行ls后的结果。与``类似。不过可以嵌套
- `反引号。用法比较独特,代表命令的输出,非常有用
- ((a+b))$((2+3))
- (())是证书扩展。把里面的变量当做整数去处理
- ({1..1010})等价于 seq 1 10 。表示1到10
echo "a\nbb" #a\nbb,没有达到转义效果,错误
echo -e "a\nbb" #-e,开启转义模式
#a
#bb
echo "a\bb" #a\bb,\b=删除前一个字符
echo -e "a\bb" #b
echo -e "a\abb" #abb,\a=发出警告的声音
a=2;b=3
echo $((a+b)) #5,(())进行两个变量间的操作
a=5;b=7;c=2
echo $((a+b*c)) #19
echo $(($a+$b*$c)) #19
a=1;b=3
(($a>$b));echo $? #1,非0的返回表示结果为false,0表示true
array=(`seq 1 15`) #1到15的整数
echo ${array[@]} #1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
变量类型
- 字符串
- 数字
- 布尔
- 数组 array=(a b c )
- 函数 foo(){echo hello world}
a="hello world" #引号引起来的都是字符串
b=1;c=2 #整型
a=true;echo $a #true ,布尔
数字型变量操作
- 计算:i=1;echo $i ;echo $((i+1))
- 更新: ((i=i+1));echo $i
- 只能进行整数计算
- 浮点数计算请使用awk 'BEGIN{print 1/3}'
- 格式化显示可以使用 awk 'BEGIN{printf("%.2f\n",1/3)}'
- printf 格式化
- BEGIN :为了读取管道或者输入的数据
a=1
echo $((a+1)) #2
((a=a+1)) #自增
echo $a #2
i=1;((i=i+1));echo $i #2
a=1
((a++));echo $a #2
awk 'BEGIN{print 2/3}' #0.666667
awk 'BEGIN{printf "%.2f\n",1/3}' #0.33,格式化为保留两个小数点
字符串操作
- # 表示掐头
- % 表示去尾
- / 表示替换
- # 与## 、%与%% 、/与//的区别 :最短匹配模式与最长匹配模式
- awk可以代替这些操作
s="hello world";echo $s #hello world
echo ${s:6} #world,从第6项开始,打印后面的字符
echo ${s:6:3} #wor ,只打印从第6项开始之后3个内容
echo ${#s} #11,字符串的长度
echo ${s#hello} #world,掐头,去掉hello内容,默认空格去掉
echo "${s#hello}" # world,掐头,去掉hello内容,空格不去掉
echo ${s#*o} #*表示任意字符, world
echo ${s##*o} #rld,表示去掉最后一个o前的所有内容
echo ${s%world} #hello ,去尾
echo ${s%%o} #hell
echo ${s/world/emily} #hello emily ,world替换为emily
布尔变量
- 命令执行返回值 $?
- 任何命令执行都会有一个返回值
- 0表示正确
- 非0表示错误
a=true;echo $? #0
b=false;echo $? #0,$?返回的是前一条命令是否正确执行
算术判断
- [ 2 -eq 2 ] 相等
- [ 2 -ne 2 ] 不等
- [ 3 -gt 1] 大于
- [ 3 -ge 3 ] 大于等于
- [ 3 -lt 4 ] 小于
- [ 3 -le 3 ] 小于等于
- (())也可以表示算术比较。((10>=8)).((10==10)).
[ 3 -eq 2 ];echo $? #1
echo $? #0
((3>2));echo $? #0
字符串比较
- [ string1 = string2 ]如果两字符串相同,则结果为真
- [ string1 != string2 ]如果两字符串不相同,则结果为真
- [ -n string1 ]如果字符串不是空,则结果为真
- [ -z string1 ]如果字符串是空,则结果为真
- [[ "xxxx" == x* ]] 在表达式中表示0个或多个字符
- [[ "xxxx" == x?? ]] 在表达式中表示单个字符
- 在引用变量的时候要记得加双引号[ -z "$a" ]否则当a未定义时会语法报错
aaa=wwef
bbb=fdgggdh
["@{aaa}" == "${bbb}"];echo $?
逻辑判断
[ 2 -ge 1 -a 3 -ge 4 ];echo $? # 与
[ 2 -ge 1 -o 3 -ge 4 ];echo $? #或
[ 2 -ge 1 && 3 -ge 4 ];echo $? # 与
[ 2 -ge 1 || 3 -ge 4 ];echo $? # 或
[ ! 2 -ge 1 ];echo $? #非
username="snow"
["${username}" == "snow"] && echo "成功" || echo "失败" # 如果等于snow就输出成功
# ,否则输出失败
passwd=123
["${username}" == "snow" -a "${passwd}" == 123 ]&& echo "成功" || echo "失败"
# 如果username等于snow并且passwd等于123就输出成功,否则就输出失败
内置判断
- -e file 如果文件存在,则结果为真
- -d file 如果文件是一个子目录,则结果为真
- -f file 如果文件是一个普通文件,则结果为真
- -r file 如果文件可读,则结果为真
- -s file 如果文件的长度不为0,则结果为真
- -w file 如果文件可写,则结果为真
- -x file 如果文件可执行,则结果为真
[-x test.sh];echo $? # 文件是否可执行
- [[]]是[]的扩展语法,在老的sh里并不支持,推荐用[]
shell脚本格式
1.格式要求
- 在文件首行指定执行shell的程序及相关说明
#!/bin/bash
# Author: Mei
# Date: 2021-12-07
- shell脚本文件后缀,建议命令为.sh
- 脚本执行失败时,使用exit返回非零值,来退出程序
- 默认缩紧4个空格
- shell脚本的命名简单、有意义
2.注释
- 单行注释
#
- 多行注释
:<
逻辑控制
- 条件 if
- 分支case
- 循环 for while until select
- && ||
- break和continue
if结构 - if [ condition ] ;then ```;fi
- if [ condition ] ;then ```;else ...;fi
- if [ condition ] ;then ```;elif ...;fi
- 简单的逻辑可以使用 && ||去替代
- 条件可以用命令返回值代替
if [ -e test ];then echo exist;else echo not exist;fi
if ls test;then echo exist;else echo not exist;fi
ls test && echo exist ||echo not exist
[ -e test ] $$ echo exist || echo not exist #不是完全等价于if else
echo "1" && echo "2" || echo "3" && echo "4" || echo "5" && echo "6" && echo "7" && echo "8" ||echo "9" #1\n2\n4\n7\n8
read -e -p "请输入路径(如:/etc/passwd):"
if [ -f ${REPLY} ]
then
echo "${REPLY}是常规文件"
elif [ -d ${REPLY} ]
then
echo "${REPLY}是目录"
else
echo "${REPLY}是其他文件"
fi
查询某个应用是否启动:(例)
if netsta -tiup | grep "3306" &> /dev/nell; then
echo "已启动"
else
echo "未启动"
fi
control+r:可搜索
less ~./bash_history:查看历史命令
for循环
- for(( c1;c2;c3 ));
- do
- ...;
- done
- for ((i=0;i<10;i++));do echo $i;done
for ((i=0;i<10;i++));do echo $i ;done #0\n1\n2\n3\n4\n5\n6\n7\n8\n9
array=(1 2 3 4 5)
for ((i=0;i<${array[@]};i++));do echo $i;done #语法报错,变量不能和数组作比较
for ((i=0;i<${#array[@]};i++));do echo ${array[i]};done #1\n2\n3\n4\n5
for遍历循环
用于递归数组,还可以递归以空格隔开的字符串序列。或者某个命令的返回值
for f in $array[*];
do
...
done
ss="aa bb cc dd";for x in x;done
for x in
ls
;do echo $x;doness=(aa bb cc "sss dd");for x in x;done
ss="aa bb cc dd";for x in $ss;do echo $x;done
for x in `ls`;do echo $x;done
ss=(aa bb cc "sss dd");for x in ${ss[@]};do echo $x;done
#若文件名中有空格,则不能使用`ls`,若使用:(文件名为2 3)
for x in `ls`;do echo $x;done #2\n3
#此时用:
for x in *;do echo $x;done #2 3
touch "2 3" #新建文件2 3
rm "2 3" #删除文件2 3
rm -rf #删除目录下的所有文件,慎用
dir=$(ls /)
index=1
for f in $dir
do
echo "${index}.${f}"
index=$((index+1))
done
批量创建用户:
for u in ${cat ~/users.txt}; do
useradd "$u"
echo "$u:123456" | chpasswd&>/dev/nell && echo "创建$u成功" || echo "创建$u失败"
passwd -e "$u"
done
while循环
- i=0;while [ $i -lt 3 ] ;do echo $i;((i=i+1));done
- 一个有用的小技巧。一行行的读取文件内容:while read line;do echo $line;done < 2
- 如果while数据源来自文件,done<文件名,重定向到while中去
while read hostname ip;do
ping -c1 -W1 "$ip" &> /dev/null
if [ $? -eq 0 ];then
echo "主机【${hostname}】在线"
else
echo "主机【${hostname}】离线"
fi
done < ~/servers.txt
i=0
while [ $i -lt 3 ] ;do echo $i;((i=i+1));done #0\n1\n2
while read line;do echo $line;done < 2 #读取文件2中每一行内容到屏幕
read x #(输入 )1111 ,相当于input,读取一个标准输入
echo $x #1111
read -p "enter:" x;echo 我刚刚输入了: $x #enter:(输入)111 我刚刚输入了:111
echo $x #111
case
function menu
{
echo -e "\t\txxx管理系统"
echo
echo -e "\t\t1.备份"
echo -e "\t\t2.显示"
echo -e "\t\t3.重启"
echo -e "\t\t4.还原"
echo -e "\t\t0.退出"
}
function main
{
while true;do
clear
menu
read -s -e -n1
echo
case "${REPLY}" in
"1")
echo "执行备份操作"
;;
"2")
echo "执行显示操作"
;;
"3")
echo "执行重启操作"
;;
"4")
echo "执行还原操作"
;;
"0")
echo "执行退出操作"
break
;;
*)
echo "其他操作"
;;
esac
read
done
}
退出控制
- return 函数返回
- exit 脚本退出
- break 退出当前循环。默认为1
- break 2 退出两层循环
- continue 跳过当前循环,进入下一次循环
- continue 2 跳到上层循环的下一次循环中
for f in *;do echo $f;if [ -d f ];then break;fi;done
for f in *;do echo $f;if [ -f f ];then echo $f is a file ;else continue;fi;done #结束当前循环,并且进入下次循环
函数
1.格式
# 格式一:
函数名()
{
命令1
命令2
···
}
# 格式二:
function 函数名
{
命令1
命令2
···
}
# 方法一
show_info()
{
echo -e "Hello,${USER},date:$(date)"
}
# 使用函数名即可调用
show_info
# 方法二
function show_info1
{
echo -e "Hello,${USER},date:$(date)"
}
show_info1
2.参数
function check_host
{
ip="$1"
hostname = "$2"
ping -c1 "&{ip}" &> /dev/null
[ $? -eq 0 ] && echo -e "主机【&{hostname}】在线" || echo -e "主机【&{hostname}】离线"
}
# check_host 114.114.114.114 应用服务器1
check_host $1 $2 # 外部执行时传参
shell运行环境概念
- bash是一个进程,bash下还可以重新启动一个shell,这个shell是sub shell,原shell会复制自身给他
- 在sub shell中定义的变量,会随着sub shell的消亡而消失
- ()在子shell中运行
- {}在当前shell中运行
- $$ 当前脚本执行的pid
- & 后台执行
- $! 运行在后台的最后一个作业的pid
echo $$ #查看当前shell的进程号
(a=1) #()内的是子shell
echo $a #返回为空,子shell运行完会消失
a=3
(a=1;echo $a );echo $a #1\n3
{a=1;echo $a ;};echo $a #1\n1
for ((i=0;i<10;i++));do echo $i ;sleep 2;done #平均每2秒打印一次i
for ((i=0;i<10;i++));do echo $i ;done & #平均每2秒打印一次i并且放在后台执行
jobs # 查看执行任务的运行状态
Ctrl z #把当前任务切换到后台执行
bg 1 #把后台的命令继续执行,1指命令标记
fg 1 #把1任务放到前台执行
shell环境变量
- shell首先是一个工作环境,有很多变量可以供我们使用
- set 可以获得当前的所有变量
- env 可以获得可以传递给子进程的变量
- export aa==bbbb 把私有变量导出
echo $PATH #查看环境变量
export PATH:`pwd` #把当前目录加到环境变量里
vim .bashrc 里面加上 export PATH=$PATH:`pwd`,保存
source ~/.bashrc #环境变量添加成功
#vim模式下
:set nu #显示行号
:set nonu #不显示行号
esc模式下:
- gg 跳到文件开头
- G 跳到文件最后一行
按键盘v ,进入剪切复制模式:
- d 选中
- y 复制
- p 粘贴
shell输入输出
- read 用来读取输入,并赋值给变量
# -p:提示用户输入内容
read -p "请输入:"
# -n:指定输入字数
read -n1 -p "请输入Y/N:"
echo
echo $REPLY
# -s:加密输入内容
read -s -p "请输入密码:"
- $REPLY:内置函数,往往只接受read的一个参数,如需多个,则需指定赋值给哪个参数
read -p "请输入主机名称:" hostname
read -p "请输入IP地址:" ip
function check_host
{
# ip="$1"
# hostname = "$2"
ping -c1 -W2 "&{ip}" &> /dev/null
[ $? -eq 0 ] && echo -e "主机【&{hostname}】在线" || echo -e "主机【&{hostname}】离线"
}
# check_host 114.114.114.114 应用服务器1
check_host
- echo,print 可以简单输出变量
-
file 将输出重定向到另一个文件,等价于 tee
- < file 输入重定向
- | 表示管道,也就是前一个命令的输出传入下一个命令的输入
echo "hello world" > 1 #1文件里内容为hello world,覆盖原来的文件内容
echo "hello china" >>1 #1文件里内容为hello world hello china,向原来文件追加内容
vim 222 #hello world \n hello china
read x < 222 #从222文件读取内容输入到x
echo $x #222文件内容
ls > 1 #1文件内容为执行ls命令后返回的数据
ls ddd >1 2>&1 #标准输入(1)和标准错误(2)都重定向到1文件中
vim text1
#hello world
#email
#hhh
#little
vim text2
#hello world,this is a grep test.
#email
#hhh
#little
vim text2
#hello world
#email
#ffff
#little
grep -i "hello world" text1 text2 text3
#text1:hello world
#text2:hello world,this is a grep test.
#text1:hello world
-i :不区分大小写
-o :只显示需要的字符串
grep -o "hello world" text1 text2 text3
#text1:hello world
#text2:hello world
#text1:hello world
grep -io "hello world" text1 text2 text3
#text1:hello world
#text2:hello world
#text1:hello world
echo abcd | grep -o c #c
正则表达式
- . 表示任意一个字符
- * 表示前面的字符有0个或多个
- .* 表示任意一个有0个或多个的字符
curl www.baidu.com
vim baidu.keyword #\mp3\nmp4\nrest-assured\nandroid\nios
while read k;do echo $k;done < baidu.keyword #\mp3\nmp4\nrest-assured\nandroid\nios
while read k; do $k ;curl http://www.baidu.com/s?wd=$k; done < baidu.keyword #搜索相应的内容后返回的网页代码输出到baidu.keyword
while read k; do $k ;curl -s http://www.baidu.com/s?wd=$k; done < baidu.keyword | grep -o "结果约[0-9,]*" #显示结果约和数量
while read k; do $k ;curl -s http://www.baidu.com/s?wd=$k; done < baidu.keyword | grep -o "结果约[0-9,]*" | grep -o "[0-9,]*" #只显示数字
while read k; do $k ;curl -s http://www.baidu.com/s?wd=$k; done < baidu.keyword | grep -o "结果约[0-9]*" | while read x;do printf $x;done;echo;done