Linux扩展(Shell)


--[SHELL编程]
Shell是一个命令行解释器,它接收应用程序/用户命令,然后调用操作系统内核.

cat /etc/shells
ls -l /bin/ | grep bash
echo $SHELL

文件名以.sh结尾
脚本以#!/bin/bash开头
less /bin/unix-lpr.sh

#!/bin/sh  --开头,默认选择的命令行解释器
#注释

mkdir scripts
cd scripts/
touch hello.sh
vim hello.sh

#!/bin/hash
echo "hello,world"

执行方式:
(1)bash或sh+脚本--不需要赋予可执行权限
bash hello.sh
bash scripts/hello.sh
bash /root/scripts/hello.sh
sh scripts/hello.sh
(2)直接输入脚本的绝对/相对路径--需要具有可执行权限
/root/scripts/hello.sh
scripts/hello.sh
./hello.sh  --在当前路径时
chmod +x scripts/hello.sh
(3)在脚本的路径前加"."或者source
source hello.sh
source /root/scripts/hello/sh
. hello.sh --.命令
type source --内置命令

ps -f
bash   --进入子shell
ps -f
exit   --退出子shell
ps -f
--在子shell设置的变量,父shell是不可见的

[变量]--全局/局部

常用系统变量:
$HOME  --当前的主目录
$PWD   --当前工作目录
$SHELL --当前Shell解释器
$USER  --当前用户
echo $HOME
env | less
printenv | less  --打印出系统全局变量
printenv USER
ls $HOME
set --当前定义的局部变量
set | less

用户自定义变量
基本语法:
定义变量:变量名=变量值  --"="号前后不能有空格

a=2
echo $a

echo $my_var  --没有声明也不会报错
my_var=hello
echo $my_var
my_var=Hello
echo $my_var
my_var="Hello, world"
my_var='Hello, world'

env | grep my_var
set | grep my_var
bash  --进入子shell
ps -f --多了个bash,代表已经进入子shell
echo $my_var
exit  --退出子shell
echo $my_var

my_var=hello
export my_var --把局部变量导出变成全局变量

--子shell更改变量时不会影响到父shell的

[hello.sh]
#!/bin/bash
echo "hello,world"
echo $my_var

source hello.sh
./ hello.sh
echo $new_var
new_var="hello,linux"

[hello.sh]
#!/bin/bash
echo "hello,world"
echo $my_var
echo $new_var

./ hello.sh
. hello.sh
source hello.sh
export new_var
./ hello.sh

a=$((1+5))  --运算
a=$[5+9]    --运算

readonly b=5  --只读变量
b=10          --报错

set | less
unset a  --撤销a变量,只读变量不能撤销
set | less

--在bash里,变量类型默认是字符串类型

ls
hello.sh  --无法执行
cd scripts/
hello.sh  --未找到命令
cp hello.sh /bin/ --复制到/bin目录下
hello.sh
rm -f /bin/hello.sh

echo $PATH

[特殊变量]
--$n --n为数字,$0代表该脚本名称,$--9代表第一到第九个参数,10以上的参数需要大括号:${10}

echo $1
./ hello.sh xiaoming  --$1输出即为xiaoming
vim parameter.sh

[parameter]
#!/bin/bash
echo '==================$n=================='
echo script name:$0
echo 1st paramater:$1
echo 2nd paramater:$2

chmod +x parameter.sh
./parameter.sh abc def

--$#  --获取所有输入参数个数,常用于循环,判断参数的个数是否正确 
vim parameter

[parameter]
#!/bin/bash
echo '==================$n=================='
echo script name:$0
echo 1st paramater:$1
echo 2nd paramater:$2
echo '==================$#=================='
echo parameter numbers:$#

./ parameter.sh
./ parameter.sh abc def

--$* $@
$*  --获取当前命令行提供的所有参数(把所有参数看成一个整体)
$@  --获取当前命令行提供的所有参数(所有参数构成的一个集合),可以for循环遍历

[parameter]
#!/bin/bash
echo '==================$n=================='
echo script name:$0
echo 1st paramater:$1
echo 2nd paramater:$2
echo '==================$#=================='
echo parameter numbers:$#
echo '==================$*=================='
echo $*
echo '==================$@=================='
echo $@

./ parameter.sh abc def

--$?  --最后一次执行命令的返回状态(0表示正常执行/其他则错误)
echo $?

[运算符]
基本语法:
$((运算符))
$[运算符]

expr 1 + 2  --要空格
expr 5 - 2
expr 5 * 2  --报错
expr 5 \* 2

echo $((5*2))
echo $[5*2]

a=$[6+8]
echo $a

a=$(expr 5 \* 2)  --命令替换
echo $a
a=`expr 5 \* 2`
echo $a

s=$[(2+3)*4]
echo $s

cd scripts/
vim add.sh

[add.sh]
#!/bin/bash
sum=$[$1+$2]
echo sum=$sum

chmod +x add.sh
./ add.sh 25 89

[条件判断]
基本语法:
(1)test condition
(2)[condition]  --condition前后要有空格

a=hello
echo $a
test $a = hello
echo $?   --0,为真
test $a = Hello
echo $?   --1,为假

[ $a = hello]
[ $a != hello]
echo $?
[ abc ]
echo $?
[ ]
echo $?

[ 2 = 8 ]
echo $?
[ 2 = 2 ]
echo $?
[ 2 < 8 ]
echo $?

>  --输出,而不是大于
<  --输入,而不是小于

常用判断条件:
(1)两个整数之间比较
-eq    --equal
-ne    --not equal
-lt    --less than
-le    --less qual
-gt    --greater than
-ge    --greater equal
(2)按照文件权限进行判断
-r     --read
-w     --write
-x     --execute
(3)按照文件的类型进行判断
-e     --文件存在(existence)
-f     --文件存在并使一个常规的文件(file)
-d     --文件存在并使一个目录(directory)

[ 2 -lt 8]
echo $?

touch test
[ -r hello.sh ]
echo $?
[ -w hello.sh ]
echo $?
[ -x test ]
echo $?

[ -e /home/atguigu/info ]
echo $?
[ -f /test]
echo $?
[ -d /etc ]
echo $?

多条件判断:  
&&  --前一条命令执行成功时,才执行一条命令
||  --上一条命令执行失败后,才执行下一条命令

a=15
[ $a -lt 20 ] && echo "$a<20" || echo "$a>=20"
a=27
[ $a -lt 20 ] && echo "$a<20" || echo "$a>=20"
[ atguigu ] && echo ok || echo notok
[ ] && echo ok || echo notok

[流程控制] --顺序/分支
if 判断
  
基本语法:

--(1)单分支
if [ 条件判断式 ];then
   程序
fi

if [ 条件判断式 ]
then 
  程序
fi  

cd /home/atguigu/;ls -l
   
a=25
if [ $a -gt 18 ];then echo OK;fi

cd scripts/
vim if_test.sh

[if_test.sh]
#!/bin/bash
if [ "$1"x = "atguigu"x ]   --添加x,避免空值时报错
then
   echo "welcome,atguigu"
fi

chmod +x if_test.sh
./if_test.sh 
./if_test.sh xiaoming
./if_test.sh atguigu

a=25
if [ $a -gt 18 ] && [ $a -lt 35 ];then echo OK;fi
a=36
if [ $a -gt 18 -a $a -lt 35 ];then echo OK;fi    --其中"-a"表示and/"-o"表示or
  
--(2)多分支
if [ 条件判断式 ];then
   程序
elif [ 条件判断式 ];then
   程序  
else
   程序   
fi

vim if_test.sh

[if_test.sh]
#!/bin/bash
if [ "$1"x = "atguigu"x ]
then
   echo "welcome,atguigu"
fi
# 输入第二个参数,表示年龄,判断属于哪个年龄段
if [$2 -lt 18 ]
then
   echo "未成年人" 
else
   echo "成年人"
fi

./if_test.sh atguigu 15
./if_test.sh atguigu 25

vim if_test.sh

[if_test.sh]
#!/bin/bash
if [ "$1"x = "atguigu"x ]
then
   echo "welcome,atguigu"
fi
# 输入第二个参数,表示年龄,判断属于哪个年龄段
if [ $2 -lt 18 ]
then
   echo "未成年人" 
elif [ $2 -lt 35 ]
then
   echo "青年人"
elif [ $2 -lt 60 ]
then
   echo "中年人"
else
   echo "老年人"
fi

./if_test.sh atguigu 35

--case语句
基本语法:
case $变量名 in
     "值1")
     如果变量的值等于值1,则执行程序1
;;          --相当于java中的break
"值2")
     如果变量的值等于值2,则执行程序2
;;
     ...省略其他分支...
*)          --相当于java中的default
     如果变量的值都不是以上的值,则执行此程序
;;
esac     

cd scripts/
vim case_test.sh

[case_test.sh]
#!/bin/bash
case $1 in
1)
    echo "one"
;;
2)
    echo "two"
;;
3)
    echo "three"
;;
*)
    echo "number else"
;;
esac

chmod +x case_test.sh 
./case_test.sh 2
./case_test.sh 6

--(3)循环
--for循环
基本语法1:

for ((初始值;循环控制条件;变量变化))
do
    程序
done

vim sum_to.sh

[sum_to.sh]
#!/bin/bash
for (( i=1; i<=$1;i++ ))  --双小括号内可以用"<" ">" 运算符
do
    sum=$[ $sum+$i ]
done
echo $sum

chmod +x sum_to.sh 
./sum_to.sh 10
./sum_to.sh 100

a=1
if (( $a>2 ));then echo OK;else echo notOK;fi

基本语法2:
for 变量 in 值1 值2 值3 ...
do
    程序
done

for os in linux windows macos ;do echo $os ; done
{}  --表示序列
{ 1..100 }  --1到100的序列

for i in {1..100};do sum=$[$sum+$i] $i;done;echo $sum  --增强for循环
  
[比较$*和$@]

vim parameter_for_test.sh

[parameter_for_test.sh]
#!/bin/bash
echo '===============$*================'
for para in $*
do
    echo $para
done
echo '===============$@================'
for para in $@
do
    echo $para
done
      
chmod +x parameter_for_test.sh
./parameter_for_test.sh a b c d  --不加""没区别

[parameter_for_test.sh]
#!/bin/bash
echo '===============$*================'
for para in "$*"
do
    echo $para
done
echo '===============$@================'
for para in "$@"
do
    echo $para
done

chmod +x parameter_for_test.sh
./parameter_for_test.sh a b c d  --$@会依次显示,而$*会把"a b c d"当成整体输出

--while循环
基本语法:
while [ 条件判断式 ]
do
    程序
done

vim sum_to.sh 

[sum_to.sh ]
#!/bin/bash
# 用for进行实现
for (( i=1; i<=$1;i++ ))
do
    sum=$[ $sum+$i ]
done
    echo $sum
# 用while做一个实现
a=1
while [ $a -le $1 ]  
do
#    sum2=$[ $sum2+$a ]
#    a=$[ $a+1 ]
     let sum2+=a  --不能有空格(let内置命令可以直接使用其他语言的写法)
     let a++
done
echo $sum2

./sum_to.sh 100

--read读取控制台输入
基本语法:
read (选项) (参数)
选项:
-p    --指定读取值时的提示符:
-t    --指定读取值时等待的时间(秒)如果-t不加表示一直等待
参数:
变量:指定读取值的变量名

vim read_test.sh

[read_test.sh]
#!/bin/bash
read -t 10 -p "请输入您的名字:" name
echo "welcome,$name"

chmod +x read_test.sh 
./read_test.sh

[函数] --方法;一段代码的集合;模块化
--系统函数
--basename
基本语法:
basename [string/pathname] [suffix]  --basename命令会删掉所有的前缀包括最后一个('/')字符,然后将字符串显示出来.
                                     --basename可以理解为取路径里的文件名称
选项:
suffix为后缀,如果suffix被指定了,basename会将pathname或string中的suffix去掉.

date
date +%s  --时间戳
vim cmd_test.sh

[cmd_test.sh]
#!/bin/bash
filename="$1"_log_$(date +%s)
echo $filename          

chmod +x cmd_test.sh 
./cmd_test.sh atguigu  --定义日志文件时可以用到

basename /root/scripts/parameter.sh --直接去掉前面的路径
basename /root/scripts/parameter.sh .sh  --把后缀也去掉

vim parameter.sh 
[parameter.sh]
#!/bin/bash
#!/bin/bash
echo '=========$n=========='
echo script name:$( basename $0 .sh )
echo 1st parameter:$1
echo 2nd parameter:$2
echo '=========$#=========='
echo parameter number:$#
echo '=========$*=========='
echo $*
echo '=========$@=========='
echo $@

./parameter.sh a b

--dirname
基本语法:
dirname 文件绝对路径   --从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下路径(目录的部分)
--dirname可以理解为取文件路径的绝对路径名称

dirname /root/scripts/parameter.sh 
dirname ../scripts/parameter.sh 

vim parameter.sh 
[parameter.sh]
#!/bin/bash
echo '=========$n=========='
echo script name:$( basename $0 .sh )
cd $( dirname $0 )
echo script path:$( cd $( dirname $0 ); pwd)
echo 1st parameter:$1
echo 2nd parameter:$2
echo '=========$#=========='
echo parameter number:$#
echo '=========$*=========='
echo $*
echo '=========$@=========='
echo $@

./parameter.sh

--命令替换
$( 命令 )

--自定义函数
基本语法:
[function] funname[()]
{
    Action
    [return int;]
}

经验技巧:
(1)必须在调用函数之前先声明函数,shell脚本是逐行运行。
(2)函数返回值,只能通过$?系统变量获得,可以显示加:return 返回,如果不加,以最后一条命令运行结果作为返回值.return后跟数值n(0-255).



=========$@==========

[root@hadoop100 scripts]# vim fun_test.sh

vim fun_test.sh

[fun_test.sh]
#!/bin/bash
function add(){
    s=$[ $1 + $2 ]
    echo "和:" $s
}
read -p "请输入第一个整数:" a
read -p "请输入第二个整数:" b
add $a $b

chmod +x fun_test.sh 

vim fun_test.sh

[fun_test.sh]
#!/bin/bash
function add(){
        s=$[ $1 + $2 ]
        return  $s
}
read -p "请输入第一个整数:" a
read -p "请输入第二个整数:" b
add $a $b
echo "和:"$?

vim fun_test.sh

[fun_test.sh]
#!/bin/bash
function add(){
    s=$[ $1 + $2 ]
    echo  $s
}
read -p "请输入第一个整数:" a
read -p "请输入第二个整数:" b
sum=$(add $a $b)
echo "和:" $sum
echo "和的平方:"$[ $sum * $sum ]

./fun_test.sh

--归档文件案例
需求:实现一个每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/),将 
     目录下所有文件按天归档保存,并将归档日期附加在归档文件上,放在/root/archive下.
用到命令:tar
后面加上-c选项表示归档,加上-z选项表示同时进行压缩,得到的文件后缀名为.tar.gz.     

vim daily_archive.sh

[daily_archive.sh]
#!/bin/bash
# 首先判断输入参数个数是否为1
if [ $# -ne 1 ]
then
    echo "参数个数错误!应该输入一个参数,作为归档目录名"
    exit
fi
# 从参数中获取目录名称
if [ -d $1 ]
then
    echo
else
    echo
    echo "目录不存在!"
    echo
    exit
fi
DIR_NAME=$(basename $1)
DIR_PATH=$(cd $(dirname $1);pwd)
# 获取当前日期
DATE=$(date +%y%m%d)
# 定义生成的归档文件名称
FILE=archive_${DIR_NAME}_$DATE.tar.gz
DEST=/root/archive/$FILE
# 开始归档目录文件
echo "开始归档..."
echo 
tar -czf $DEST $DIR_PATH/$DIR_NAME
if [ $? -eq 0 ]
then
    echo
    echo "归档成功!"
    echo "归档文件为:$DEST"
else
    echo "归档出现问题!"
fi
exit

chmod u+x daily_archive.sh 
mkdir /root/archive
./daily_archive.sh a b
./daily_archive.sh ../scripts

crontab -l  --查看定时任务
crontab -e  --编辑

0 2 * * * /root/scripts/daily_archive.sh /root/scripts  --设置定时归档

--正则表达式入门
Linux中grep,sed,awk等文本处理工具都支持通过正则表达式进行模式匹配

cat /etc/passwd  --获取所有用户信息
cat /etc/passwd | grep atguigu  --常规匹配

常用特殊字符:
^  --匹配一行的开头
cat /etc/passwd | grep ^a  --匹配以"a"开头的用户
$  --匹配一行的结束
cat /etc/passwd | grep bash$  --匹配以"bash"结尾的所有用户
cat daily_archive.sh | grep -n ^$  --"-n"显示行号
.  --匹配一个任意的字符
cat /etc/passwd | grep r..t
*  --不单独使用,它和上一个字符连用,表示匹配上一个字符0次或多次
cat /etc/passwd | grep ro*t
cat /etc/passwd | grep .*
cat /etc/passwd | grep ^a.*bash$  --指定以a开头,以bash结尾
cat /etc/passwd | grep ^a.*var.*bash$
[] --表示匹配某个范围内的一个字符
[6,8]--匹配6或者8
[0-9]--匹配0-9的数字
[0-9]*--匹配任意长度的数字串
[a-z]--匹配任意长度的字母字符串
[a-c,e-f]--匹配a-c或者e-f之间的任意字符
cat /etc/passwd | grep r[a,b]t
echo "rbtadfasf" | grep r[a,b]t
echo "23rbtadfasf" | grep r[a,b]t
"23rbtadfasf" | grep r[ab]t  --可以不用,分割
echo "23rbtadfasf" | grep r[a-z]*t
\  --表示转义
cat daily_archive.sh | grep '\$'  --搜索$符号
cat daily_archive.sh | grep '/\$' --匹配/$符号

a{2}  --a出现两次
+     --一次或多次
?     --0次或1次,不会多次

--匹配手机号
echo "13812345678" | grep ^1[34578][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$
echo "13812345678" | grep -E ^1[34578][0-9]{9}$   --grep加"-E"支持"{n}"的扩展写法

--[文本处理工具]
--cut
[cut 数据剪切]
基本语法:
cut [选项参数] filename
说明:默认分隔符是制表符
选项:
-f  --列号,提取第几列
-d  --分隔符,按照指定分隔符分割列,默认是制表符"\t"
-c  --按字符进行切割,后加加n表示去第几列,比如 -c 1

vim cut.txt

[cut.txt]
dong shen
guan zhen
wo   wo
lai  lai
le   le

cat cut.txt
cut -d " " -f 1 cut.txt   --截取第一列
cut -d " " -f 2,3 cut.txt --截取第二三列
cat /ect/passwd | grep bash$
cat /etc/passwd | grep bash$ | cut -d ":" -f 1,6,7   --截取1,6,7列
cat /etc/passwd | grep bash$ | cut -d ":" -f 1,6-    --“6-”截取到最后
cat /etc/passwd | grep bash$ | cut -d ":" -f -4      --截取到第4列 
echo $PATH
echo $PATH | cut -d ":" -f 2-

ifconfig
ifconfig ens33
ifconfig ens33 | grep netmask
ifconfig ens33 | grep netmask | cut -d " " -f 10 --截取IP地址

--awk
{awk 文本分析工具,把文件逐行读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理}
基本语法:
awk [选项参数] '/patternl/{action1} /pattern2/{action2}...' filename
pattern  --表示awk在数据中查找的内容,就是匹配模式
action   --在找到匹配内容时所执行的一系列命令

选项参数:
-F  --指定输入文件分隔符,默认是空格
-v  --赋值一个用户定义变量

awk的内置变量:
FILENAME  --文件名
NR        --已读的记录数(行号)
NF        --浏览记录的域的个数(切割后,列的个数)


which awk
ll /usr/bin |grep awk

cat /etc/passwd | grep ^root | cut -d ":" -f 7
cat /etc/passwd | awk -F ":" '/^root/ {print $7}'
cat /etc/passwd | awk -F ":" '/^root/ {print $1","$6","$7}'

BEGIN  --在所有数据读取行之前执行
END    --在所有数据执行之后执行

cat /etc/passwd | awk -F ":" 'BEGIN{print "user,shell"}{print$1","$7}END{print "end of file"}'

cat /etc/passwd | awk -v i=1 -F ":" ' {print $3+i}'

cat /etc/passwd | awk -F ":" '{print "文件名:"FILENAME " 行号: "NR " 列数: "NF}'
awk -F ":" '{print "文件名:"FILENAME " 行号: "NR " 列数: "NF}' /etc/passwd 
ifconfig | grep -n ^$  --“-n”显示行号
ifconfig | awk '/^$/ {print NR}'
ifconfig | awk '/^$/ {print "空号:" NR}'

ifconfig ens33 | grep netmask
ifconfig ens33 | grep netmask | cut -d " " -f 10
ifconfig | grep netmask | cut -d " " -f 10
ifconfig  | awk '/netmask/ {print $2}'

--[综合应用案例]

--发送消息
利用Linux自带的mesg和write工具,向其他用户发送消息.

需求:实现一个向某个用户快速发送消息的脚本,输入用户名作为第一个参数,后面直
接跟要发送的消息.脚本需要检测用户是否登录在系统中、是否打开消息功能,以及当前发
送消息是否为空.

who am i
who   --查看当前所登录的所有用户
mesg  --检查消息功能是否打开,"y"表示打开
who -T--检查消息功能是否打开,"+"表示打开,并且可以查看对于的控制台
mesg n--关闭消息功能
mesg y--打开消息功能

write atguigu pts/1 --给atguigu用户发送消息 pts为对应的控制台
ctrl+c  --退出;退出后对方会显示"EOF",表示发送消息已经结束

vim send_msg.sh

[send_msg.sh]
#!/bin/bash
# 查看用户是否登录
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}' )  -- -i忽略大小写;-m匹配多少行
if [ -z $login_user ]                                    -- -z判断是否为空
then
    echo "$1 不在线!"
    echo "脚本退出.."
    exit
fi
# 查看用户是否开启消息功能
is_allowed=$( who -T | grep -i -m 1 $1 | awk '{print $2}')
if [ $is_allowed != "+" ]
then
    echo "$1 没有开启消息功能"
    echo "脚本退出.."
    exit
fi
# 确认是否有消息发送
if [ -z $2 ]
then
    echo "没有消息发送"
    echo "脚本退出.."
    exit
fi
# 从参数中获取要发送的消息
whole_msg=$( echo $* | cut -d " " -f 2- )
# 获取用户登录的终端
user_terminal=$( who | grep -i -m 1 $1 | awk '{print $2}' )
# 写入要发送的消息
echo $whole_msg | write $login_user $user_terminal
if [ $? != 0 ]
then
    echo "发送失败!"
else
    echo "发送成功!"
fi
exit

chmod u+x send_msg.sh
./send_msg.sh atguigu hi,atguigu
./send_msg.sh atguigu boss is coming





  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Violet_Sam

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值