当前位置: 首页 > news >正文

Linux-Shell编程

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

写一个hello.sh脚本:
1.mkdir scripts
2.cd scripts
3.touch hello.sh
4.vim hello.sh

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

5.bash hello.sh(另外启用一个bash进程)
5.chmod +x /scripts/hello.sh /scripts/hello.sh(必须要用相对或绝对路径,不能进入scripts文件夹再执行) ./hello.sh(最常用)
5.(前两种都启用子shell进程)source hello.sh . hello.sh(不启用子shell进程,没有父子shell嵌套关系)


用ps -f可以查看bash进程数量,用exit可以退出子bash进程。
开子shell与不开子shell的区别就在于,环境变量的继承关系,如在子shell中设置的当前变量,父shell是不可见的。

echo $HOME查看HOME系统变量
env | less查看所有系统全局变量
set | less查看定义的所有变量

自定义变量

在子shell更改变量,不影响父shell。
全局环境变量名字建议大写。

a=2(等号两边不能加空格)
echo $a

s="hello, world"(变量的值有空格,需要使用双引号括起来。)
echo $s
export s(升级成全局变量)

a=$((1+5))(可以进行符号运算) a=$[1+5]
echo $a

readonly b=5(定义一个只读变量)
b=10(报错,只读)

unset a(删除变量a)
unset b(删除不了,只读)


特殊变量

1.$n (功能描述:n为数字,$0代表该脚本名称,$1-$9代表第一到第九个参数,十以上的参数需要用大括号包含,如${10})

vim hello.sh

#!/bin/bash echo "hello, world" echo "hello, $1"

./hello.sh xiaoming(将xiaoming填入$1)


vim parameter.sh
单引号就不会把里面的$认为是变量,而是原封不动输出。

#!/bin/bash echo '----------$n-----------' echo script name:$0 echo 1st parameter:$1 echo 2nd parameter:$2

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


2.$#(功能描述:获取所有输入参数个数,常用于循环,判断参数的个数是否正确以及加强脚本的健壮性)。

vim parameter.sh

#!/bin/bash echo '----------$n-----------' echo script name:$0 echo 1st parameter:$1 echo 2nd parameter:$2 echo '----------$#-----------' echo parameter numbers:$#

./parameter.sh abc def

3.$*(功能描述:这个变量代表命令行中所有的参数,$*把所有的参数看成一个整体)
$@(功能描述:这个变量也代表命令行中所有的参数,不过$@把每个参数区分对待)

vim parameter.sh

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

./parameter.sh abc def

4.$?(功能描述:最后一次执行的命令的返回状态。如果这个变量的值为0,证明上一个命令正确执行:如果这个变量的值为非0(具体是哪个数,由命令自己来决定),则证明上一个命令执行不正确了。)

echo $?(接上面代码,结果为0)
parameter.sh(报错)
echo $?(非0)


运算符

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

cd /scripts
vim add.sh

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

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


条件判断

[ condition ](注意condition前后要有空格)
注意:条件非空即为 true,[ happygame ]返回true,[ ]返回 false。


[ $a = Hello ](等号两边需要空格,否则识别成一个值)
echo $?(如果a是Hello则结果为0)

[ $a != Hello ]
echo $?(a是Hello则结果为1)

两个整数之间比较

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

[ 2 -lt 8 ]
echo $?(结果为0)

[ 2 -gt 8 ]
echo $?(结果为1)

按照文件权限进行判断

-r有读的权限(read)
-w有写的权限(write)
-x有执行的权限(execute)

touch test
[ -r test ]
echo $?

[ -w test ]
echo $?

[ -x test ]
echo $?

按照文件类型进行判断

-e文件存在(existence)
-f文件存在并且是一个常规的文件(file)
-d文件存在并且是一个目录(directory)

[ -e test ]
echo $?

[ -f test ]
echo $?

[-d test ]
echo $?

a=15
[ $a -lt 20 ] && echo "$a < 20" || echo "$a > 20"(输出15 < 20)
a=27
[ $a -lt 20 ] && echo "$a < 20" || echo "$a > 20"(输出27 > 20)


if单分支

cd /scripts
vim if_test.sh
在条件判断里面加入双引号和x防止报错,当变量传入为空时就用上x。

#!/bin/bash if [ "$1"x = "happygame"x ] then echo "welcome, happygame" fi

chmod +x if_test.sh
./if_test.sh happygame

a=25
if [ $a -gt 18 ] && [ $a -lt 35 ]; then echo OK; fi(输出OK)


if多分支

vim if_test.sh

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

./if_test.sh happygame 15(未成年人)
./if_test.sh happygame 25(成年人)


vim if_test.sh

#!/bin/bash if [ "$1"x = "happygame"x ] then echo "welcome, happygame" 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 happygame 25(青年人)
./if_test.sh happygame 45(中年人)
./if_test.sh happygame 65(老年人)


case

vim 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(two)
./case_test.sh 6(number else)


for

vim sum_to.sh
用$取到sum和i的值,加号运算符要在$和[]运算表达式中

#!/bin/bash for (( i=1; i <= $1; i++ )) do sum=$[ $sum + $i ] done echo $sum

chmod +x sum_to.sh
./sum_to.sh 100(输出5050)

以下是linux中常用的写法:
for os in linux windows macos; do echo $os; done(输出这三个操作系统名称)
for i in {1..100}; do sum=$[$sum+$i]; done; echo $sum(输出5050)


$*和$@的区别:
vim 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 e
$*把它们作为一个整体,$@仍然把他们作为独立个体。
结果如下:


while

vim 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] done echo $sum2

./sum_to.sh 100(输出两个5050)


read

用于读取控制台输入。

-p指定读取值时的提示。
-t指定读取值时等待的时间(秒)如果不加 - t则一直等待。


vim read_test.sh

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

chmod +x read_test.sh
./read_test.sh


系统函数

vim cmd_test.sh
$(date +%s)相当于用$()将date函数的值取出来。

#!/bin/bash filename="$1"_log_$(date +%s) echo $filename

chmod +x cmd_test.sh
./cmd_test.sh happygame


basename /root/scripts/parameter.sh(结果输出parameter.sh)
basename /root/scripts/parameter.sh .sh(结果输出parameter,删除了后缀.sh)
vim parameter.sh

#!/bin/bash echo '----------$n-----------' echo script name: $(basename $0 .sh) echo 1st parameter:$1 echo 2nd parameter:$2 echo '----------$#-----------' echo parameter numbers:$# echo '----------$*-----------' echo $* echo '----------$@-----------' echo $@

/scripts/parameter.sh a b

dirname /root/scripts/parameter.sh(结果输出/root/scripts)
vim parameter.sh
先进入路径,再得出绝对路径。

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

./parameter.sh a b


自定义函数

vim fun_test.sh
用sum变量得到echo的返回值,如果用$?返回只能返回0-255的数字。

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

chmod +x 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 * $sum]

练习一:归档文件

vim 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" echo else echo "归档出现问题!" echo fi exit

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

接下来将这个脚本加入到定时任务中:
crontab -e

0 2 * * * /root/scripts/daily_archive.sh /root/scripts

常用特殊字符

cat /etc/passwd | grep ^a找到以a开头的内容
cat /etc/passwd | grep bash$找到以bash结尾的内容
cat daily_archive.sh | grep -n ^$找到空行的位置,-n显示行号
cat daily_archive.sh | grep r..t找到r和t中间有两个任意字符的内容,如root
cat etc/passwd | grep ro*t找到o出现0次或多次的内容,如rt,rot,root等
cat etc/passwd | grep ^a.*bash$找到以a开头以bash结尾的内容
cat etc/passwd | grep ^a.*var.*in$找到以a开头以in结尾,中间还有var的内容
[6,8]匹配6或者 8
[0-9]匹配一个0-9的数字
[0-9]*匹配任意长度的数字字符串
[a-z]匹配一个a-z之间的字符
[a-z]*匹配任意长度的字母字符串
[a-c,e-f]匹配a-c或者e-f之间的任意字符
cat /etc/passwd | grep r[a,b,c]*t找到rt,rat,rbt,rabt等的内容
cat daily_archive.sh | grep '\$'用\转义,找到含$的内容
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}$用-E支持扩展的正则

cut

vim cut.txt

dong shen guan zhen wo wo lai lai le le

cat cut.txt

-f列号,提取第几列
-d分隔符,按照指定分隔符分割列,默认制表符\t

-c

按字符进行切割,加n表示取第几列

cut -d " " -f 1 cut.txt(截取出第一列的内容)
cut -d " " -f 2,3 cut.txt(截取第二、三列的内容)

cat /etc/passwd | grep bash$

cat /etc/passwd | grep bash$ | cut -d ":" -f 1,6,7
截取出以下内容:

cat /etc/passwd | grep bash$ | cut -d ":" -f 1-4截取1-4列
cat /etc/passwd | grep bash$ | cut -d ":" -f -4截取1-4列
cat /etc/passwd | grep bash$ | cut -d ":" -f 4-截取4到最后一列
ifconfig ens33 | grep netmask | cut -d " " -f 10截取到IP地址,inet前面有8个空格
ifconfig | grep netmask | cut -d " " -f 10截取出IP地址,本地环回地址,虚拟地址

awk

-F指定输入文件分隔符,空格为默认分隔符
-v赋值一个用户定义变量
cat /etc/passwd | grep ^root | cut -d ":" -f 7以root开头的第七列所有内容
cat /etc/passwd | awk -F ":" '/^root/ {print $7}'等价于上面的式子
cat /etc/passwd | awk -F ":" '/^root/ {print $1","$7}'第一列和第七列,要给逗号打双引号拼接
cat /etc/passwd | awk -F ":" '/^root/ {print $1","$6","$7}'第一列第六列和第七列
cat /etc/passwd | awk -F ":" 'BEGIN{print "user, shell"}{print $1","$7} END{print "end of file"}'在最开头加入user, shell,在结尾加入end of file
cat /etc/passwd | awk -F ":" '{print $3+1}'将第三列的数字全部加1
cat /etc/passwd | awk -v i=1 -F ":" '{print $3+i}'将第三列的数字全部加i,更灵活
awk的内置变量
FILENAME文件名
NR已读的记录数(行号)
NF浏览记录的域的个数(切割后列的个数)
awk -F ":" '{print "文件名:"FILENAME " 行号:"NR " 列数:"NF }' /etc/passwd显示文件名、行号、列数
ifconfig | awk '/^$/ {print "空行:"NR}'显示空行的行号
ifconfig | awk '/netmask/ {print $2}'显示IP地址,本地环回地址,虚拟地址,awk前八个空格不考虑

练习二:发送消息

mesg(查看是否打开消息功能)
who -T(+则打开了消息功能,-则关闭了消息功能)

vim send_msg.sh

#!/bin/bash # 查看用户是否登录(-i忽略大小写,-m最多拿几行,awk那第一列) login_user=$(who | grep -i -m 1 $1 | awk '{print $1}') # -z判断是否为空 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}') # 写入要发送的消息(write写入要指定发送的用户和发送的终端) 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 happygame hi, happygame

http://www.jsqmd.com/news/595536/

相关文章:

  • WPF性能优化实战:利用Dispatcher优先级与骨架屏实现流畅加载
  • 避开时区陷阱:React Spectrum日期时间处理完全指南
  • 告别云端依赖!用Ollama+GPT-OSS-20B打造个人专属AI,免费又安全
  • 保姆级教程:Qwen3-TTS-Tokenizer-12Hz快速入门,小白也能玩转音频压缩
  • RVC GPU算力适配指南:A10/A100/V100显存优化配置方案
  • Ollama+GPT-OSS-20B黄金组合:无需网络,随时可用的智能助手
  • PyTorch 2.8镜像部署教程:RTX 4090D上量化Llama-3-8B至INT4推理实操
  • Qwen3.5-2B效果实测:对中文OCR弱场景(艺术字/印章)识别增强方案
  • 为什么algorithms是Ruby开发者的终极选择:8种排序算法性能对比分析
  • 如何利用社交媒体平台来优化网站SEO
  • 别再只调包了!用Python从零手搓K-Means,在鸢尾花数据集上彻底搞懂聚类
  • Audio Pixel Studio实操案例:中小企业低成本AI配音工作站搭建全过程
  • 开源模型可持续维护:雯雯的后宫-造相Z-Image-瑜伽女孩版本更新与回滚策略
  • Chandra OCR快速上手:一键安装vLLM,开箱即用的布局感知OCR
  • GLM-OCR系统资源优化:C盘清理与显存高效利用技巧
  • 终极ESLint代码审查效率提升指南:使用diff、multiplexer等工具优化工作流程
  • Qwen3.5-9B-AWQ-4bit LSTM时间序列预测模型原理与调参详解
  • TensorRT加速HY-Motion:NVIDIA推理性能提升方案
  • 终极指南:如何用SuperDuperDB CDC技术构建实时AI应用
  • 如何快速实现jsTree上下文菜单:为树形节点添加智能右键操作功能
  • PasteMD快捷键自定义指南:提升操作效率的实用技巧
  • 实测有效:FLUX.1+SDXL风格,3分钟生成游戏UI按钮图标
  • OpenClaw模型微调:让Phi-3-mini适配你的专属工作流
  • Swagger Client 与微服务架构:如何管理多个 API 端点的终极方案
  • 终极指南:如何为开源本地AI模型平台Gallery44贡献代码
  • 2026年4月目前评价高的折弯机企业推荐,PSH-SSM伺服折弯机/电液同步折弯机,折弯机实力厂家哪个好 - 品牌推荐师
  • Play与Hubot集成教程:通过聊天机器人控制企业音乐播放
  • BepuPhysics2查询系统完全指南:射线检测、扫掠查询与体积查询实战
  • 从唤醒到合成:基于讯飞、VOSK与DeepSeek的纯离线语音助手全链路实践
  • 终极FlyingCarpet使用指南:掌握拖放传输与QR码扫描的高效文件分享技巧