shell基础

参考文章:https://www.cnblogs.com/jingmoxukong/p/7867397.html#一篇文章让你彻底掌握-shell-语言

shell 概述

shell是一个命令解释器,它接收应用程序/用户命令,然后调用操作系统内核

shell是一个功能强大的编程语言,易编写,易调试,灵活性强。

  1. Linux提供的shell解释器有

    cat /etc/shells
    
  2. bash 和 sh的关系

    cd /bin
    ll  | grep bash
    
    [root@lys bin]# echo $SHELL
    /bin/bash
    

脚本入门

脚本以#!/bin/bash开头(指定解析器)

#!/bin/bash
echo "hello world"

常见解释器

# 以下两种方式都可以指定 shell 解释器为 bash,第二种方式更好
#!/bin/bash
#!/usr/bin/env bash

执行脚本:

方式一:

bash helloworld.sh

方式二:

./helllworld.sh

会出现权限不够的启示

chmod 777 ./helloworld.sh

多命令处理

#!/bin/bash
cd /home/linux/
touch banzhang.txt
echo "I LOVE YOU" >> banzhang.txt

注释

  • 单行注释 - 以 # 开头,到行尾结束。
  • 多行注释 - 以 :<<EOF 开头,到 EOF 结束。
#--------------------------------------------
# shell 注释示例
# author:zp

#--------------------------------------------

# echo '这是单行注释'

########## 这是分割线 ##########

:<<EOF
echo '这是多行注释'
echo '这是多行注释'
echo '这是多行注释'
EOF

echo

echo 用于字符串的输出

$ cat exc1.sh 
#!/bin/bash
# 这是一个echo的练习
# 1.输出普通字符串:
echo "hello world"
# 2.输出含变量的字符串
echo "hello, \"zp\""
# 3.输出含变量的字符串
name=zp
echo "hellp \"$name\""
# 4.输出含换行符的字符串
echo "YES \nNO"
echo "YES -e \nNO"   # -e 开启转义
# 输出不换行字符串
echo "YES"
echo "NO"
echo "YES\c"
echo "NO"
# 输出重定向到文件
echo "test" test.txt
# 输出执行结果
echo `pwd` 

printf

printf 用于格式化输出字符串

默认,printf不会像echo一样自动添加换行符,如果需要换行,可以手动添加\n

#!/bin/bash

# printf 格式化输出字符串
# 单引号
printf '%d %s\n' 1 "abc"
# 双引号
printf "%d %s\n" 1 "abc"
# 无引号
printf %s absdf
# 格式只指定了一个参数,但多出的参数仍会按照格式输出
printf "%s\n" abc def
# 如果没有参数,那么 %s 用NULL代替,%d用0代替
printf "%s and %d \n"

自定义变量

基本语法:

  1. 定义变量:变量=值,等号两边不能有空格
  2. 撤销(删除)变量:unset 变量
  3. 输出变量:echo $变量
  4. 声明静态变量:readonly 变量, 注意:不能unset

变量定义规则:

  • 变量名:可以由字母,数字和下划线组成,不能以数字开头,环境变量名建议大写

  • 等号两侧不能有空格

  • 在bash中,变量默认类型都是字符串类型,无法直接进行数值运算

  • 变量的值如果有空格,需要使用双引号或单引号括起来

  • 可以把变量提升为全局变量(export),可供其他shell程序使用

    export 变量

访问变量的语法形式: ${var} 和 $var

变量名外面的花括号是可选的,加不加都行,但是推荐加花括号

特殊变量

$n

$n (描述:n为数字,$0代表脚本名称,10以内参数用$1-9 表 示 , 10 以 上 的 需 要 用 大 括 号 包 含,{10})

#!/bin/bash
echo "$0 $1 $2 $3"
12

$#

$# (功能描述:获取所有输入参数个数,常用于循环)

#!/bin/bash
echo "$0 $1 $2 $3"
echo $#
123

$* 和$@

  • $* (描述:代表命令行中所有的参数,把所有参数看成一个整体)
  • $@ (描述:也代表命令行中所有的参数,不过把每个参数区分对待)
[linux@localhost datas]$ bash parameter.sh test1 test2
parameter.sh test1 test2 
2
test1 test2
test1 test2
12345

$?

$? (描述:最后一次执行命令的状态,0:正确执行)

运算符

  1. $((运算式)) 或 $[运算式]

  2. expr +,-,*,/,% 加减乘除取余

    expr运算符间要有空格

    # 计算2+3
    [linux@localhost datas]$ expr 2 + 3
    5
    
    # 计算(2+3)*4
    ## 方式1
    [linux@localhost datas]$ expr `expr 2 + 3` \* 4
    20
    
    ## 方式2
    [linux@localhost datas]$ s=$[(2+3)*4]
    [linux@localhost datas]$ echo $s
    20
    

常见的系统环境变量

变量 描述
$HOME 当前用户的用户目录
$PATH 用分号分隔的目录列表,shell 会到这些目录中查找命令
$PWD 当前工作目录
$RANDOM 0 到 32767 之间的整数
$UID 数值类型,当前用户的用户 ID
$PS1 主要系统输入提示符
$PS2 次要系统输入提示符
$USER 获取当前用户

条件判断

基本语法

[ condition ] ( 注意 Condition 前后要有空格 )

常用判断条件

两整数之间比较

符合 描述
-lt (less than) 小于
-le (less equal)小于等于
-eq (equal)等于
-gt (greater than)大于
-ge (greater equal) 大于等于
-ne (not equal) 不等于

文件权限判断

  • -r 有读的权限
  • -w 有写的权限
  • -x 有执行的权限

文件类型判断

  • -f 文件存在并且是一个常规文件
  • -e 文件存在
  • -d 文件存在并是一个目录
# 判断23是否大于2
[linux@localhost datas]$ [ 23 -gt 2 ]
[linux@localhost datas]$ echo $?
0

# 判断helloworld.sh是否有写入权限
[linux@localhost datas]$ [ -w hellowrld.sh ]
[linux@localhost datas]$ echo $?
1

# 判断目录中文件是否存在
[linux@localhost datas]$ [ -e /home/linux/datas ]
[linux@localhost datas]$ echo $?
0

多条件判断

&& ||

流程控制

IF判断

[linux@localhost datas]$ cat if.sh
#!/bin/bash
if [ $1 -eq 1 ]
then
	echo "班长真帅"
elif [ $1 -eq 2 ]
then
	echo "班长真丑"
fi
[linux@localhost datas]$ bash if.sh 2
班长真丑

casse 语句

[linux@localhost datas]$ cat case.sh
#!/bin/bash
case $1 in
1)
	echo "班长"
;;
2)
	echo "学习委员"
;;
3)
	echo "体育委员"
;;
*)
	如果变量的值都不是以上的值,则执行此程序
;;
esac
[linux@localhost datas]$ bash case.sh 2
学习委员

注意事项:

  • case行尾必须为单词 in,每个模式匹配必须以有结束
  • 双分号;;表示命令序列结束,相当于java的break
  • 最后的*),表示默认模式,相当于java中的default

for 循环

  • 语法1

    [linux@localhost datas]$ cat for.sh
    #!/bin/bash
    s=0
    for((i=1;i<=100;i++))
    do
    	s=$[$s+$i]	
    done
    echo $s
    [linux@localhost datas]$ bash for.sh
    5050
    
    
  • 语法2

    [linux@localhost datas]$ cat for2.sh
    #!/bin/bash
    for i in $*
    do
    	echo $i
    done
    [linux@localhost datas]$ bash for2.sh 1 2
    1
    2
    

while 循环

[linux@localhost datas]$ cat while.sh
#!/bin/bash
s=0
i=1
while [ $i -le 100 ]
do
	s=$[$s + $i]
	i=$[$i + 1]
done
echo $s

[linux@localhost datas]$ bash while.sh
5050

read 读取控制台输入

read(选项)(参数)
  • -p 指定读取值时的提示符
  • -t 指定读取值时等待的时间(秒)
# 提示7秒内,读取控制台输入的名称
[linux@localhost datas]$ cat read.sh
#!/bin/bash
read -t 7 -p "在7s内请输入你的名字" NAME
echo $NAME
[linux@localhost datas]$ bash read.sh
在7s

函数

系统函数

basename

basename [string / pathname] [suffix] (描述:basename命令会删掉所有的前缀包括最后一个‘/’字符,然后将字符串显示出来)

# 方式1
[linux@localhost datas]$ basename /home/linux/banzhang.txt
banzhang.txt

# 方式2
[linux@localhost datas]$ basename /home/linux/banzhang.txt .txt
banzhang

dirname

dirname 文件绝对路径 (描述:从给定的包含绝对路径的文件名中去除文件名(非目录的部分),然后返回剩下的路径(目录的部分))

[linux@localhost datas]$ dirname /home/linux/banzhang.txt
/home/linux

自定义函数

# 格式
[ function ] funname[()]
{
	Action:
	[return int;]
}
funname
# DESC 计算输入两个参数的值
[linux@localhost datas]$ cat sum.sh
#!/bin/bash
function sum(){
	s=0;
	s=$[$1 + $2]
	echo $s
}
read -p "input your param1:" P1
read -p "input your param2:" P2
sum $P1 $P2
[linux@localhost datas]$ bash sum.sh
input your param1:1
input your param2:2
3

综合案例

归档文件

实际生产中,往往需要对重要数据进行归档备份。

需求:实现一个每天对指定目录归档备份的脚本,输入一个目录名称(末尾不带/),将目录下所有文件按天归档保存,并将归档日期附加在归档文件名上,放在/root/archive下。

这里使用到了归档命令:tar

后面可以加上-c表示归档。加上-z选项表示同时进行压缩,得到的文件后缀名为tar.gz

脚本实现:

 #!/bin/bash


#首先判断输入参数个数是否为1
if [ $# -ne 1 ]
then
        echo "参数个数错误!应该输入一个参数,作为归档目录"
        exit
fi

# 从参数中获取目录名称
if [ -d $1 ]
then
        echo 
else
        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 -zcf $DEST  $DIR_PATH/$DIR_NAME

if [ $? -eq 0 ]
then
        echo
        echo "归档成功"
        echo "归档文件为:$DEST"
else
        echo "归档出现问题"
        echo 
fi
exit

shell 工具

cut命令从文件的每一行剪切字节,字符和字段并将这些字节,字符和字段输出
cut [选项参数] filename

  • -f 列号,提取第几列
  • -d 分隔符,按照指定分隔符分隔列
  • -c 按字符进行切割,后加n 表示取第几列 比如:-c 10
# DESC 切割cut.txt第一列
[linux@localhost datas]$ cat cut.txt
dong shen
guan zhen
wo wo
lai lai
le le
[linux@localhost datas]$ cut -d " " -f 1 cut.txt
dong
guan
wo
lai
le

# DESC 获取第三行第一个单词
[linux@localhost datas]$ cat cut.txt | grep guan | cut -d " " -f 1
guan

选取系统PATH变量值,第2个“:”开始后的所有路径

echo $PATH 
/java-project/jdk1.8.0_321/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bi

echo $PATH | cut -d ":" -f 3-
/usr/local/bin:/usr/sbin:/usr/bin:/root/bin

sed

sed是一种流编辑器,它一次处理一行内容,处理时,把当前处理的行存储在临时缓冲区中,成为“模式空间”,接着sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕,接着处理下一行,这样不断重复,知道文件末尾,文件内容并没有改变,除非你使用重定向存储输出
sed [选项参数] ‘command’ filename

  • -e 直接在指令列模式上进行sed的动作编辑

命令功能描述

  • a 新增
  • d 删除
  • s 查找并替换
# DESC 在第二行后增加mei nv字符
[linux@localhost datas]$ sed -e "2a mei nv" sed.txt
dong zhen
guan zhen
mei nv
wo wo
lai lai

awk

awk 一个强大文件分析工具,把文件逐行的读入,以空格为默认分隔符将每行切片,切开的部分再进行分析处理

awk [选项参数] 'pattern1 {action1} pattern2{action2}..' filename

pattern: 表示awk在数据中查找的内容,就是匹配模式

action:在匹配内容时所执行的一系列命令

  • -F 指定输入文件分隔符
  • -v 赋值一个用户定义变量

案例实操

  1. 数据准备

    搜索passwd文件以root关键字开头的所有行,并输出该行的第7列

    #cut 实现
     cat /etc/passwd | grep ^root | cut -d ";" -f 7
    # awk实现
    cat /etc/passwd | awk -F ":" '/^root/ {print $7}'
    ps -ef | awk '{print $}'
    # 打印文件大小
    ll -l | awk '{print $5}'
    

    搜索passwd文件以root关键字开头的所有行,并输出该行的第1列和第7列,中间以“,”号分割。

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

    只显示/etc/passwd的第一列和第七列,以逗号分割,且在所有行前面添加列名"begin"在最后一行添加"end"

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

    将passwd文件中的用户id增加1并输出

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

awk的内置变量

变量 说明
FILENAME 文件名
NR 已读的记录数(行号)
DF 浏览记录的域的个数(切割后,列的个数)

案例实操

  1. 统计password文件名,每行的行号,每列的列数

    cat /etc/passwd | awk  -F ":" '{print "文件名 :"FILENAME "行号:" NR "列数: "NF }'
    
  2. 查询ifconfig命令和输出结果中的空行所在的行号

    ifconfig | awk '/^$/ {print "空行: " NR}' 
    

综合应用

我们可以利用 Linux 自带的 mesg 和 write 工具,向其它用户发送消息。

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

前置知识

who 查看有多少控制台
[root@bogon logs]# who
root     pts/0        2022-05-26 17:34 (124.64.252.49)
root     pts/1        2022-05-27 10:12 (120.244.202.117)
root     pts/2        2022-05-27 11:37 (58.34.52.34)
root     pts/5        2022-05-27 10:16 (120.244.202.117)
root     pts/6        2022-05-27 19:10 (120.245.102.201)
root     pts/8        2022-05-25 10:18 (124.64.252.49)

# 向某个控制台发出消息
write root pts/1
hi

脚本实现如下:

#!/bin/bash/

# 查看用户是否登录
login_user=$(who | grep -i -m 1 $1 | awk '{print $1}' )

if [ -z $login_user ]
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

sort

sort 命令是在Linux里非常有用,它将文件进行排序,并将排序结果标准输出

sourt [选项] (参数)
参数 描述
-n 依照数值大小排序
-t 以相反的顺序排序
-t 设置排序时使用的分隔字符
-k 指定需要排序的列
posted @ 2025-06-15 22:50  小郑[努力版]  阅读(14)  评论(0)    收藏  举报