2 ############################################################
4 # author: devenkong(18151155@qq.com)
6 ############################################################
8 # strfmt函数是字符串中@{}变量的expanssion。不同于${}环境变量的
9 # expanssiin的是,@{}的expanssion输出到pipeline,且可以嵌套使用,
10 # 且只输出到pipeline。与strfmt类似的函数evaln实现的是字符串的多重
11 # eval,但是eval操作时,是将${}环境变量的内容expanssion到命令行,
12 # 多次expansion的方法。这在环境变量内包含的字符串内容较长,且变量嵌套
14 # 字符串进行@{}的解析之外,还可设置eval进行${}的expanssion,
15 # 和\特殊功能屏蔽符解析,以及使用trans进行\转义符的解析。
16 # strfmt函数的命令行程序为evstrn,将strfmt字符串的三种形式以参数
18 # strfmt的常用的用途,可用于templete文件中插入一些填充的信息,
19 # 或是应用中定义格式字符串,在使用时调用strfmt,根据环境变量内容,输出
20 # 不同运行环境下的字符串内容。例如系统中不同的软件的安装路径不同,使用
21 # strfmt定义的字符串,根据软件包名称等信息,输出一个软件包的安装路径。
22 # paths.shlib中定义的格式化字符串即是这个用途。
23 ############################################################
25 ############################################################
27 # strfmt函数中的变量操作和作用域的功能特性,抽象到attr.shlib
28 # 库文件中。所以需要将代码中对应的功能进行调整。
29 # 属性的表示,以__替换::,用于体现变量的层次关系。原有代码中未将
30 # 属性的代码以一个功能模块抽象成一组函数。
31 ############################################################
34 # 打开脚本中使用alias功能,否则脚本中使用alias无效,而在命令行才有效。
35 shopt -s expand_aliases
38 # @ strfmt解析的var,添加domain。${var domain}
45 ######################
47 ######################
51 ######################
53 ######################
62 ######################
64 ######################
68 # fdesc: process one line string to output.
89 eval line
="\${MAPFILE${embcnt}[$idx]}"
91 # todo:这里需要对输出的字符串,进行eval/evaln的处理。
94 # 屏蔽符"\\\\"在shell命令行解析成‘\\‘,所以实际字符串操作是是'\\'转换成'\\\\',但单个的'\'不进行replace。
95 # line="${line//\\\\/\\\\\\\\}"
96 # 当字符串转义后,'\\\\'转换成'\\',等同于字符串转义操作对'\\'不进行转义,使得奇数个'\'时进行转义,
97 # 如果未转义,\后的字符为非转义字符。如果奇数个'\'之后是'&','&'replace成$’\n'再以$’\n'分隔成数组时,
98 # 可根据'\'为奇数个,表示换行符replace之前被'\'屏蔽,&不作为变量标记符号使用。
101 if [[ $STRFMT_EVALN == "eval" ]]; then
102 ## eval echo "xxxxxxxxxxx=\"$line\""
103 line
="`eval echo -n \"\\\"$line\\\"\"`"
105 ## echo -n${STRFMT_TRANS} "${line[0]}"
106 elif [[ $STRFMT_EVALN == "evaln" ]]; then
107 line
="`evaln echo -n \"$line\"`"
110 # echo "STRFMT_TRANS=$STRFMT_TRANS"
112 # fmt变量以{}之间的字符串为变量,进行字符串插入
119 bakline
=( ${bakline} )
122 cnt
=$
((${#line[@]}-1))
125 echo -n${STRFMT_TRANS} "${line[0]}"
126 for ((i
=1; i
<cnt
; i
++)); do
127 case "${line[$i]:0:1}" in
129 [[ ! "${line[$i]}" =~
")" ]] && echo -ne "err($idx): ')' missing.\n" >&2 && break
132 reststr
="${line[$i]#*\)}"
133 bakreststr
="${bakline[$i]#*\)}"
138 [[ ! "${line[$i]}" =~
"}" ]] && echo -ne "err($idx): '}' missing.\n" >&2 && break
142 # STRFMT_PARAM=( ${var} )
151 if [[ ${var:1:1} != ':' ]]; then
154 var
="${STRFMT_DOMAIN}_${var}"
156 # ":",被dup的section作用域
158 var
="${STRFMT_DOMAIN}${var}"
166 # echo -e "\nvar=$var\n"
167 reststr
="${line[$i]#*\}}"
168 bakreststr
="${bakline[$i]#*\}}"
172 # 变量名称中不使用转义符,所以不需要判断行末的'\',存在'\'即是不正确的变量名
173 if [[ -z "$var" ||
"${var}" =~
[^
[:alnum
:]_-
]+ ||
${#line[$i]} -gt 256 ]]; then
175 echo -ne "err: symbol name '$var' is not valid.\n" >&2
177 elif [[ ! $var =~
[^
0-9] ]]; then
178 # 如果为数字,表示@{var paramlist}中的参数id。
179 # 格式字符串使用时的函数参数$1不使用,而@{1}用于格式字符串的参数
180 if [[ -n ${STRFMT_PARAM[${var}]} ]]; then
181 # echo "strfmt \${STRFMT_PARAM[${var}]}=${STRFMT_PARAM[${var}]}"
182 echo -n${STRFMT_TRANS} "${STRFMT_PARAM[${var}]}"
184 echo -ne "warn
: paramter
${var} is not valid.
\n" >&2
188 # 变量已定义,则调用strfmt_v2p()函数解析并输出
189 if [[ -n ${!var} ]]; then
190 # echo var="strfmt
$var"
194 [[ $ret != 0 ]] && return 3
196 echo -ne "warn
: symbol
&${line[$i]}& is not defined.
\n" >&2
204 id=$((${#bakreststr}-1))
206 baktmp="${bakreststr}"
207 [[ "${baktmp:$id:1}" != "\\" ]] && echo -n${STRFMT_TRANS} "${tmp}" && continue
209 str=${baktmp##*[^\\]}
210 if [[ $((${#str}%2)) == 1 ]]; then
211 # 再将字符串中的'\\'替换为'\',进行'\\'的转义操作。
213 echo -n${STRFMT_TRANS} "${tmp}@
"
216 echo -n${STRFMT_TRANS} "${tmp}"
220 while [[ $i < $cnt ]]; do
221 id=$((${#line[$i]}-1))
223 baktmp="${bakline[$i]}"
224 [[ "${baktmp:$id:1}" != "\\" ]] && echo -n${STRFMT_TRANS} "${tmp}" && break
226 str=${baktmp##*[^\\]}
227 if [[ $((${#str}%2)) == 1 ]]; then
228 # 再将字符串中的'\\'替换为'\',进行'\\'的转义操作。
230 echo -n${STRFMT_TRANS} "${tmp}@
"
233 echo -n${STRFMT_TRANS} "${tmp}"
241 # fsyntax: cat xxx | strfmt_p2p
242 # fdesc: 不指定第二个参数时,$1格式解析后输出到$2。第二个参数为-时输出到stdout
251 embcnt=$(( embcnt+1 ))
253 vname=MAPFILE${embcnt}
255 declare -g -a ${vname}
256 while read -r data; do
257 eval "${vname}+=( \"\
$data\" )"
259 if [[ $idx -gt 1 ]]; then
262 if [[ $idx == 0 ]]; then
267 eval data=\${$vname[$((idx-1))]}
268 if [[ -n $data ]]; then
275 [[ $idx -gt 1 ]] && echo
277 vname="vname
[$
((idx-1
))]"
278 if [[ -n ${!vname} ]]; then
279 strfmt_ln $((idx-1)) "${!vname}"
282 embcnt=$(( embcnt-1 ))
283 unset MAPFILE${embcnt}
289 # fsyntax: strfmt_v2p <str-var>
290 # fdesc: 不指定第二个参数时,$1格式解析后输出到$2第二个参数为-时输出到stdout
293 # echo strfmt_v2p\($1\)
294 strfmt_p2p < <(echo -n "${!1}"; echo)
299 ######################
301 ######################
303 #####################################################################
305 #####################################################################
309 # @ 多行字符串的fmt,输出通常为pipe或file,输出到var,通常使用evaln。
310 # # 字符串处理中,以pipe输出,逐行处理时,以var+=或var+=( $content )保存到环境变量。
311 # # 输出到file,实际以pipe输出的重定向来实现。所以strfmt的输出,都以stdout为输出。
313 # @ 输入包括var、pipe、file。
314 # # 以var为输入,用于环境变量定义的格式化字符串。
315 # # 以pipe为输入,通常输入的字符串内容较长。可以和其它程序以pipe连接,形成字符串处理链。
316 # # 以file为输入,输入的字符串内容较长,且以文件的形式存储。
317 # @ 无论是哪种输入,都转换为str的逐行处理,所以定义一个单行处理的alias命令行。
318 # # 以参数var为输入,stdout为输出。
322 # 环境变量的expanssion使用evaln进行多重eval,
323 # 多行字符串和pipe字符串的expanssion使用strfmt。
324 # 文本文件的expansion使用tmpl调用strfmt linedata,
331 # @ eval,进行$的expanssion和\特殊字符的屏蔽
333 # @ -e,与echo中的-e参数一样,对字符串中\转义符的解析
334 # @ fmt,对字符串中&var&的内容进行插入
337 # @ evaln:字符串的evaln,变量之后的@e只对参数变量进行\转义符解析,@E对字符串中的变量进行转义符的循环解析。
338 # @ strfmt:只进行&var&变量的插入。参数为var_fmt或VAR_FMT或var[1]时,输出到$var变量,其它输出到stdout。
339 # @ STRFMT_EVALN:对于fmt字符串进行eval输出,再进行变量内容的插入。设置为N或N进行evaln,其它为eval,未定义不进行eval。
340 # @ STRFMT_TRANS:对fmt字符串进行\转义符的输出。
341 # @ strfmtset:设置eval、evaln、trans参数。
344 # syntax: evaln <var-name>
345 # fdesc: 函数将指定var中的变量expanssion。字符串中的var在字符串expanssion后与其它字符串连接,
346 # 会使var的名称字符串不同,使var在expanssion时异常。须在字符串中以${var}的格式使用。
348 # var-name: eval的字符串变量名称。如果为_FMT或_fmt的sfx,expanssion的字符串保存到不含
349 # sfx的变量中。如果变量不含sfx,在stdout输出字符串。
350 # e: 只对变量字符串进行转义符解析。该参数以vname@e的形式使用。
351 # E: 对变量及其包含的变量中的转义符进行解析。
363 if [[ "$evstrn_tmp" =~ "@
" ]]; then
364 evstrn_e=${evstrn_tmp##*@}
365 evstrn_tmp=${evstrn_tmp%%@*}
368 if [[ ${evstrn_tmp} =~ "_FMT
" || ${evstrn_tmp} =~ "_fmt
" ]]; then
369 evstrn_vname=${evstrn_tmp%%_*}
370 declare ${evstrn_vname}="${!evstrn_tmp}"
373 evstrn_vname="$evstrn_tmp"
377 if [[ "$evstrn_e" == E ]]; then
379 elif [[ "$evstrn_e" == e ]]; then
380 declare ${evstrn_vname}="`echo -ne \"${!evstrn_vname}\"`"
386 for (( evstrn_i=0; evstrn_i < evstrn_cnt; evstrn_i++ )); do
387 eval ${evstrn_vname}="$(echo -n${evstrn_e} "\"${!evstrn_vname}\"")"
388 [[ ! "${!evstrn_vname}" =~ "\$
" ]] && break
391 [[ "$evstrn_tmp" == 1 ]] && echo -n${evstrn_e} "${!evstrn_vname}"
403 # fsyntax: strfmt <str-var>
404 # fdesc: $1格式解析后输出到$2。不指定第二个参数时,或第二个参数为-时,输出到stdout
412 # eval vname="\
${!${1}*}"
415 [[ -z $var ]] && return
418 if [[ ${var:0:1} == "@
" ]]; then
419 if [[ ${var:1:1} == "(" ]]; then
420 # TBD: check '@', and check the attr after it.
424 elif [[ ${var:1:1} == "{" ]]; then
429 STRFMT_PARAM=( $var )
432 if [[ ${var:0:1} == ':' ]]; then
433 if [[ ${var:1:1} == ':' ]]; then
436 var="${STRFMT_DOMAIN}_
${var}"
438 # ":",被dup的section作用域
439 # :使用定义类型的domain,当前domain之后使用。
441 var="${STRFMT_DOMAIN}${var}"
443 elif [[ ${var:0:1} == '!' ]]; then
448 # 不使用:,不添加domain,以普通环境变量使用。
455 if [[ ${var} =~ "[" ]]; then
457 array="[${array%%]*}]"
458 [[ "$array" == "[*]" || "$array" == "[@
]" ]] && array=""
461 ## vname="${var}${array}"
463 if [[ -z $array ]]; then
464 eval cnt=\$\(\( \${\#${var}[@]} - 1 \)\)
470 # echo -n "STRFMT_PARAM
=${STRFMT_PARAM[@]}"
471 if [[ ! "$var" =~ [^0-9] ]]; then
472 # 如果为数字,表示@{var paramlist}中的参数id。
473 # 格式字符串使用时的函数参数$1不使用,而@{1}用于格式字符串的参数
474 if [[ -n ${STRFMT_PARAM[${var}]} ]]; then
475 # echo "strfmt \
${STRFMT_PARAM[${var}]}=${STRFMT_PARAM[${var}]}"
476 echo -n${STRFMT_TRANS} "${STRFMT_PARAM[${var}]}"
479 echo -ne "warn: paramter ${var} is not valid.\n" >&2
485 # 参数指定的变量xxx已定义,或xxx未定义,但xxx[]数组有定义,且只包含一个元素
486 if [[ -v "${var}" ||
$cnt -ge 0 ]]; then
487 # todo:这里需要对输入、输出的变量进行设置。
489 if [[ ${var} =~
"_FMT" ]]; then
492 if [[ $cnt -ge 1 ]]; then
493 # 不指定数组idx,变量为数组,XXX_FMT[@],输出到XXX[@]
494 for ((; cnt
>=0; cnt--
)); do
495 ## echo "${var}[$cnt]=\${${var}_FMT[$cnt]}"
496 declare -g ${var}[$cnt]="$(strfmt_p2p < <(eval echo \"\${${var}_FMT[$cnt]}a
\"))"
497 eval ${var}[$cnt]="\${${var}[$cnt]:0:-1}"
500 # 指定idx的单个数组的元素,XXX_FMT[$i],输出到XXX[$i]
501 # 非数组的元素,XXX_FMT,输出到XXX,即XXX_FMT[0],输出到XXX[0]
502 if [[ -z $array ]]; then
503 eval array
="[\${!${var}_FMT[@]}]"
505 ## echo "${var}$array=\${${var}_FMT$array}"
506 declare -g ${var}$array="$(strfmt_p2p < <(eval echo \"\${${var}_FMT$array}a
\"))"
507 eval ${var}$array="\${${var}$array:0:-1}"
509 elif [[ ${var} =~
"_fmt" ]]; then
512 if [[ $cnt -ge 1 ]]; then
513 # 不指定数组idx,变量为数组,XXX_FMT[@],输出到XXX[@]
514 for ((; cnt
>=0; cnt--
)); do
515 ## echo "${var}[$cnt]=\${${var}_fmt[$cnt]}"
516 declare -g ${var}[$cnt]="$(strfmt_p2p < <(eval echo \"\${${var}_fmt[$cnt]}a
\"))"
517 eval ${var}[$cnt]="\${${var}[$cnt]:0:-1}"
520 # 指定idx的单个数组的元素,XXX_FMT[$i],输出到XXX[$i]
521 # 非数组的元素,XXX_FMT,输出到XXX,即XXX_FMT[0],输出到XXX[0]
522 if [[ -z $array ]]; then
523 eval array
="[\${!${var}_fmt[@]}]"
525 ## echo "${var}$array=\${${var}_fmt[$cnt]}"
526 declare -g ${var}$array="$(strfmt_p2p < <(eval echo \"\${${var}_fmt$array}a
\"))"
527 eval ${var}$array="\${${var}${array}:0:-1}"
529 elif [[ -n ${array} ]]; then
531 # 指定idx的单个数组的元素,XXX[$i],输出到XXX[0]
532 # xxx: 对于XXX[0],输出到XXX[0],但实际不应这么使用。
533 ## echo "${var}[$array]=\${${var}$array}"
534 declare -g ${var}[0]="$(strfmt_p2p < <(eval echo \"\${${var}${array}}a
\"))"
535 eval ${var}[0]="\${${var}[0]:0:-1}"
537 if [[ ${cnt} == 1 ]]; then
539 if [[ -v ${var}[0] ]]; then
540 # 其中一个idx为0,使用非0的idx值。
543 eval array
=( \
${#$var[@]} )
544 array
="[${array[1]}]"
545 ## echo "${var}[0]=\${${var}$array}"
546 declare -g ${var}[0]="$(strfmt_p2p < <(eval echo \"\${${var}${array}}a
\"))"
547 eval ${var}[0]="\${${var}[0]:0:-1}"
550 ### echo "an array var '$var' without index or _fmt or _FMT surfix cannot be used."
552 eval echo "\${${var}[@]}"
554 elif [[ ${cnt} -gt 1 ]]; then
556 ### echo "an array var '$var' without index or _fmt or _FMT surfix cannot be used."
558 eval echo "\${${var}[@]}"
561 # 非数组的元素,XXX,输出到stdout
562 eval array
="[\${!${var}[@]}]"
563 if [[ $array == "[0]" ]]; then
564 ## echo "${var} ==> stdout"
565 strfmt_p2p
< <(echo "${!var}")
567 ## echo "${var}[$array]=\${${var}$array}"
568 # xxx: 这里末尾的字符不需要添加,调试用
569 declare -g ${var}[0]="$(strfmt_p2p < <(eval echo \"\${${var}${array}}a
\"))"
570 eval ${var}[0]="\${${var}[0]:0:-1}"
574 elif [[ "$(readlink /proc/self/fd/0)" =~
'pipe:[' ]]; then
577 echo "no input data or no variable specified."
585 # fsyntax: strfmt_set_domain <domain-str>
586 # fdesc: 设置作用域。作用域字符串以imi和section名称组合。
592 [[ -z $imi ]] && STRFMT_DOMAIN
="" && return
595 STRFMT_DOMAIN
="${imi}__${section}_"
596 # echo "strfmt_set_domain($STRFMT_DOMAIN)"
600 # fsyntax: strfmtset <str-var>
601 # fdesc: 设置eval、evaln、trans参数。
604 if [[ "$2" =~
"en" ]]; then
616 elif [[ "$2" =~
"dis" ]]; then
632 # $ if [[ "<cataid>" =~ \<([A-Za-z0-9_ ]+)\> ]]; then echo xxxxxxxx ${BASH_REMATCH[@]} xxxxxxxx; fi
633 # $ abc='\<([[:alnum:]]*)\>'; if [[ "<cataid>" =~ $abc ]]; then echo xxxxxxxx ${BASH_REMATCH[@]} xxxxxxxx; fi
635 # $ abc='\<([[:alnum:]]*)\>'; if [[ "<cataid> [type] name \"desc\" [ftype]" =~ \<$abc\>[\ \t]+\[([[:alnum:]]+)*\][\ \t]+([[:alnum:]]+)[\ \t]+\"([[:alnum:]]*)\"[\ \t]+\[([[:alnum:]]*)\].* ]]; then echo xxxxxxxx ${BASH_REMATCH[@]} xxxxxxxx; fi
636 # xxxxxxxx <cataid> [type] name "desc" [ftype] cataid type name desc ftype xxxxxxxx
637 # $ abc='([[:alnum:]]*)'; if [[ "<cataid> [type] name \"desc\" [ftype]" =~ \<$abc\>[\ \t]+\[$abc\][\ \t]+$abc[\ \t]+\"$abc\"[\ \t]+\[$abc\].* ]]; then echo match[0]=${BASH_REMATCH[0]}; echo match[1]=${BASH_REMATCH[1]}; echo match[2]=${BASH_REMATCH[2]}; echo match[3]=${BASH_REMATCH[3]}; echo match[4]=${BASH_REMATCH[4]}; echo match[5]=${BASH_REMATCH[5]}; fi
638 # match[0]=<cataid> [type] name "desc" [ftype]
645 # $ abc='([[:alnum:]]*)+'; fmt="((\<[[:alnum:]]*)\>[\ \t]*)*"; echo fmt="\"$fmt\""; if [[ "<cataid> <type> <name> <desc> [ftype]" =~ $fmt ]]; then echo match[0]=${BASH_REMATCH[0]}; echo match[1]=${BASH_REMATCH[1]}; echo match[2]=${BASH_REMATCH[2]}; echo match[3]=${BASH_REMATCH[3]}; echo match[4]=${BASH_REMATCH[4]}; echo match[5]=${BASH_REMATCH[5]}; fi
649 # fsyntax: str2var_regexpr <str-var> <fmt-var> <attr-var>
650 # recogonize string with reg-expr.
651 regex_str_dispatch
()
655 if [[ "${1}" =~
$2 ]]; then
656 for ((i
=0; i
<${#BASH_REMATCH[@]}; i
++)); do
657 attr_create
${3}::BASH_REMATCH_${i} = "${BASH_REMATCH[$i]}"
662 # use string format defined for strfmt.
663 # strrvsfmt "<cataid1
> <type1
> <name1
> <desc1
> <ftype1
>" "<@
{cataid
}>\\\
<@
{type}>\\\
<@
{name
}>\\\
<@
{desc
}>\\\
<@
{ftype
}>" var
669 local var_fmt="([[:alnum
:]_
]+)"
672 # dispatch fmt str, and get the var-name list.
673 # echo "aaa
=${aaa//@/$'\n'}"
674 # echo "${aaa//@/$'\n'}" | while read data; do echo data=${data}; if [[ "$data" =~ .*\{([[:alnum:]]+)\}.
* ]]; then echo MATCH
=${BASH_REMATCH[1]}; fi; done
677 if [[ "$data" =~ ^\
{([[:alnum
:]_
]+)\
} ]]; then
678 # echo "vnamelist[$i]=${BASH_REMATCH[1]}"
679 # echo "data=$var_fmt${data#*\}}"
680 fmt+="$var_fmt${data#*\}}"
682 vnamelist
[$i]="${BASH_REMATCH[1]}"
687 done < <(echo "${2//@/$'\n'}")
689 # reg-expr matching, and fill to the corresponding var under the domain of attr-var paramter given.
690 # try this example string if there is some problem.
691 #[[ "<cataid1> <type1> <name1> <desc1> <ftype1>" =~ \<([[:alnum:]_]+)\>\ \<([[:alnum:]_]+)\>\ \<([[:alnum:]_]+)\>\ \<([[:alnum:]_]+)\>\ \<([[:alnum:]_]+)\> ]] && echo XXXXXXXXXXXXXXXXXXXX
692 if [[ "${1}" =~
$fmt ]]; then
693 [[ $
(( ${#vnamelist[@]} + 1 )) != ${#BASH_REMATCH[@]} ]] && return
694 for ((i
=1; i
<${#BASH_REMATCH[@]}; i
++)); do
695 echo ${3}::${vnamelist[$(($i - 1))]} = "${BASH_REMATCH[$i]}"
696 # attr_create ${3}::${vnamelist[$i]} = "${BASH_REMATCH[1]}"
704 # fsyntax: templete <tmpl-file> [output-file]
705 # fdesc: 对templete-file进行var-content-insert。
710 if [[ -z $output ||
$output == "-" ]]; then
713 cat $1 | strfmt_p2p |
$output
721 echo XXXXXXXXXXXXXXXXXXXXXXXXXX
725 # use reloadshlib strfmt.shlib to load this lib, and open this invoking to debug
726 # code in tesing mode.
730 ######################
732 ######################