如何将含sed参数的变量转换为数组?适配bash4.4与sed4.4
Bash中兼容缩进格式的sed表达式数组简洁实现
使用环境
sed (GNU sed) 4.4 GNU bash, version 4.4.12(1)-release (x86_64-pc-linux-gnu)
问题背景
在bash脚本中需要使用20余个复杂的sed -e表达式,例如将aa bb cc转换为aaBBcc,单行写法如下:
sed -e 's# ##g' -e 's#b#B#g' <<< "aa bb cc"
或者用数组形式管理参数:
k=('-e' 's# ##g' '-e' 's#b#B#g'); sed "${k[@]}" <<< "aa bb cc"
为了提升可读性和可编辑性,我将这些参数以带空格、缩进的形式存入变量,示例如下:
#!/bin/bash k="-e s# # #g \ -e s# b # B #g \ "
现有转换为数组的方法虽能运行,但流程繁琐,且无法支持带引号的表达式(比如-e 's#b#B#g')。需要更简洁的实现,且必须兼容当前带空格的变量格式。
实际场景示例(书签URL转换)
原始URL
https://www.ebay.com/sch/i.html?_from=R40&_trksid=123456&_nkw=%28vintage%2Cvtg%29+%28polartec%2Cfleece%29+%28full%2Czip%2Czips%2Czipper%2Czippered%2Czipping%29+-%28hilfiger%2C%22old+navy%22%2Chooded%2Ccamo%2Ccamouflage%2Cvest%2Csmall%2Cmedium%2Cxl%2Cxxl%2Chalf%2Cquarter%2C%221%2F4%22%2C%221%2F2%22%2C+lined%2Cwinnie%2Ctoddler%2Ckids%2Cladies%2Cwomens%2Cwomen%29&_sacat=11450&LH_TitleDesc=0&_odkw=%28vintage%2Cvtg%29+fleece+%28full%2Czip%2Czips%2Czipper%2Czippered%2Czipping%29+-%28hilfiger%2C%22old+navy%22%2Chooded%2Ccamo%2Ccamouflage%2Cvest%2Csmall%2Cmedium%2Cxl%2Cxxl%2Chalf%2Cquarter%2C%221%2F4%22%2C%221%2F2%22%2C+lined%2Cwinnie%2Ctoddler%2Ckids%2Cladies%2Cwomens%2Cwomen%29&_osacat=11450&_sop=10&LH_PrefLoc=3&_ipg=240&_udhi=99
转换后URL
https://www.ebay.com/sch/i.html?&_nkw=(vintage,vtg)+(polartec,fleece)+(full,zip,zips,zipper,zippered,zipping)+-(hilfiger,old+navy,hooded,camo,camouflage,vest,small,medium,xl,xxl,half,quarter,1/4,1/2,+lined,winnie,toddler,kids,ladies,womens,women)&_sacat=1145011450&_sop=10&LH_PrefLoc=3&_udhi=99&_ipg=240
当前脚本
#!/bin/bash echo k="-e s# [&]*_from=R40 # # \ -e s# [&]*_trk[^&]* # # \ -e s# [&]*_odkw[^&]* # # \ -e s# [&]*_osacat[^&] # # \ -e s# [&]*_sacat=0 # # \ -e s# [&]*LH_TitleDesc=0 # # \ -e s# ++ # + #g \ -e s# %2F # / #g \ -e s# %28 # ( #g \ -e s# %29 # ) #g \ -e s# %2C # , #g \ -e s# %22 # #g \ -e s# &_ipg=[0-9]* # # \ -e s# $ # \&_ipg=240 # \ " k=$(sed -e 's# ##g' \ -e 's|###|# ##|g' \ -e 's#-e#|-e #g' \ -e 's#|##' \ <<< "$k" \ ) z=$IFS; IFS="|"; k=($k); IFS=$z sed "${k[@]}" <<< "$1"
简洁实现方案
方案1:无引号表达式的高效处理
如果不需要带引号的表达式,可通过sed预处理变量后用readarray生成数组,流程更简洁:
#!/bin/bash k="-e s# [&]*_from=R40 # # \ -e s# [&]*_trk[^&]* # # \ -e s# [&]*_odkw[^&]* # # \ -e s# [&]*_osacat[^&] # # \ -e s# [&]*_sacat=0 # # \ -e s# [&]*LH_TitleDesc=0 # # \ -e s# ++ # + #g \ -e s# %2F # / #g \ -e s# %28 # ( #g \ -e s# %29 # ) #g \ -e s# %2C # , #g \ -e s# %22 # #g \ -e s# &_ipg=[0-9]* # # \ -e s# $ # \&_ipg=240 # \ " # 预处理:去掉缩进、多余空格,拆分每个-e表达式为独立行 processed=$(sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//; s/[[:space:]]+#/#/g' <<< "$k" | tr '\n' ' ' | sed -E 's/ -e /\n-e /g') # 将处理后的内容读入数组 readarray -t sed_args < <(echo "$processed") # 执行sed转换 sed "${sed_args[@]}" <<< "$1"
方案2:支持带引号表达式的兼容处理
如果需要使用带引号的表达式(比如-e 's#b#B#g'),可通过eval安全构造数组,完美兼容原有缩进格式:
#!/bin/bash k="-e 's# [&]*_from=R40 # #' \ -e 's# [&]*_trk[^&]* # #' \ -e 's# ++ # + #g' \ -e 's# %2F # / #g' \ -e 's# %28 # ( #g' \ -e 's# %29 # ) #g' \ -e 's# %2C # , #g' \ -e 's# %22 # #g' \ -e 's# &_ipg=[0-9]* # #' \ -e 's# $ # \&_ipg=240 #' \ " # 生成数组定义字符串:去掉缩进和换行 array_def="sed_args=($(sed -E 's/^[[:space:]]+//; s/[[:space:]]+$//' <<< "$k" | tr '\n' ' '))" # 执行数组定义 eval "$array_def" # 执行sed转换 sed "${sed_args[@]}" <<< "$1"
这两种方案都保留了原变量的缩进格式,提升了可读性,同时解决了原方法流程复杂、不支持带引号表达式的问题。
内容的提问来源于stack exchange,提问作者Will
相关产品推荐
相关产品推荐

