shell基础用法

shell基础知识

shell中的多行注释

shell 复制代码
:<<EOF
read
echo $REPLY  # read不指定变量,则默认写入$REPLY
EOF
# :<<EOF ...EOF  多行注释,EOF可以替换为!# 等

文件目录和执行目录

shell 复制代码
echo '$0='$0  # ./demo.sh
echo '$0的realpath='$(realpath $0)      # 完整路径
echo '$0的父目录='$(dirname $(realpath $0))    # 文件的父目录

echo "BASH_SOURCE="${BASH_SOURCE}  # ./demo.sh
echo "BASH_SOURCE的realpath="$(realpath ${BASH_SOURCE}) # 完整路径

echo '文件执行目录:'`pwd`  # 执行目录

当使用bash或./执行时,0和{BASH_SOURCE}是一样的,但是通过.或source命令执行脚本时,$0就变为了/bin/bash ,无法达到想要的效果;

shell变量

变量赋值

var='hello word'

unset var # 删除变量,不能删除只读变量

readonly var=89 # 只读变量
数组定义

shell 复制代码
var_array=(value1 value2 value3)    # 普通数组
declare -A  var_url_array=(['baidu']='www.baidu.com' ['taobao']='www.taobao.com' ['jd']='jingdong.com')  # 关联数组
echo ${var_array}  # value1
echo ${var_url_array}  # 空
echo ${var_array[1]}  # value2
echo ${var_url_array["taobao"]}  # www.baidu.com
echo ${var_array[*]}  # value1 value2 value3
echo ${var_array[@]}  # value1 value2 value3
echo ${var_url_array[*]}  # www.baidu.com jingdong.com www.taobao.com
echo ${var_url_array[@]}  # www.baidu.com jingdong.com www.taobao.com
echo ${#var_array[1]}  # 6  计算数组下标元素的长度
echo ${#var_array[*]}  # 3  计算数组的长度
echo ${#var_url_array[*]}  # 3
echo ${#var_url_array["taobao"]}  # 14
echo ${!var_array[*]}  # 0 1 2  获取数组的key
echo ${!var_url_array[*]}  # baidu jd taobao  获取数组的key
echo ${var_array[@]:0:2}  # value1 value2 切片访问,从0下标开始,长度是2
echo ${var_url_array[@]:1:2} # www.baidu.com jingdong.com
echo ${var_array[@]/value/num} # num1 num2 num3 数组内容替换
echo ${var_url_array[@]/www/} # .baidu.com jingdong.com .taobao.com
# 以上4条命令,同样可以指定下标,就是对数组下标元素操作
unset var_array[0]
echo ${var_array[*]}  # value2 value3
unset var_url_array["taobao"]
echo ${var_url_array[*]}  # www.baidu.com jingdong.com
# 需要注意的是,var_array删除下标0之后,并不会重置下标
echo ${!var_array[*]}  # 1 2
var_array[7]=78
echo ${!var_array[*]}  # 1 2 7
echo ${var_array[*]}  # value2 value3 78
var_array+=(10 20)
echo ${var_array[*]}  # value2 value3 78 10 20
var_array=(${var_array[*]} 1 2 3)
echo ${var_array[*]}  # value2 value3 78 10 20 1 2 3
unset var_array # 删除整个数组 
for i in ${!var_url_array[*]} ; do echo $i; done  # 遍历数组的key:baidu \n jd
for i in ${var_url_array[*]} ; do echo $i; done  # 遍历数组的值:www.baidu.com \n jingdong.com
shell 复制代码
test1(){
      echo "接收到的参数列表:$@"
      newarr=($@)
      echo "新数组的值为:${newarr[*]}"
      return $newarr   # 不建议这样写,其实只能返回数组第一个元素的整数值
}
# shell脚本调用也不能传入数组,所以建议直接在脚本中定义数组
arr=(10 2 3)
test1 ${arr[*]}
echo $?

#接收到的参数列表:10 2 3
#新数组的值为:10 2 3
#10

shell内置变量

$MACHTYPE:机器类型

$OSTYPE:操作系统类型

$HOSTNAME:当前主机名

$HOME:当前用户家目录

$USER:当前用户名

$UID:当前用户ID

$SHELL:当前shell的路径

$PWD:当前目录

$IFS:字段分隔符

$RANDOM:随机数

$SECONDS:shell 脚本启动的秒数

$FUNCNAME:当前函数的名称

特殊参数变量

$0 --脚本名称

$n --位置参数

$# --参数个数

$* --所有参数

$@ --所有参数;

不加双引号时,\* 、@相同,都是独立字符串;加上双引号时,\*是将所有参数作为整个字符串,而@是将每个参数作为独立字符串;

特殊状态变量

$? -- 退出状态码

$! -- 上一个后台运行的程序的进程id

$$ -- 当前脚本的pid

$_ -- 上一个命令的最后一个参数

shell内置命令

date
shell 复制代码
date +'%Y-%m-%d %H:%M:%S'
# 2024-11-18 19:14:02
tee
shell 复制代码
# 同时写入标准输出和文件。
echo "hello"|tee a.txt  # 写入a.txt文件,同时也会到标准输出,-a参数代表追加;
echo "hello world"|tee a.txt b.txt c.txt  # 同时重定向到多个文件;
lsblk|tee a.txt > /dev/null # 这样可以不到标准输出
lsblk 2>&1|tee a.txt # 将标准错误也写入a.txt文件;
tee -i a.txt    # 从键盘获取输入写入文件,-i不会被ctrl+c打断,可用ctrl+d打断;
echo和printf
shell 复制代码
echo -e "\n hello" # 支持转义字符
echo -n "hello"  # 不换行
printf "hello \t world" # printf默认支持转义,默认不换行
printf "%d %s %.2f" 24 hello 4.5678  # 24 hello 4.57
exec 和 eval
shell 复制代码
eval "pwd"  # eval可以将字符串作为命令执行
exec date   # exec 执行命令后退出,相当于自动执行一次exit
cat 重定向
shell 复制代码
cat > b.txt <<EOF
first
second
third
EOF
local
shell 复制代码
function func(){
local name='inside var'
}
# 默认情况下,函数内部定义的变量也属于全局变量。但是只要在变量名前加local关键字,该变量就会变成局部变量。
trap

用于捕获和处理信号:

shell 复制代码
#!/bin/bash
# 定义错误处理函数,将错误信息记录到日志文件
handle_error() {
    echo "$(date): $BASH_COMMAND failed with exit code $?" 
}
# 设置 ERR 信号处理函数
trap handle_error ERR EXIT
# 其他代码逻辑
ls none.txt
# 运行 ./demo.sh 
# ls: 无法访问'none.txt': 没有那个文件或目录
# 2024年 11月 22日 星期五 21:30:53 CST: ls none.txt failed with exit code 0
# 2024年 11月 22日 星期五 21:30:53 CST: ls none.txt failed with exit code 0

捕获了ERR信号,同时也捕获了EXIT信号,所以打印了2遍;这里打印的退出码是0,其实是因为执行$(date)命令成功。

shell 复制代码
#!/bin/bash
trap cleanup INT

cleanup() {
    echo "Caught interrupt signal. Cleaning up..."
}

echo "Press Ctrl+C to interrupt me!"
while true; do sleep 1; done
declare

declare +/- 选项 变量名, 其中,+表示取消设置,-表示设置。

‌声明变量类型‌:在默认情况下,Linux中的变量是弱类型,即默认都是字符串类型。使用declare可以声明变量的类型,例如使用-i选项将变量作为整数处理。参考

shell 复制代码
declare -i data1 data2 res
data1=10
data2=20
res=$data1+$data2
echo $res  # 30   如果不声明,那结果是10+20
shell 复制代码
declare +i data  # 去掉整数属性
declare -r name='test'  # 声明只读变量
name='zyy'  # 报错bash: name: 只读变量
readonly a=1   # 也是定义只读变量
shell 复制代码
declare -a arr=(1 2)    
#对于索引数组来说,declare是否声明似乎并无差别(索引数组顺序固定)
declare -A b=(["apple"]=1 ["boy"]=2 ["cat"]=3)    
# 但对于关联数组,则必须使用declare -A声明(关联数组顺序不定)
shell 复制代码
demo(){
	echo "demo function"
}
declare -f demo
# declare -f 函数名 显示函数的名称和定义代码; -F是只显示函数名称

declare -x APP="mail" -x代表声明环境变量,完全等同于export。

export

直接执行export命令,会显示所有环境变量;

export APP="mail" 其实相当于 declare -x APP="mail",设置后,在当前shell及其子shell中共用该环境变量。直接定义变量a=1,是不能在子shell中共用的。

set
shell 复制代码
set -xe
echo $var
ls none.txt
echo hello
# 运行./demo.sh 
# ++ echo

# ++ ls none.txt
# ls: 无法访问'none.txt': 没有那个文件或目录

-x 代表打印每一行的执行结果;

-e代表遇到错误就退出;bash默认遇到错误会接着执行;

还有-u,如果指定了未定义的变量,会报错退出;

shell 复制代码
set first second third
echo $1
# 运行./demo.sh param1
# first
# set 会重新赋值给位置参数
env

env --显示所有环境变量,包括自己脚本中编写的export的变量;

export --显示所有环境变量,和env查询出的变量一致;

declare --显示所有变量,包括全局变量(包括环境变量)、局部变量;

set --显示所有变量,和declare查询出的变量一致;

ps --forest

ps -ef --forest --展示父进程和子进程的关系

()开启子shell

利用括号,开启子she11的理念,以及检查,在she11脚本开发中,经常会用子she11进行多进程的处理,提高程序并发执行效率。

shell 复制代码
echo $BASH_SUBSHELL
0  # 0表示当前shell
(pwd;echo $BASH_SUBSHELL)
/home/xxx/Desktop
1  # 1 嵌套一层shell
(pwd;(echo $BASH_SUBSHELL))
/home/xxx/Desktop
2  # 2 嵌套2层shell
{}  # 代表当前shell执行
tr 转换和压缩
shell 复制代码
echo 1234abcd|tr 12 34   # 3434abcd
echo 1234abcd|tr [0-9] d  # ddddabcd
echo 1234abcd|tr [a-z] [A-Z]  # 1234ABCD
echo 1234abcd|tr "1234" "[A*]C"  # AAACabcd
echo 1234abcd|tr "1234" "AC" # ACCCabcd
echo 1234abcd|tr -t "1234" "AC"  # AC34abcd
# 以上这种由set1转换为set2,是一一对应的,如果个数对不上,默认重复set2的最后一个字符。加上-t,可以避免重复,而不替换;

echo 1234abcd|tr -d [:digit:]  # abcd
echo 1234abcd|tr -dc [:digit:] # 1234
# -d 删除匹配的字符,-dc 保留匹配的字符(相当于反选)并且-c删除了末尾的换行符;

echo aaa123bbbb123|tr -s 'ab'  # a123b123
echo aaa123bbbb123|tr -s '123' 'ccc'  # aaacbbbbc
# -s 代表压缩,第一个命令是对a和b字符压缩,第二个命令是先将123替换为ccc,再对ccc字符压缩;
mkdir
  1. mkdir a/{b, c} 指定目录下同时创建多个文件夹
  2. mkdir -p xx/xxx 代表无父目录可直接创建
解压和压缩
  1. tar czf a.tar.gz 1.txt 2.txt
  2. tar xzf a.tar.gz -C 目录
    都可以加-v,显示具体过程
sort和uniq
shell 复制代码
cat file|sort  # 默认按ASCII码排序
cat file|sort -u  # 去重排序
echo -e '2\n1\n-9'|sort -n  # 按数字排序
sort -r # 反向排序
sort -k2 file # 按第二列排序
uniq  # 按行去重,只会对相邻重复的行去重
uniq -d file # 列出重复的行
uniq -u file # 列出不重复的行
uniq -c # 统计行的重复次数
# 实现对文件中获取重复行的重复次数
sort example.txt | uniq -ic  # -i 不区分大小写
curl
shell 复制代码
curl -s -I http://example.com | grep HTTP
# -s 安静模式,不打印错误信息、进度信息、网速等;
# -I 发送一个 HEAD 请求,仅请求页面的 HTTP 头部信息,当仅需检查状态码时(HTTP/1.1 200 OK)可使用此命令。
shell内置和外置命令

内置命令通过compgen -b 查看;

"type + 命令" 可以判断是内置还是外置命令;

内置命令:在系统启动时就加载入内存,常驻内存,执行效率更高,但是占用资源,如pwd;

外置命令:系统需要从硬盘中读取程序文件,再读入内存加载,一定会开启子进程执行;

shell循环

shell 复制代码
# 循环读取test.txt中的每一行
while read x
do
  echo input is $x
done<test.txt
shell 复制代码
until [ ${i:=0} -gt 10 ]
do
        let i++
        echo $i
done  # 1 2 ... 10 11

while循环在条件为真时继续执行,条件为假时停止。

until循环在条件为假时继续执行,条件为真时停止。

shell 复制代码
for((i=0;i<5;i++));do echo $i;done
for i in {0..4};do echo $i;done
for i in $(seq 0 4);do echo $i;done
for i in 0 1 2 3 4;do echo $i;done
# 0 1 2 3 4
shell 复制代码
num=3
if (($num==3));then echo succ;elif (($num>3));then echo bigger;else echo smaller;fi
# succ

shell计算

普通计算
shell 复制代码
res=$((10+67))
num=10
let num=num+10
echo $num  # 20
expr 10 + 9  # 19
# expr支持模式匹配,判断是否以.jpg为后缀,返回非0就是符合要求的;冒号代表计算匹配的字符长度
expr taohua.jpg : ".*\.jpg"  # 10
expr taohua.jpg : ".*"   # 10
expr length taohua.jpg   # 10
# bc命令,计算1+2+3+..+1000
echo {1..1000}|tr " " "+"|bc
# []计算
echo $[10-9]  # 1

需要注意的是,双小括号、let、expr、中括号只能进行整数计算,而bc可以支持小数计算。awk也可用于计算,虽然不是专门做计算的。

shell 复制代码
echo 2.4 4.5|awk '{print $1+$2}'  # 6.9

shell应用

统计字符串长度
shell 复制代码
# 方式一:
name="Katherine Pierce"
echo ${#name}

# 方式二:
echo $name|wc -L

# 方式三:
expr length "$name"

# 方式四:
echo $name|awk '{print length($0)}'
统计命令执行时间
shell 复制代码
time for i in {1..3000};do str1=`seq -s ":" 100`;echo ${#str1}>/dev/null;done
# real    0m5.143s   实际执行时间
# user    0m3.506s   用户态执行时间
# sys     0m1.692s   内核态执行时间

一般shell内置命令和语法,执行效率最高,因为底层都是用C实现,尽量减少管道符实现;

获取目录下的所有文件的完整路径
shell 复制代码
# demo.sh
#!/bin/bash

function get_file(){
  for file in `ls $1`
  do
    dir_file=$1/$file
    if [ -d $dir_file ]
    then
      get_file $dir_file
    else
      echo $dir_file
    fi
  done
}
get_file $1

# ./demo.sh /home/uos  打印目录下所有的文件
字符串截取
shell 复制代码
name="Katherine Pierce"
echo ${name:2}  # therine Pierce
echo ${name:2:3}  # the  截取3个字符
echo ${name:2:-3} # therine Pie 如果是负数,则截取到-3的位置处
name="Katherine therce"
echo ${name#*th}  # erine therce 左边开始匹配,删除最短匹配
echo ${name##*th}  ## erce  左边开始匹配,删除最长匹配
echo ${name%th*}  ## Katherine  右边开始匹配,删除最短匹配
echo ${name%%th*}  ## Ka  右边开始匹配,删除最长匹配
echo ${name/th/boy}  ## 仅替换第一个,Kaboyerine therce
echo ${name//th/boy}  ## 替换所有匹配,Kaboyerine boyerce
# cut命令也可用于截取字符
echo $name|cut -c 3-5  # the  -c按字符位置切割
扩展变量
shell 复制代码
# 如果param变量为空,则word赋给$res,不会给$param;注意如果param不为空,则不进行相关处理;
res=${param:-word}
# 这里的冒号可以省略,有时候看到这样的写法也是可以的: ${BASE_SOURCE-$0}

# 如果param变量为空,则word赋给$res和$param;注意如果param不为空,则不进行相关处理;
res=${param:=word}

# 如果param变量为空,word作为stderr错误输出,否则输出变量值 ${param:?word}
${age:?无变量错误}
# bash: age: 无变量错误

# 如果param变量为空,什么都不做,否则word返回 ${param:+word}
age=10
echo ${age:+jk}
# jk

# 按目录查找文件,这样实现避免dir_path变量未赋值时报错
find ${dir_path:-./} -name "*.png"

待补充

参考

  1. shell课程;
  2. shell数组
相关推荐
大树884 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠4 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush45 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5205 小时前
Linux 11 动态监控指令top
linux
小宇宙Zz5 小时前
Maven依赖冲突
java·服务器·maven
不会C语言的男孩6 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言
古城小栈6 小时前
Unix 与 Linux 异同小叙
linux·服务器·unix
程序猿阿伟7 小时前
《Chrome离线扩展安装的底层逻辑与场景落地指南》
服务器·网络·chrome
凡人叶枫7 小时前
Effective C++ 条款42:了解 typename 的双重意义
java·linux·服务器·c++
AC赳赳老秦8 小时前
用 OpenClaw 搭建服务器故障应急响应系统,自动处理 80% 常见运维故障
android·运维·服务器·python·rxjava·deepseek·openclaw