Shell学习
Contents
💠
-
- 4.1. 常用代码片段
💠 2024-11-14 23:33:08
学习Shell
shellcheck
Shell语法检测
cmd-wrapped统计命令执行历史
shell类别
切换默认shell
chsh -s /bin/zsh
- sh
- 几乎所有Linux都支持的shell类别
- bash
- 主流发行版都支持的shell
- Zsh
- dash
- 它主要是为了执行脚本而出现,而不是交互,它速度更快,但功能相比bash要少很多,语法严格遵守POSIX标准
- 速度确实要快,输入上的交互确实交互不了
- fish
- 交互式的,补全功能比较好,适合不懂太多的新手会提示大部分命令接下来的参数以及历史,不适合已经做了大量默认配置的用户(这个时候的交互式提示反而影响操作)。
- Garuda发行版的默认Shell
执行方式
-
文件头部
#!/bin/sh
表示要使用sh解释器来执行, 可以替换成bash dash- 只要该文件具有执行权限就可以直接运行了
./a.sh
或者绝对路径
- 只要该文件具有执行权限就可以直接运行了
-
pueue
shell后台执行队列
基础结构
输入输出
输入
read answer
处理管道的输入也是使用 read
|
|
select
|
|
输出
echo printf
printf
- 原样输出字符串:
- printf("%s", str);
- 输出指定长度的字符串, 超长时不截断, 不足时右对齐:
- printf("%Ns", str); N 为指定长度的10进制数值
- 输出指定长度的字符串, 超长时不截断, 不足时左对齐:
- printf("%-Ns", str); N 为指定长度的10进制数值
- 输出指定长度的字符串, 超长时截断, 不足时右对齐:
- printf("%N.Ms", str); N 为最终的字符串输出长度 M 为从参数字符串中取出的子串长度
- 输出指定长度的字符串, 超长时截断, 不足时左对齐是:
- printf("%-N.Ms", str); N 为最终的字符串输出长度 M 为从参数字符串中取出的子串长度
- 上述N,M是可以动态指定的,方法是用*代替M或者N,然后在参数列表里加上一个数字参数。
printf("%-*.*s", 5, 2, "123");
等价于printf("%-5.2s", "123");
printf("%*s", 5, "123");
printf("%5s", "123");
变量
- 获取命令输出作为变量
$(ls)
`ls`
变量作用域
比Python的作用域更加宽泛和不可控,引用一个变量时需要注意值的来源。
嵌套
|
|
${command}
执行 command变量 内容echo ${command} |awk '{run=$0;system(run)}'
推荐方式
数据类型
整型
- 自增:
- i=$(( $i + 1 )) dash sh 都有效
- ((a++))
i=`expr $i + 1`;
- let i+=1;
- i=$[$i+1];
- 取余
- i=$(( $i % 3))
- 四则运算 参考博客
- ((i=$j+$k)) 等价于 i=
expr $j + $k
- ((i=$j-$k)) 等价于 i=
expr $j -$k
- ((i=$j*$k)) 等价于 i=
expr $j \*$k
- ((i=$j/$k)) 等价于 i=
expr $j /$k
- ((i=$j+$k)) 等价于 i=
判断变量是否为数值
|
|
字符串
- 字符串截取 | Blog:变量字符串截取 | Shell正则
Pattern | 描述 | |
---|---|---|
${varible#*str} |
截取 首个 |
str 右 的字符串 |
${varible##*str} |
截取 最末 |
str 右 的字符串 |
${varible%%str*} |
截取 首个 |
str 左 的字符串 |
${varible%str*} |
截取 最末 |
str 左 的字符串 |
${varible:start:end}
定长截取
${varible:4}
第四个字符到结束
ls -al | cut -d "." -f 2
取常规文件后缀名
获取命令的输出
-
使用 保存结果的变量名=
需要执行的linux命令
这种方式来获取命令的输出时,注意的情况总结如下: -
1)保证反单引号内的命令执行时成功的,也就是所命令执行后$?的输出必须是0,否则获取不到命令的输出
-
2)即便是命令的返回值是0,也需要保证结果是通过标准输出来输出的,而不是标准错误输出,否则需要重定向
-
因此我们推荐使用 保存结果的变量名=
需要执行的linux命令 2>&1
的方式来获取命令的执行结果。 -
输出变量时:
$var
会丢失换行和空格"$var"
不会
字符串的包含问题
|
|
求长 ${#var}
字符串拆分成数组
- 如果是空格分割的字符串
- 直接
for element in $target
- 直接
数组
结构
传递参数
参考博客
命令行选项 参数处理
参数 | 说明 |
---|---|
$# |
传递到脚本的参数个数 |
$* |
以一个单字符串显示所有向脚本传递的参数。以"$1 $2 … $n"的形式输出所有参数。 |
$$ |
脚本运行的当前进程ID号 |
$! |
后台运行的最后一个进程的ID号 |
$@ |
与$*相同,但是使用时加引号 以"$1" “$2” … “$n” 的形式输出所有参数。 |
$- |
显示Shell使用的当前选项,与set命令功能相同。 |
$? |
显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。 |
读取脚本参数
|
|
判断
if
注意方括号格式时,内部要留一个空格,否则会报 语法错误
判断文件
-
文件存在
if [ -e path ]
- -r 可读
- -f 存在,而且是普通文件
- -s 存在且文件大小大于0字节
-
链接存在
if [ -L path ]
-
目录存在
if [ -d path ]
-
整数比较
-eq
等于,如:if [ “$a” -eq “$b” ]-ne
不等于,如:if [ “$a” -ne “$b” ]-gt
大于,如:if [ “$a” -gt “$b” ]-ge
大于等于,如:if [ “$a” -ge “$b” ]-lt
小于,如:if [ “$a” -lt “$b” ]-le
小于等于,如:if [ “$a” -le “$b” ]大于
(需要双括号),如:(("$a" > “$b”))>=
大于等于(需要双括号),如:(("$a" >= “$b”))
-
字符串比较
if [ "$root"x = 'x' ]
判断是否为空if test -z "$repo_path";
if [ $(echo $str | grep -e '$str1') ]
判断包含if [[ $a == z* ]]
模式匹配,但是不是严格正则表达式格式
-
判断数组包含
if [[ "${ary[@]}" =~ "$a" ]]
- 其中 ary=(1 2 3); a=2;
case
|
|
循环
for
|
|
while
|
|
逐行遍历命令的输出
|
|
Tips
- break continue: break能跳出多层循环,只需带上数字
break num
,也可以理解为无参的break默认带了1参数 - done 后面可以重定向,将循环的输出转到文件而不是终端:
for do done > loop.log
函数
Shell的函数只能返回整型数据类型
- 定义函数
function a {}
a(){}
- 注意:
- Shell是解释执行,必须先定义,然后调用,而且函数没有重载,只覆盖
- 命令行内定义函数:
function hi { echo hi;};
注意左括号和命令的空格,以及命令;结尾
|
|
- 引用 shell 文件
source shell文件相对路径
source可以简写为.
多线程
定时执行
watch
execute a program periodically, showing output fullscreen
- -d 高亮差异数据
watch 等待命令对应进程执行完成后才进入计时到下一个周期执行,可以利用这个特性来执行异步shell
demo.sh
|
|
watch demo.sh 达到的效果为:等到sh中的100个子进程执行结束后,主进程退出,才会等2s再执行一次demo.sh
sleep
集成
文件处理
读取当前目录文件
|
|
读取文件行
|
|
- 当前目录创建临时文件,并输出创建的文件名
mktmp data.XXXXXX
-t
在 /tmp/目录下创建,并返回全路径-d
创建目录
- 输出到终端并写入文件
echo "test" | tee a.log
- 基于模板快速创建多份配置文件
1 2
REPLICA=01 SHARD=01 envsubst < config.xml > clickhouse01/config.xml REPLICA=02 SHARD=01 envsubst < config.xml > clickhouse02/config.xml
- config.xml 中使用
${}
做占位符 例如:<interserver_http_host>clickhouse${REPLICA}</interserver_http_host>
- config.xml 中使用
配置文件
ini和conf
|
|
- 如果没有
[block]
这样的声明就可以当sh用, 直接 source file 就加载了配置内容
shyaml
脚本的参数自动补全
Bash
Zsh
更为直观, 简单
学习怎么使用的话, 可以看上面的博客(虽然有点简陋), 但是如果是 oh-my-zsh 的用户, 可以直接看别人的插件, 模仿就行了, 例如 redis-cli 插件的自动补全, 就很简单直接
#compdef redis-cli rec
这第一行很重要, 定义了是对哪个命令或脚本的自动补全
Tips
hyperfine命令压测工具
- set -x 开启调试 每条实际执行的命令都会输出到控制台
- set +x 关闭调试
常用代码片段
- 获取命名或函数标准输出: 反引号 `cmd` 或者 $(cmd)
- 检查当前用户为Root用户
1 2 3 4
if [ $(id -u) != "0" ]; then printf $red"Please use root to run this script\n"$end exit 1 fi
- kill 脚本进程
1 2 3 4 5 6
id=`ps -ef | grep "WithRedis.py" | grep -v "grep" | grep -v "\-d" | awk '{print $2}'` if [ "${id}1" = "1" ];then printf $red"not exist background running script\n"$end else kill -9 $id fi
- 得到脚本绝对路径;
如果脚本内执行 pwd 只会得到执行脚本时会话的绝对路径,而不是脚本的路径
1
basepath=$(cd \`dirname $0\`; pwd)
Author Kuangcp
LastMod 2018-12-15