Shell 编程常见用法

本文主要记录写 shell 脚本时的常用写法。

目录

  • 规范
  • 配置
    • 配置脚本报错立即退出
    • 配置任何一个子命令失败,整个管道命令就失效,脚本终止执行
  • 判断操作
    • 若变量未设置则为真
    • 判断变量不为空
    • 判断是否等于某字符串
    • 判断文件中是否包含特定字符串
    • 判断文件是否存在
    • 判断文件夹是否存在
    • 判断路径是否存在
    • 多项判断错误
      • condition expected
    • 文件比较符
    • 判断系统是否安装了某软件
  • 比较
  • 查找
    • 查找排除某个或多个目录的文件
    • 限制目录深度查找文件/文件夹
    • 仅查找文件夹
  • 匹配
    • grep
      • 匹配数字、浮点数、版本号
      • 仅显示匹配部分
      • grep 统计匹配到的内容数量
  • 获取
    • 逐行读取文件
    • 获取找到的匹配文件的数量
  • sed 用法
    • 在文件末的下一行添加内容
    • 去除字符串中的引号
    • 替换特定的字符串
  • 是分隔符,用 $3 替换 $2
    • 删除文件中特定字符串的操作
  • 正则表达式
  • 字符串处理
    • 替换特定字符串
    • 以特殊字符分隔字符串为数组
    • 查找某文件特定字符串后的内容
    • 获取字符串变量特定字符串之后的内容
    • 查找文件或变量中特定的字符串
    • 以特定字符分隔字符串
    • 字符串排序
    • 字符串排序并去除重复的字符串
  • 运算
    • awk
      • 除法运算
      • 打印每行的第一个字段
    • 整形变量自增
  • 卸载软件
  • Shell 脚本中调用另一个 Shell 脚本的方式
  • 颜色
    • 彩色打印
  • 变量
  • Json 处理
    • 使用 jq 处理 Json 格式
      • 获取 Json 数组的长度
      • 获取 Json 键值 Key
      • 获取 Key 对应的 Vaule
      • 获取不带引号的值
      • 使用 jq 小工具处理 json 文件
    • 命令行中传递 JSON 参数
  • Shell 脚本参数高级用法
    • getopt 长参数/长选项用法
    • getopts 参数匹配
  • 数组
    • 遍历数组的方法
    • 循环获取数组中的元素
    • 获取数组的元素个数
    • 将排序后的字符串存储到一个数组中
  • 循环遍历
  • 字符串数字互转
  • Shell 脚本获取 Python 命令的返回结果
  • 批量以日期重命名
  • 命令结果输出给变量
  • 下载
    • wget
  • patch
    • 打 patch
    • 生成 patch 文件
  • 错误修复
    • **BrokenPipeError: **管道重定向导致 stdout 错误
  • 致谢

规范

  • 脚本使用 set -o errexit 开始,使得脚本出错立即停止,避免出现更严重的问题。
  • 每个脚本开头必须写清楚解释器。否则在多个环境中默认解释器不同,运行结果也不同。

配置

配置脚本报错立即退出

set -o errexit
# ...
set +o errexit

set -e从根本上解决了这个问题,它使得脚本只要发生错误,就终止执行。
set +e表示关闭-e选项,set -e表示重新打开-e选项。
-e还有另一种写法-o errexit

配置任何一个子命令失败,整个管道命令就失效,脚本终止执行

set -o pipefail
# ...
set +o pipefail

判断操作

若变量未设置则为真

[ -z ${VAR} ]

判断变量不为空

if [ -n "${var}"]; then
	echo "not null"
fi
if [ "${var}" = "null" ]; then
	echo "var is null"
fi

判断是否等于某字符串

shell脚本判断变量是否包含某个字符串的几种方法_lkdcom的博客-CSDN博客_shell脚本判断字符串是否包含某个字符

if [ "${APPS_CHECK_RESULT}" = "OK" ];then
	echo "OK"
elif [ "${APPS_CHECK_RESULT}" = "FAIL" ];then
	echo "FAIL"
fi

判断文件中是否包含特定字符串

if [ `grep -c "字符串" 文件路径` -eq '0' ]; then
	echo "Found!"
else
	echo "error"
fi

判断文件是否存在

if [ -d 文件夹路径 ]; then echo "exist";fi
if [ ! -d 文件夹路径 ]; then echo "missing";fi

判断文件夹是否存在

if [ -d "/data/" ];then
  echo "文件夹存在"
else
  echo "文件夹不存在"
fi

判断路径是否存在

if [[ -x "${IDF_PATH}" ]]; then
  pushd ${IDF_PATH}
  local idf_ver=$(git ls-remote --heads origin release/${idf_ver_tag} | grep -o "release/.*")
  if [[ -n "$idf_ver" ]]; then
    export IDF_VERSION="release/${idf_ver_tag}"
  else
    export IDF_VERSION="${idf_ver_tag}"
  fi
  popd
else
  echo "IDF_PATH not set or path not exist"
fi

多项判断错误

condition expected

./tools/ci/apps_filter.sh:57: condition expected:
# 如果存在此情况的行是 if 判断语句,则可能是由于多项判断被大括号整体括住了,详情如下:
if [[ xxx ] && [ xxx ]]; then
	echo "这是错误的示范"
fi

文件比较符

-e 判断对象是否存在
-d 判断对象是否存在,并且为目录
-f 判断对象是否存在,并且为常规文件
-L 判断对象是否存在,并且为符号链接
-h 判断对象是否存在,并且为软链接
-s 判断对象是否存在,并且长度不为0
-r 判断对象是否存在,并且可读
-w 判断对象是否存在,并且可写
-x 判断对象是否存在,并且可执行
-O 判断对象是否存在,并且属于当前用户
-G 判断对象是否存在,并且属于当前用户组
-nt 判断file1是否比file2新  [ "/data/file1" -nt "/data/file2" ]
-ot 判断file1是否比file2旧  [ "/data/file1" -ot "/data/file2" ]

判断系统是否安装了某软件

if ! type ${name} >/dev/null 2>&1;then
        echo "${name} 未安装"
else
        echo "${name} 已安装"
fi

比较

shell 浮点数比较大小_51CTO博客_shell 浮点数比较

[root@db03 ~]# echo "10.3 > 10.1" | bc 
1
[root@db03 ~]# echo "1.1 < 0.7" | bc 
0

# 如果使用 if 判断一定注意使用下方写法
if [ `echo ${var}" > "${array[1]} | bc` = 1 ]; then
	echo "> "${array[1]}
else
	echo "< "${array[1]}
fi
# 不能使用下方写法
if [ `echo ${var}" > "${array[1]} | bc` ]; then  # 这是错误的示范
	echo "> "${array[1]}
else
	echo "< "${array[1]}
fi

查找

查找排除某个或多个目录的文件

find "要查找的目录路径" ! -path "*/希望排除的目录/*" -type f -name "要查找的文件名"
还可以排除多个目录
find "要查找的目录路径" ! -path "*/希望排除的目录1/*" ! -path "*/希望排除的目录2/*" -type f -name "要查找的文件名"
eg:

find . ! -path "*/test/*" -type f -name "*.js" ! -name "*-min-*" ! -name "*console*"

限制目录深度查找文件/文件夹

例如:在下一级目录中查找

find . -maxdepth 1 -name <find-name>
# eg: find . -maxdepth 1 -name esp-adf

仅查找文件夹

加一个参数 -type f。
例如:

  • f 普通文件
  • d 目录文件
  • l 链接文件
  • b 块设备文件
  • c 字符设备文件
  • p 管道文件
  • s socket文件
find . -name <find-folder-name> -type d

匹配

grep

匹配数字、浮点数、版本号

# -o 只输出匹配到的内容
echo "v4.4.3" | grep -o "[0-9].*"

仅显示匹配部分

grep 仅显示匹配部分_liuck的博客-CSDN博客_grep只显示匹配内容

grep -o 

grep 统计匹配到的内容数量

统计grep查找的字符 一共有多少个匹配_大胖头leo的博客-CSDN博客

grep -o "aaa" file | wc -l

获取

逐行读取文件

while read line
do
	echo $line
done < examples.txt

获取找到的匹配文件的数量

NUM=$(ls dir | grep "sdkconfig" | wc -l)
echo ${NUM}

sed 用法

在文件末的下一行添加内容

sed -i '$a\CONFIG_'$1'_BOARD=y' sdkconfig.defaults,其中 $1 是传入 shell 脚本的第一个参数,sdkconfig.defaults 是需要修改的文件。

去除字符串中的引号

echo \"hello\" | sed 's/\"//g'

替换特定的字符串

是分隔符,用 $3 替换 $2

sed -i "s#$2#$3#" $1

删除文件中特定字符串的操作

sed -i "s/\/Makefile//g" examples.txt

正则表达式

【Linux】【Shell】Shell 正则表达式_RadiantJeral的博客-CSDN博客_shell正则
Linux基础知识(18): Shell编程——正则表达式、字符截取命令、字符处理命令_TechArtisan6的博客-CSDN博客

字符串处理

替换特定字符串

shell 字符串操作 ${} 的截取,删除,和 替换 - 腾讯云开发者社区-腾讯云

# 普通替换
${string/match_string/replace_string}:将 string 中第一个 match_string 替换成 replace_string
${string//match_string/replace_string}:将 string 中的 match_string 全部替换成 replace_string [user@host dir]$ str=123abc123 [user@host dir]$ echo "${str/123/r}" rabc123 [user@host dir]$ echo "${str//123/r}" rabcr

# 前后缀替换
${string/#match_string/replace_string}:将 string 中第一个 match_string 替换成 replace_string
${string/%match_string/replace_string}:将 string 中的 match_string 全部替换成 replace_string [user@host dir]$ str=123abc123 [user@host dir]$ echo "${str/#123/r}" rabc123 [user@host dir]$ echo "${str/%123/r}" 123abcr

# 正则匹配
match_string 可以是一个正则表达式 [user@host dir]$ str=123abc123 [user@host dir]$ echo "${str/3*1/r}" 12r23

以特殊字符分隔字符串为数组

Shell_Linux Shell 中实现字符串切割的几种方法_Baiyi_destroyer的博客-CSDN博客_shell 切割字符串

#!/bin/bash
# 在某些 shell 环境使用 tr 会数组长度出错
string="hello,shell,split,test"
array=(${string//,/}
for var in ${array[@]}
do
	echo ${var}
done

查找某文件特定字符串后的内容

cat examples.txt | grep -o 'examples/.*' > example_cp.txt

获取字符串变量特定字符串之后的内容

echo ${result}
echo ${result#*:*:}

查找文件或变量中特定的字符串

result=$(cat ${ADF_PATH}/tools/ci/apps.json | grep -E $line)
# 或
result=$(echo ${board} | grep "${var}")

以特定字符分隔字符串

array=(`echo ${result} | tr ';' ' '` )
for var in ${array[*]}
do
	echo ${var}
done

字符串排序

可将相同的字符串紧挨在一起。然后取出重复的字符串。

echo ${var} | sort

字符串排序并去除重复的字符串

echo ${var} | sort -u

运算

awk

除法运算

echo ${ver} 2 | awk '{printf("%d\n",$1/$2)}'
echo ${被除数} ${除数} | awk '{printf("%2.f\n",$1/$2)}'

打印每行的第一个字段

git submodule | grep "esp-idf" | sed -r 's/-(.*) esp-idf/\1/g' | awk '{print $1}'

原本输出为
3c8bc2213cd5719d0c61ae9465a188eac83dfd70 esp-idf (v4.4.4-278-g3c8bc2213c)
使用 | awk '{print $1}'
3c8bc2213cd5719d0c61ae9465a188eac83dfd70

整形变量自增

((a++))
echo ${a}

卸载软件

sudo apt-get remove jq

Shell 脚本中调用另一个 Shell 脚本的方式

Command Explanation
fork 新开一个子 Shell 执行,子 Shell 可以从父 Shell 继承环境变量,但是子 Shell 中的环境变量不会带回给父 Shell。
exec 在同一个 Shell 内执行,但是父脚本中 exec 行之后的内容就不会再执行了
source 在同一个 Shell 中执行,在被调用的脚本中声明的变量和环境变量, 都可以在主脚本中进行获取和使用,相当于合并两个脚本在执行。

颜色

彩色打印

echo -e "\e[1;31m This is red test \e[0m"
说明:

  • -e :解释字符串中的转义字符,若不加此选项,转义字符会按照文本直接输出
  • \e[1;31m … \e[0m:前景色编号,其中 “\e” 输出Esc符号,也可以用 “\033”;"[1;31m"中,"1"表示粗体(高亮),"31"表示红色,中间用分号隔开,“m"后面为输出文本;”\e[0m"表示将颜色重新置回默认终端颜色

前景色种类:0 透明(使用终端颜色) 30 黑, 31 红, 32 绿, 33 黄, 34 蓝, 35 紫, 36 青绿, 37 白(灰)
背景色种类:0 透明(使用终端颜色) 40 黑, 41 红, 42 绿, 43 黄, 44 蓝 45 紫, 46 青绿, 47 白(灰)

变量

shell 中有两种声明变量的方式
第一种是直接赋值,比如a=1。但是一定要记住,以这种方法命名的变量的类型都是默认的字符串类型。这个为什么很重要我们在后面会说到,但是你一定要记住,默认的类型都是字符串类型。

第二种是使用declare命令。declare命令的格式为 declare [+或者-][选项] 变量名。其中-的含义是赋予变量名特定的属性,而+是取消变量名特定的属性,这点和我们平时的习惯有所不同,但是因为shell中很多命令的选项都是-xxx,相信大家也比较好理解。我们再说选项,选项主要有以下几种:

原文链接:https://blog.csdn.net/weixin_28698129/article/details/113490028
Shell 编程常见用法_第1张图片
declare -i 变量名称

Json 处理

使用 jq 处理 Json 格式

export ESP_ADF_MODULES='{"adf-libs-ut":"master","esp-adf-libs":""}'

获取 Json 数组的长度

cat apps_news.json | jq length

获取 Json 键值 Key

echo ${ESP_ADF_MODULES} | jq -r keys[]

获取 Key 对应的 Vaule

echo ${ESP_ADF_MODULES} | jq -r '.["adf-lib-ut"]'

获取不带引号的值

# 你会发现这个参数是获取到了但是怎么带了引号,jq 如何去掉引号获取呢?(现在我演示一下如何获取一个类似:{"name":"zmide","id":1234} 中获取 name 并赋值到 shell 变量上 )

# 去掉引号获取可以使用 -r 模式,这里我用了管道符代替文件了
echo '{"name":"zmide","id":1234}' | jq -r .name

# 将获取到的值赋值到 name 变量
name=`echo '{"name":"zmide","id":1234}' | jq -r .name`
echo ${name}

使用 jq 小工具处理 json 文件

sudo apt-get install jq

使用情景:

{
    "name": "test",
    "AppID": "com.test",
    "DisplayName": "demo",
    "Version": "1.0",
    "Build": 1,
}
jq -c .name ./app.json

jq 部分指令如下:–version
输出 jq 版本并退出。

–seq
使用application/json-seqMIME类型方案在jq的输入和输出中分隔JSON文本。这意味着将在输出的每个值之前打印一个ASCII RS(记录分隔符)字符,并在每个输出之后打印一个ASCII LF(换行)字符。无法解析的输入JSON文本将被忽略(但会发出警告),并丢弃所有后续输入,直到下一个RS。此模式还解析不带–seq选项的jq的输出。

–stream
以流方式解析输入,输出路径和叶值数组(标量和空数组或空对象)。例如,"a"变[[],“a”],并且[[],“a”,[“b”]]成为[[0],[]],[[1],“a”],和[[1,0],“b”]。
这对于处理非常大的输入很有用。将此与过滤以及the reduce和foreach语法结合使用,可逐步减少大输入。

–slurp/ -s
不必为输入中的每个JSON对象运行筛选器,而是将整个输入流读入一个大数组,然后仅运行筛选器一次。

–raw-input/ -R
不要将输入解析JSON。相反,每一行文本都作为字符串传递到过滤器。如果与组合–slurp,则整个输入将作为单个长字符串传递到过滤器。

–null-input/ -n
根本不读任何输入!而是将过滤器null作为输入运行一次。当将jq用作简单计算器或从头开始构造JSON数据时,这很有用。

–compact-output/ -c
默认情况下,jq pretty-prints JSON输出。通过将每个JSON对象放在一行上,使用此选项将导致输出更紧凑。

–tab
为每个缩进级别用一个选项卡,而不是两个空格。

–indent n
使用给定的空格数(不超过8个)进行缩进。

–color-output/ -C 和 --monochrome-output/ -M
默认情况下,如果写入终端,jq将输出彩色的JSON。即使使用写入管道或文件-C,也可以强制其产生颜色,并使用禁用颜色-M。
可以使用JQ_COLORS环境变量配置颜色。

–ascii-output/ -a
jq通常将非ASCII Unicode代码点输出为UTF-8,即使输入将其指定为转义序列(例如“ \ u03bc”)也是如此。使用此选项,您可以强制jq产生纯ASCII输出,并用等效的转义序列替换每个非ASCII字符。

–unbuffered
在打印每个JSON对象之后刷新输出(如果将慢速数据源输送到jq并将jq的输出输送到其他位置,则很有用)。

–sort-keys/ -S
用键按顺序输出每个对象的字段。

–raw-output/ -r
使用此选项,如果过滤器的结果是字符串,则将其直接写入标准输出,而不是将其格式化为带引号的JSON字符串。这对于使jq过滤器与基于非JSON的系统进行通信很有用。

–join-output/ -j
就像,-r但是jq在每个输出之后不会打印换行符。

-f filename/ --from-file filename
从文件而不是从命令行读取过滤器,如awk的-f选项。您也可以使用“#”发表评论。

-Ldirectory/ -L directory
前置directory为模块搜索列表。如果使用此选项,则不使用内置搜索列表。请参阅下面的模块部分。

-e/ --exit-status
设置为0 JQ的退出状态,如果最后输出值既不false也不null,1,如果最后的输出值要么false或null或4,如果没有有效的结果是以往任何时候产生的。通常,如果存在任何使用问题或系统错误,jq退出时为2;如果存在jq程序编译错误,则退出3;如果运行jq程序,则退出0。
设置退出状态的另一种方法是使用halt_error内置功能。

–arg name value
此选项将值作为预定义变量传递给jq程序。如果使用来运行jq --arg foo bar,则 f o o 该程序在程序中可用并且具有值 " b a r " 。请注意, v a l u e 它将被视为字符串,因此 − − a r g f o o 123 将绑定 foo该程序在程序中可用并且具有值"bar"。请注意,value它将被视为字符串,因此--arg foo 123将绑定 foo该程序在程序中可用并且具有值"bar"。请注意,value它将被视为字符串,因此argfoo123将绑定foo到"123"。
命名参数也可用于jq程序$ARGS.named。

–argjson name JSON-text
此选项将JSON编码的值作为预定义变量传递给jq程序。如果使用来运行jq --argjson foo 123,则$foo该程序在程序中可用并且具有值123。

–slurpfile variable-name filename
此选项读取命名文件中的所有JSON文本,并将已解析的JSON值数组绑定到给定的全局变量。如果使用来运行jq --slurpfile foo bar,则$foo该程序中可用,并且有一个数组,其元素对应于名为的文件中的文本bar。

–rawfile variable-name filename
此选项读取命名的文件,并将其内容绑定到给定的全局变量。如果使用来运行jq --rawfile foo bar,则$foo该程序中可用,并且有一个字符串,其内容与名为的文件中的tex有关bar。

–argfile variable-name filename
不使用,使用–slurpfile代替。
(此选项类似于–slurpfile,但是当文件只有一个文本时,则使用该文本,否则使用文本数组–slurpfile。)

–args
其余参数是位置字符串参数。这些可作为到jq程序使用$ARGS.positional[]。

–jsonargs
其余参数是位置JSON文本参数。这些可作为到jq程序使用$ARGS.positional[]。

–run-tests [filename]
在给定文件或标准输入中运行测试。这必须是给出的最后一个选项,并且不支持所有前面的选项。输入由注释行,空行和程序行组成,后跟一个输入行,与预期一样多的输出行(每个输出一个)和一个终止的空行。编译失败测试从仅包含“ %% FAIL”的行开始,然后是包含要编译的程序的行,然后是包含要与实际值进行比较的错误消息的行。

命令行中传递 JSON 参数

SAP Help Portal

Shell 脚本参数高级用法

这样处理shell脚本参数,爽多了! - 腾讯云开发者社区-腾讯云

getopt 长参数/长选项用法

shell小技巧–长选项参数getopt用法-腾讯云开发者社区-腾讯云

# Process parameters, normalize parameters
ARGS=`getopt -a -o c:h: --long check:,help -- "$@"`
if [ $? != 0 ];then
        echo "Terminating..."
        exit 1
fi

# Rearrange parameter order
eval set -- "${ARGS}"

# Processing parameters through shift and while loops
while :
do
  case $1 in
    -c|--check)
      CHECK_TYPE=$2
      shift
      ;;
    -h|--help)
      usage
      ;;
    --)
      shift
      break
      ;;
    *)
      echo "Internal error!"
      exit 1
    ;;
    esac
    shift
done

getopts 参数匹配

getopts 使用简单,但无长选项支持
这样处理shell脚本参数,爽多了! - 腾讯云开发者社区-腾讯云

#!/usr/bin/env bash
# -n 名称
# -a 作者
# -h 帮助
while getopts ":n:a:h" optname
do
  case "$optname" in
  "n")
  echo "get option -n,value is $OPTARG"
  ;;
  "q")
  echo "get option -a ,value is $OPTARG"
  ;;
  "h")
  echo "get option -h,eg:./test.sh -n 编程珠玑 -a 守望先生"
  ;;
  ":")
  echo "No argument value for option $OPTARG"
  ;;
  "?")
  echo "Unknown option $OPTARG"
  ;;
  *)
  echo "Unknown error while processing options"
  ;;
  esac
  #echo "option index is $OPTIND"
done

解释一下:

  • 有两个预先定义的变量,_OPTARG_表示选项值,OPTIND表示参数索引位置,类似于前面提到$1。
  • n后面有:,表示该选项需要参数,而h后面没有:,表示不需要参数
  • 最开始的一个冒号,表示出现错误时保持静默,并抑制正常的错误消息

数组

Shell数组添加元素注意事项__荣耀之路_的博客-CSDN博客_shell添加数组元素
元素追加。

遍历数组的方法

shell 数组遍历的3种方法_「已注销」的博客-CSDN博客_shell for 数组
shell 数组及其遍历的3种方法_Data_IT_Farmer的博客-CSDN博客_shell 数组

# 注意:数组第一个元素有时从下标 1 开始,也就是 TEST_MODULE_BRANCH[1]
for (( i = 0; i < ${#TEST_MODULE_BRANCH[@]}; i ++ )) do
    echo ${TEST_MODULE_BRANCH[i]}
done

循环获取数组中的元素

for num in ${var[*]}
do
	#....
done

获取数组的元素个数

echo ${#array[@]}

将排序后的字符串存储到一个数组中

array=`ls | sort`    # 这可不是存一个数组。
array=(`ls | sort`)  # 存储到一个数组

循环遍历

浅谈shell 遍历数组的几种方法_linux shell

i=0
while [ $i -lt ${#array[@]} ]
#当变量(下标)小于数组长度时进入循环体
do
	echo ${ array[$i] }
	#按下标打印数组元素
	let i++
done

字符串数字互转

shell 字符串和数字互相转换_小白的进阶的博客-CSDN博客_shell 字符串转数字

# 双括号运算符:
a=$(( 1 + 2 ));
echo $a;
# 等同于:
a=`expr 1 + 2`

Shell 脚本获取 Python 命令的返回结果

shell 脚本如何获取 python 命令返回的结果 | 程序员笔记

#! /bash/sh

python3 -u demo.py

if [ $? -eq 0 ];then
    echo '上一个命令执行成功'
else
    echo $?
    echo '上一个命令执行失败!'
fi

批量以日期重命名

shell批量以日期重命名文件 - 走看看

find ./log -name "*.log" | while read file
do
DATE=$(date +%Y%m%d_%H%M%S_%N)
mv $file ./temp_log/${DATE}.log
done

命令结果输出给变量

shell脚本将命令的输出结果赋值给变量_Yellow0523的博客-CSDN博客_shell将命令执行结果赋值给变量

#使用反引号,存入Line变量
line=`cat tempstatus.txt wc-1`
#使用邵(),存入变量
line=$(cat tempstatus.txt wc -1)
#输出Line的行数
echo $line

下载

wget

下载工具

wget https://github.com/llvm/llvm-project/releases/download/llvmorg-16.0.0/clang+llvm-16.0.0-86_64-linux-gun-ubuntu-18.04.tar.xz

patch

打 patch

# 方式一
patch -p0 < your.patch
# 方式二
patch -p0 -i your.patch
# 方式三
git apply -p0 your.patch

#这里比较推荐前两种方式,第三种方式有时候打不上。

解释一下 -p0 表示指定的目标文件前没有 a/ b/ 的前缀,这点与 git diff 生成的 patch 不同
patch 结构如下

必备要素为
--- 路径 a
--- 路径 b
@@ -行号,行数 +行号,行数 @@
- 内容
+ 内容
--- 路径 a
+++ 路径 b
@@ -行号,行数 +行号,行数 @@
- 内容
+ 内容

举例

--- components/audio_hal/driver/es8311/es8311.h
+++ components/audio_hal/driver/es8311/es8311.h
@@ -29,49 +29,47 @@
 #include "esp_types.h"
 #include "esxxx_common.h"

-
-#define ES8311_DAC_VOL_CFG_DEFAULT(a+b)     \
-    do {                                    \
-        .max_dac_volume = 32,               \
-        .min_dac_volume = -95.5,            \
-        .board_pa_gain = BOARD_PA_GAIN,     \
-        .volume_accuracy = 0.5,             \
-        .dac_vol_symbol = 1,                \
-        .zero_volume_reg = 0xBF,            \
-        .reg_value = 0,                     \
-        .user_volume = 0,                   \
-        .offset_conv_volume = NULL,         \
-    } while(0);
+#define ES8311_DAC_VOL_CFG_DEFAULT(a + b) do {   \
+    .max_dac_volume     = 32,                    \
+    .min_dac_volume     = -95.5,                 \
+    .board_pa_gain      = BOARD_PA_GAIN,         \
+    .volume_accuracy    = 0.5,                   \
+    .dac_vol_symbol     = 1,                     \
+    .zero_volume_reg    = 0xBF,                  \
+    .reg_value          = 0,                     \
+    .user_volume        = 0,                     \
+    .offset_conv_volume = NULL,                  \
+} while (0);

 #ifdef __cplusplus
 extern "C" {
--- components/audio_hal/driver/es8311/es8311.h
+++ components/audio_hal/driver/es8311/es8311.h
@@ -320,7 +320,7 @@
  *     - ESP_FAIL Parameter error
  *     - ESP_OK   Success
  */
-esp_err_t es8311_set_mic_gain(es8311_mic_gain_t gain_db) ;
+esp_err_t es8311_set_mic_gain(es8311_mic_gain_t gain_db);

 /**
  * @brief Print all ES8311 registers

生成 patch 文件

import difflib

formatted_code = StringIO(stdout).readlines()
diff = difflib.unified_diff(code, formatted_code,
                            filename, filename,
                            '(before formatting)', '(after formatting)')
diff_string = ''.join(diff)
if len(diff_string) > 0:
  sys.stdout.write(diff_string)

错误修复

**BrokenPipeError: **管道重定向导致 stdout 错误

if eval "$diff_command" | $CLANG_FORMAT_DIFF_SCRIPT -p1 | grep -q . &>/dev/null; then

若 $CLANG_FORMAT_DIFF_SCRIPT 中打印没有执行完时,外面的命令直接重定向或者关闭终端会发生以下错误

Traceback (most recent call last):
  File "/home/xxx/clang-format-diff.py", line 384, in <module>
    main()
  File "/home/xxx/clang-format-diff.py", line 373, in main
    sys.stdout.write(diff_string)
BrokenPipeError: [Errno 32] Broken pipe

解决方法
不使用管道

diff_result=$(eval "$diff_command")
clang_format_diff_result=$($CLANG_FORMAT_DIFF_SCRIPT -p1 <<< "$diff_result")
if [ -n "$clang_format_diff_result" ]; then

致谢

感谢上方链接中各位博主的帮助。大家可以去看看,这些方法都是验证过的。

你可能感兴趣的:(Shell,用法与编程,shell,脚本)