20230322
[shlib.git] / shlib / strfmt.shlib
blob1d636f8531cb954f1f0d3d6e417a440acac415e1
1 #!/bin/bash
2 ############################################################
3 # source: strfmt.shlib
4 # author: devenkong(18151155@qq.com)
5 # date: 2022-04-30
6 ############################################################
7 # note:
8 # strfmt函数是字符串中@{}变量的expanssion。不同于${}环境变量的
9 # expanssiin的是,@{}的expanssion输出到pipeline,且可以嵌套使用,
10 # 且只输出到pipeline。与strfmt类似的函数evaln实现的是字符串的多重
11 # eval,但是eval操作时,是将${}环境变量的内容expanssion到命令行,
12 # 多次expansion的方法。这在环境变量内包含的字符串内容较长,且变量嵌套
13 # 层数较多时,包含字符串的重复操作。
14 # 字符串进行@{}的解析之外,还可设置eval进行${}的expanssion,
15 # 和\特殊功能屏蔽符解析,以及使用trans进行\转义符的解析。
16 # strfmt函数的命令行程序为evstrn,将strfmt字符串的三种形式以参数
17 # 进行设置。
18 # strfmt的常用的用途,可用于templete文件中插入一些填充的信息,
19 # 或是应用中定义格式字符串,在使用时调用strfmt,根据环境变量内容,输出
20 # 不同运行环境下的字符串内容。例如系统中不同的软件的安装路径不同,使用
21 # strfmt定义的字符串,根据软件包名称等信息,输出一个软件包的安装路径。
22 # paths.shlib中定义的格式化字符串即是这个用途。
23 ############################################################
24 # date: 2022-10-03
25 ############################################################
26 # note:
27 # strfmt函数中的变量操作和作用域的功能特性,抽象到attr.shlib
28 # 库文件中。所以需要将代码中对应的功能进行调整。
29 # 属性的表示,以__替换::,用于体现变量的层次关系。原有代码中未将
30 # 属性的代码以一个功能模块抽象成一组函数。
31 ############################################################
34 # 打开脚本中使用alias功能,否则脚本中使用alias无效,而在命令行才有效。
35 shopt -s expand_aliases
37 # todo:
38 # @ strfmt解析的var,添加domain。${var domain}
39 # @
43 #include gplib.shlib
45 . shlibinc
47 include stdio.shlib
50 ######################
51 # section: 公用注释信息
52 ######################
56 ######################
57 # section: 变量定义
58 ######################
61 STRFMT_DOMAIN=
62 STRFMT_PARAM=
64 export embcnt=0
67 ######################
68 # section: private函数
69 ######################
73 ######################
74 # section: public函数
75 ######################
77 #####################################################################
78 # [字符串操作函数]
79 #####################################################################
83 # @ 多行字符串的fmt,输出通常为pipe或file,输出到var,通常使用evaln。
84 # # 字符串处理中,以pipe输出,逐行处理时,以var+=或var+=( $content )保存到环境变量。
85 # # 输出到file,实际以pipe输出的重定向来实现。所以strfmt的输出,都以stdout为输出。
87 # @ 输入包括var、pipe、file。
88 # # 以var为输入,用于环境变量定义的格式化字符串。
89 # # 以pipe为输入,通常输入的字符串内容较长。可以和其它程序以pipe连接,形成字符串处理链。
90 # # 以file为输入,输入的字符串内容较长,且以文件的形式存储。
91 # @ 无论是哪种输入,都转换为str的逐行处理,所以定义一个单行处理的alias命令行。
92 # # 以参数var为输入,stdout为输出。
96 # 环境变量的expanssion使用evaln进行多重eval,
97 # 多行字符串和pipe字符串的expanssion使用strfmt。
98 # 文本文件的expansion使用tmpl调用strfmt linedata,
99 # linedata存放单行字符串。
104 # 字符串包含的功能:
105 # @ eval,进行$的expanssion和\特殊字符的屏蔽
106 # @ evaln,n次eval
107 # @ -e,与echo中的-e参数一样,对字符串中\转义符的解析
108 # @ fmt,对字符串中&var&的内容进行插入
109 # @
111 # @ evaln:字符串的evaln,变量之后的@e只对参数变量进行\转义符解析,@E对字符串中的变量进行转义符的循环解析。
112 # @ strfmt:只进行&var&变量的插入。参数为var_fmt或VAR_FMT或var[1]时,输出到$var变量,其它输出到stdout。
113 # @ STRFMT_EVALN:对于fmt字符串进行eval输出,再进行变量内容的插入。设置为N或N进行evaln,其它为eval,未定义不进行eval。
114 # @ STRFMT_TRANS:对fmt字符串进行\转义符的输出。
115 # @ strfmtset:设置eval、evaln、trans参数。
118 # syntax: evaln <var-name>
119 # fdesc: 函数将指定var中的变量expanssion。字符串中的var在字符串expanssion后与其它字符串连接,
120 # 会使var的名称字符串不同,使var在expanssion时异常。须在字符串中以${var}的格式使用。
121 # fparam:
122 # var-name: eval的字符串变量名称。如果为_FMT或_fmt的sfx,expanssion的字符串保存到不含
123 # sfx的变量中。如果变量不含sfx,在stdout输出字符串。
124 # e: 只对变量字符串进行转义符解析。该参数以vname@e的形式使用。
125 # E: 对变量及其包含的变量中的转义符进行解析。
127 alias evaln='
129 local evstrn_i
130 local evstrn_cnt=100
131 local evstrn_tmp
132 local evstrn_vname=
133 local evstrn_e=""
135 read -t 1 evstrn_tmp
137 if [[ "$evstrn_tmp" =~ "@" ]]; then
138 evstrn_e=${evstrn_tmp##*@}
139 evstrn_tmp=${evstrn_tmp%%@*}
142 if [[ ${evstrn_tmp} =~ "_FMT" || ${evstrn_tmp} =~ "_fmt" ]]; then
143 evstrn_vname=${evstrn_tmp%%_*}
144 declare ${evstrn_vname}="${!evstrn_tmp}"
145 evstrn_tmp=0
146 else
147 evstrn_vname="$evstrn_tmp"
148 evstrn_tmp=1
151 if [[ "$evstrn_e" == E ]]; then
152 evstrn_e=e
153 elif [[ "$evstrn_e" == e ]]; then
154 declare ${evstrn_vname}="`echo -ne \"${!evstrn_vname}\"`"
155 evstrn_e=""
156 else
157 evstrn_e=""
160 for (( evstrn_i=0; evstrn_i < evstrn_cnt; evstrn_i++ )); do
161 eval ${evstrn_vname}="$(echo -n${evstrn_e} "\"${!evstrn_vname}\"")"
162 [[ ! "${!evstrn_vname}" =~ "\$" ]] && break
163 done
165 [[ "$evstrn_tmp" == 1 ]] && echo -n${evstrn_e} "${!evstrn_vname}"
167 unset evstrn_i
168 unset evstrn_cnt
169 unset evstrn_tmp
170 unset evstrn_vname
171 unset evstrn_e
172 } <<< '
175 # @
176 # # _fmt/_FMT sfx or not.
177 # # normal var or single elem array or multi elem array.
178 # # index value specified or not.
179 # # if it is an array, output is xxx[0], or, output is xxx.
180 # the difference is array attribute.
182 # @ not array
183 # xxx ==> stdout.
184 # xxx_fmt/XXX_FMT ==> xxx/XXX. (equal to xxx[0]/XXX[0])
185 # @ one elem array, and index is not 0
186 # xxx ==> xxx[0].
187 # xxx[$i] ==> xxx[0].
188 # xxx_fmt ==> xxx[0].
189 # xxx_fmt[$i]/XXX_FMT[$i] ==> xxx[0]/XXX[0].
190 # @ multi elem array
191 # xxx/xxx[@] ==> stdout.
192 # xxx_fmt[@]/XXX_FMT[@] ==> xxx[@]/XXX[@].
193 # xxx_fmt/XXX_FMT ==> xxx[@]/XXX[@]. (auto detected as xxx_fmt[@]/XXX_FMT[@])
194 # xxx[$i]/XXX[$i] ==> stdout.
195 # xxx_fmt[$i]/XXX_FMT[$i] ==> xxx[0]/XXX[0].
197 # @ three kind of output
198 # # stdio: no _fmt/_FMT sfx, not an none 0 indexed single array, or not an multi array.
199 # # xxx[0]: default output var, it's equal to xxx.
200 # # xxx[@]: only multi elem array with _fmt/_FMT sfx use this output.
204 # variable with '@' is a attr, without '@' is a env var, or use attr to use env var.
210 # fsyntax: strfmt_var_name_check <str>
211 # fdesc: check file name of variable.
213 strfmt_var_name_check ()
215 local vname=$1
216 local vout=
217 local idx=
218 local idxp=
219 local idxout=
220 local sfx=
222 local param=
224 local i=
225 local cnt=
226 local array=
228 # declare -p vname
230 if [[ $vname =~ ^@[{]([^\}]*)[\}]$ ]]; then
231 vname="${BASH_REMATCH[1]}"
232 # declare -p vname
233 param=( $vname )
234 unset param[0]
236 vname="$(attr_get_vname $vname)"
237 # TBD: define paramter variable.
238 else
239 if [[ $vname =~ @([[:alnum:]_:\.-=\>\[\@\*]*) ]]; then
240 vname="${BASH_REMATCH[1]}"
241 # declare -p vname
242 vname="$(attr_get_vname $vname)"
243 else
244 # set +x
245 # err "paramter of variable name is not valid.\n"
246 return 1
249 # echo -ne "$vname="
250 # attr_get $vname
252 # get idx value.
253 idxp=0
254 if [[ $vname =~ \[([[:alnum:]:.-=>]*)\] ]]; then
255 idxp=${BASH_REMATCH[1]}
256 vname=${vname%%[*}
259 # cut _fmt/_FMT sfx, get vname.
260 if [[ $vname =~ (_fmt)$|(_FMT)$ ]]; then
261 sfx=${BASH_REMATCH}
262 eval vname=\${vname%${BASH_REMATCH}}
264 # default vout setting is vname, or set "-stdout".
265 # set -x
266 eval cnt=\${#${vname}${sfx}[@]}
267 vout=$vname
268 if [[ -z $sfx ]]; then
269 if [[ $cnt == 1 ]]; then
270 eval idx=\${!${vname}${sfx}[@]}
271 [[ $idxp != $idx ]] && return 0 # err "specified idx in paramter is not valid.\n"
272 if [[ $idx == 0 ]]; then
273 vout="-stdout"
276 if [[ $cnt == 2 ]]; then
277 eval idx=\${!${vname}${sfx}[@]}
278 if [[ ${idx:0:1} == 0 ]]; then
279 idx="${idx#0\ }"
280 cnt=1
281 idxout="[0]"
282 else
283 vout="-stdout"
286 if [[ $cnt -gt 2 ]]; then
287 eval idx=\${!${vname}${sfx}[@]}
288 vout="-stdout"
290 else
291 eval idx=\${!${vname}${sfx}[@]}
292 idxout="[${idx%%\ *}]"
293 [[ $idxout == "[0]" ]] && idxout=""
294 [[ $cnt == 1 && $idxp != $idx ]] && return 0 # err "specified idx in paramter is not valid.\n"
296 # set +x
298 # eval idx=\${!${vname}${sfx}[@]}
299 # declare -p vname idx sfx vout idxout cnt
301 [[ "$idx" =~ [^0-9\ ] ]] && opt="-A"
303 for i in $idx; do
304 idx="[$i]"
305 [[ $i != 0 ]] && idxout="$idxout"
306 if [[ $vout == '-stdout' ]]; then
307 strfmt_pipe <<< "${!vname}"
308 else
309 # dbgoutd "$vout$idxout=\$(strfmt_pipe <<< \"@{$vname${sfx}$idx}\")\n"
310 tmp="$(strfmt_pipe <<< "@{$vname${sfx}$idx}")"
311 # dbgoutd "tmp=$tmp\n"
312 # set -x
313 declare -g $opt $vout$idxout="$tmp"
314 # declare -p $vout
315 # set +x
317 done
319 return 0
322 # index number for '@' var name processing.
323 strfmt_idx=0
324 strfmt_data=""
325 strfmt_len=0
327 strfmt_nest=0
329 # store the dispatch result
330 ##strfmt_type= # env/func/vfunc
331 #strfmt_vname=
332 #strfmt_vfunc=
333 #strfmt_param=
335 display_var ()
337 local vdata=
338 local vidx=
339 local vlen=
341 local vtype=
342 local vname=
343 local vfunc=
344 local vparam=
346 vdata=strfmt_${strfmt_nest}_data
347 vidx=strfmt_${strfmt_nest}_idx
348 vlen=strfmt_${strfmt_nest}_len
350 vtype=strfmt_${strfmt_nest}_vtype
351 vname=strfmt_${strfmt_nest}_vname
352 vfunc=strfmt_${strfmt_nest}_vfunc
353 vparam=strfmt_${strfmt_nest}_param
355 echo xxxxxxxxxxxxxxxxxxxxxxx
356 declare -p ${vdata}
357 declare -p ${vidx}
358 declare -p ${vlen}
359 echo xxxxxxxxxxxxxxxxxxxxxxx
360 declare -p ${vname}
361 declare -p ${vdata}
362 declare -p ${vidx}
363 declare -p ${vlen}
364 echo xxxxxxxxxxxxxxxxxxxxxxx
367 # matching '@' prefix, and get the variable name string len.
368 strfmt_get_var_name_str ()
370 local vdata=
371 local vidx=
372 local vlen=
374 local vtype=
375 local vname=
376 local vfunc=
377 local vparam=
379 local tmp=
380 local syntax=
381 local quote=
383 vdata=strfmt_${strfmt_nest}_data
384 vidx=strfmt_${strfmt_nest}_idx
385 vlen=strfmt_${strfmt_nest}_len
387 # if no @var, string dispaly, update idx value.
388 eval tmp="\"\${$vdata:${!vidx}}\""
389 if [[ ! ${tmp} =~ @ ]]; then
390 echo -ne "${tmp}"
391 eval ${vidx}=${!vlen}
392 return
395 vtype=strfmt_${strfmt_nest}_vtype
396 vname=strfmt_${strfmt_nest}_vname
397 vfunc=strfmt_${strfmt_nest}_vfunc
398 vparam=strfmt_${strfmt_nest}_param
400 # display_var
403 # variable beginning matching.
405 if [[ "$tmp" =~ [^\\]?(@[{\(]?) ]]; then
406 tmp="${tmp%%@*}"
408 # it means there is a '\' before '@'
409 # update $vidx, and return for next processing.
410 if [[ -n $tmp && "${tmp: -1}" == '\' ]]; then
411 eval $vidx=$(( $vidx + ${#tmp} + 1 ))
413 # TBD: the idx value is not correct
414 tmp="${tmp:0: -2}@"
415 echo -ne "$tmp"
416 return
419 # display the prevous string before @var.
420 echo -ne "$tmp"
421 case ${BASH_REMATCH[1]} in
422 '@' )
423 eval $vidx=$(( $vidx + ${#tmp} + 1 ))
424 eval tmp="\"\${$vdata:${!vidx}}\""
425 syntax=""
427 'aaa' )
428 # nest @var process.
429 if [[ "${tmp}" =~ [^\\]?@[{\(]? ]]; then
430 strfmt_get_var_name_str
431 # variable process.
434 '@{' )
435 eval $vidx=$(( $vidx + ${#tmp} + 2 ))
436 eval tmp="\"\${$vdata:${!vidx}}\""
437 syntax="[^\\]?}"
438 quote='\}'
440 '@(' )
441 eval $vidx=$(( $vidx + ${#tmp} + 2 ))
442 eval tmp="\"\${$vdata:${!vidx}}\""
443 syntax="[^\]?)"
444 quote=')'
445 # echo xxxxxxxxxxxxxxxx
449 esac
452 # variable ending matching.
454 # eval tmp="\"\${tmp:${!vidx}}\""
455 if [[ -n $syntax && "$tmp" =~ $syntax ]]; then
456 eval tmp="\"\${tmp%%${quote}*}\""
457 # echo tmp="$tmp"
458 eval $vidx=$(( $vidx + ${#tmp} + 1 ))
459 else
460 tmp="${tmp%%[^[:alnum:]_:.=>]*}"
461 eval $vidx=$(( $vidx + ${#tmp} ))
463 eval $vname="\"${tmp}\""
464 else
465 # no variable, string dispaly.
466 # should not be running here.
467 echo -ne "$tmp"
468 eval ${vidx}=${!vlen}
469 return
472 tmp="${!vname}"
473 tmp=${tmp%% *}
474 # variable display.
475 if [[ "$quote" != ')' ]]; then
476 # eval $vtype=attr
477 # dbgoutd "${FCGREEN}@{${!vname}}${CNORMAL}\n"
478 attr_get ${!vname} | strfmt_pipe
479 return
482 dbgoutd "$tmp($(attr_type $tmp))\n"
484 # check executable type
485 # @() processing. recogonize cmd type.
486 case $(attr_type "$tmp") in
487 attr )
488 err "should not be running here.\n"
489 progexit
491 func )
492 # eval $vtype=func
493 # echo "${FCGREEN}@{${vname} $strfmt_param}${CNORMAL}"
494 attr_func ${!vname} ${!vparam} | strfmt_pipe
496 vfunc )
497 # eval $vtype=vfunc
498 # echo "${FCGREEN}@{${vfunc} $strfmt_param}${CNORMAL}"
499 attr_func $vfunc $strfmt_param | strfmt_pipe
502 # for unkown type, the default action is command running.
503 eval "${!vname}" | strfmt_pipe
504 # eval $vtype=unkown
506 esac
510 # this code matching @{var} name by regex
512 foo ()
514 if [[ "${tmp}" =~ [^\\]?@([[:alnum:]_:.=>]*) ]]; then
515 # if [[ "${tmp}" =~ '@\(([^\}]*)\)' ]]; then
516 eval $vidx=$(( $vidx + ${#BASH_REMATCH} ))
517 # eval $vlen=${#BASH_REMATCH}
518 eval $vname="${BASH_REMATCH:1}"
519 elif [[ "${tmp}" =~ [^\\]?@\{([^}]*)\} ]]; then
520 tmp="${tmp%%@*}"
521 eval $vidx=$(( $vidx + ${#tmp} ))
522 # $vlen=$(( ${#BASH_REMATCH} + 3 ))
523 return;
524 elif [[ "${tmp}" =~ [^\\]?@\(([^\)]*)\) ]]; then
525 tmp="${tmp%%@*}"
526 eval $vidx=$(( $vidx + ${#tmp} ))
527 # $vlen=$(( ${#BASH_REMATCH} + 3 ))
528 return;
529 else
530 eval $vidx=$(( $vidx + ${#tmp} ))
531 return
536 # fsyntax: strfmt_pipe
537 # fdesc:
538 # $1格式解析后输出到$2。不指定第二个参数时,或第二个参数为-时,输出到stdout
539 strfmt_pipe ()
541 local vlen=
542 local vidx=
543 local vdata=
545 local vtype=
546 local vname=
547 local vfunc=
548 local vparam=
550 local tmp=
551 local flag=
553 : $((strfmt_nest++))
555 # dbgoutd "strfmt_nest=$strfmt_nest\n"
556 vlen=strfmt_${strfmt_nest}_len
557 vidx=strfmt_${strfmt_nest}_idx
558 vdata=strfmt_${strfmt_nest}_data
560 vtype=strfmt_${strfmt_nest}_vtype
561 vname=strfmt_${strfmt_nest}_vname
562 vfunc=strfmt_${strfmt_nest}_vfunc
563 vparam=strfmt_${strfmt_nest}_param
565 declare -g $vlen=0
566 declare -g $vidx=0
567 declare -g $vdata=""
569 declare -g $vtype=""
570 declare -g $vname=""
571 declare -g $vfunc=""
572 declare -g $vparam=""
574 flag=0
575 # set IFS paramter, or, the previous blanks in a line can not be readed.
576 OLD_IFS=$IFS
577 IFS=$'\n'
578 while read -r $vdata; do
579 # when blank line readed.
580 [[ -z ${!vdata} ]] && echo && continue;
582 eval declare -g $vlen="\${#${vdata}}"
583 declare -g $vidx=0
585 # newline char appending begin from the second line data in a variable.
586 if [[ $flag != 0 ]]; then
587 echo
588 else
589 flag=1
592 # get @{var} in string, and output variable content.
593 while [[ ${!vidx} -lt ${!vlen} ]]; do
594 strfmt_get_var_name_str
595 done
596 done
597 IFS=$OLD_IFS
599 : $((strfmt_nest--))
603 # fsyntax: strfmt <str-var>
604 # fdesc: output the formated string in paramter $1. all variable used in the string
605 # should be initialized. if those variables defined in a attr var, invoke attr_domain()
606 # to set the domain first.
607 strfmt ()
609 local vout=
611 # dbgoutd "strfmt $1\n"
614 if [[ $# != 0 ]]; then
615 if [[ $# == 1 && ${1} =~ ([[:alnum:]_]*)(_evl|_EVL)$ ]]; then
616 if [[ ${1:0:1} == '@' || "$1" =~ :: ]]; then
617 attr_declare ${BASH_REMATCH[1]}="$(echo "${1}")"
618 else
619 declare -g ${BASH_REMATCH[1]}="$(echo "${1}")"
621 else
622 strfmt_var_name_check $1
623 [[ $? == 0 ]] && return
625 # XXX: it seems can not running to here.
626 strfmt_pipe <<< "$@"
628 # if [[ $# == 1 && ${1} =~ (_fmt)$|(_FMT)$ ]]; then
629 # else
630 # strfmt_pipe <<< "$@"
631 # fi
632 elif [[ "$(readlink /proc/self/fd/0)" =~ 'pipe:[' ]]; then
633 strfmt_pipe
634 else
640 # fsyntax: strfmt_set_domain <domain-str>
641 # fdesc: 设置作用域。作用域字符串以imi和section名称组合。
642 strfmt_set_domain ()
644 local imi="$1"
645 local section=
647 [[ -z $imi ]] && STRFMT_DOMAIN="" && return
648 section="${imi##*:}"
649 imi="${imi%%:*}"
650 STRFMT_DOMAIN="${imi}__${section}_"
651 # echo "strfmt_set_domain($STRFMT_DOMAIN)"
655 # fsyntax: strfmtset <str-var>
656 # fdesc: 设置eval、evaln、trans参数。
657 strfmtset ()
659 if [[ "$2" =~ "en" ]]; then
660 case $1 in
661 eval )
662 STRFMT_EVALN=eval
664 evaln )
665 STRFMT_EVALN=evaln
667 trans )
668 STRFMT_TRANS=e
670 esac
671 elif [[ "$2" =~ "dis" ]]; then
672 case $1 in
673 eval )
674 STRFMT_EVALN=""
676 evaln )
677 STRFMT_EVALN=""
679 trans )
680 STRFMT_TRANS=""
682 esac
688 # fsyntax: strtrunc <str> <char>
689 # fdesc: 设置作用域。作用域字符串以imi和section名称组合。
690 strtrunc ()
692 [[ -z ${!1} || -z $2 ]] && return
694 if [[ "${!1}" =~ ^([^$2]*)$2(.*) ]]; then
695 eval ${1}[1]="\${BASH_REMATCH[1]}"
696 eval ${1}[2]="\${BASH_REMATCH[2]}"
701 # fsyntax: strsplit <str> <left_bracket> <right_bracket>
702 # fdesc: 设置作用域。作用域字符串以imi和section名称组合。
703 strsplit ()
705 [[ -z ${!1} || -z $2 ]] && return
707 if eval [[ "${STR}" =~ "^([^\\$U]*)\\$U([^\\$V]*)\\$V(.*)" ]]; then
708 eval ${1}[1]="\${BASH_REMATCH[1]}"
709 eval ${1}[2]="\${BASH_REMATCH[2]}"
710 eval ${1}[3]="\${BASH_REMATCH[3]}"
715 # fsyntax: strstrip <str>
716 # fdesc: delete blanks at the beginning and ending position.
718 strstrip ()
720 sed -e "s/^[[:blank:]]*//g; s/[[:blank:]]*$//g" <<< "$1"
723 # $ if [[ "<cataid>" =~ \<([A-Za-z0-9_ ]+)\> ]]; then echo xxxxxxxx ${BASH_REMATCH[@]} xxxxxxxx; fi
724 # $ abc='\<([[:alnum:]]*)\>'; if [[ "<cataid>" =~ $abc ]]; then echo xxxxxxxx ${BASH_REMATCH[@]} xxxxxxxx; fi
726 # $ abc='\<([[:alnum:]]*)\>'; if [[ "<cataid> [type] name \"desc\" [ftype]" =~ \<$abc\>[\ \t]+\[([[:alnum:]]+)*\][\ \t]+([[:alnum:]]+)[\ \t]+\"([[:alnum:]]*)\"[\ \t]+\[([[:alnum:]]*)\].* ]]; then echo xxxxxxxx ${BASH_REMATCH[@]} xxxxxxxx; fi
727 # xxxxxxxx <cataid> [type] name "desc" [ftype] cataid type name desc ftype xxxxxxxx
728 # $ 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
729 # match[0]=<cataid> [type] name "desc" [ftype]
730 # match[1]=cataid
731 # match[2]=type
732 # match[3]=name
733 # match[4]=desc
734 # match[5]=ftype
736 # $ 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
739 # fsyntax: fmt_str_to_matching_regex
740 # fdesc:
741 # input var:
742 # STR_FMT
743 # PAIRCHARS
744 # output var:
745 # VNAME_ARRAY, var defined in STR_FMT.
746 # STR_REGEX, reg-expr string for matching variable in STR_FMT.
748 fmt_str_to_matching_regex ()
750 local LKJDS_TMP_FMT=
751 local fmt=
752 # local STR_FMT=
753 local vname=
754 local vname_list=
755 local i=
756 local leftchar=
757 local rightchar=
759 # get STR_FMT
760 # STR_FMT="$(attr_get STR_FMT)"
761 [[ -z $STR_FMT ]] && err "fmt_str_to_matching_regex(): STR_FMT is null.\n" && return 0
763 if [[ -n $SEPCHAR ]]; then
765 OLD_IFS="$IFS"
766 IFS="${SEPCHAR}"
767 array=( $STR_FMT )
768 IFS="$OLD_IFS"
770 # dbgoutd "array[]=${array[@]}\n"
772 i=$(( ${#array[@]} - 1 ))
773 [[ $i -lt 0 ]] && err "fmt_str_to_matching_regex(): array matching size err.\n" && return 1
774 for ((; i>=0; i--))
776 vname="${array[$i]:2:-1}"
777 declare -g "VNAME_ARRAY[$i]=$vname"
779 if [[ -v ${vname}_REGEX ]]; then
780 eval ${vname}="\$(${vname}_REGEX}"
781 else
782 # declare -g "${vname}=\"([[:alnum:]_]*)\""
783 declare -g "${vname}=\"([^\\${SEPCHAR}*)\""
786 STR_FMT="${STR_FMT//@{$vname}/${!vname}}"
789 return 0
793 # get regex format string 'fmt', which is used for variable name matching.
795 fmt=`echo "$STR_FMT" | sed -e 's/\(.[@][{][[:alnum:]_]*[}].\)/(\1)/g'`
796 fmt=`echo "$fmt" | sed -e "s/\([[:punct:][:blank:]]\)/\\\\\\\\\1/g"`
797 fmt="$(echo "$fmt" | sed -e "s/\\\\\([\(\)\<\>]\\)/\1/g")"
798 fmt=`echo "$fmt" | sed -e 's/\\\@\\\{[[:alnum:]_]*\\\}/@\\\\\{[[:alnum:]]*}/g'`
799 #fmt=`echo "$STR_FMT" | sed -e 's/@{[[:alnum:]_]*}/(.?@\\\\\{[[:alnum:]]*}.?)/g'`
800 #if [[ "$STR_FMT" =~ $fmt ]]; then echo BASH_REMATCH=${BASH_REMATCH[@]}; fi
802 # 1.regex matching in STR_FMT to get the variable name.
803 unset VNAME_ARRAY
804 if [[ "$STR_FMT" =~ $fmt ]]; then
805 echo BASH_REMATCH=${BASH_REMATCH[@]}
807 # variable relative process
808 i="$(( ${#BASH_REMATCH[@]} - 1 ))"
809 [[ $i -le 0 ]] && err "fmt_str_to_matching_regex(): no string matching.\n" && return 1
810 VNAME_ARRAY=( "" )
811 for ((; i>0; i--))
813 vname="${BASH_REMATCH[$i]:3:-2}"
814 # dbgoutd "i=$i\n"
815 # dbgoutd "\${vname}=${vname}\n"
816 [[ -z "${vname}" ]] && continue
817 VNAME_ARRAY[$((i-1))]="${vname}"
818 # dbgoutd "\${BASH_REMATCH[$i]}=\"${BASH_REMATCH[$i]}\"\n"
819 if [[ -v ${vname}_REGEX ]]; then
820 eval ${vname}="\$(${vname}_REGEX}"
821 else
822 leftchar="${BASH_REMATCH[$i]:0:1}"
823 # use minus signature after ':', append a blank. or it
824 # will be treated as an initial expr.
825 rightchar="${BASH_REMATCH[$i]: -1:1}"
826 if [[ "${PAIRCHARS//$leftchar$rightchar/}" != "${PAIRCHARS}" ]]; then
827 #eval "${vname}=\"\[$leftchar\]([^\\$rightchar]*)\[$rightchar\]\""
828 # append three '\' before $rightchar, if it is '"', one '\' mask
829 # it's particular function, it's equal to no mask.
830 eval declare -g "${vname}=\"([^\$rightchar]*)\""
831 else
832 eval declare -g "${vname}=\"([[:alnum:]_]*)\""
834 # dbgoutd "$vname = ${leftchar} ${!vname} ${rightchar}\n"
837 # save it to correspondding domain.
838 # attr_create $1::${vname}="${!vname}"
839 #eval ${vname}="${!vname}"
841 else
842 # 2.if not match, use single var reg-expr matching.
843 fmt="${STR_FMT}"
844 while true; do
845 : $(( i++ ))
846 if [[ "$fmt" =~ [.]?@{([[:alnum:]_]*)}[.]? ]]; then
847 VNAME_ARRAY[$i]="${BASH_REMATCH[1]}"
848 str="${BASH_REMATCH[1]}_REGEX"
849 [[ ! -v $str ]] && str="${BASH_REMATCH[1]}_regex"
850 [[ ! -v $str ]] && str="GENERAL_REGEX"
852 eval fmt="\${fmt/${BASH_REMATCH[0]}/${!str}}"
853 else
854 break;
856 done
858 if [[ "$(attr_get $vname)" =~ ${fmt} ]]; then
859 if [[ "${#BASH_REMATCH[@]}" != "$i" ]]; then
860 for ((; i>0; i--)); do
861 # TBD: set variable by attr_declare, it save data with domain pfx.
862 declare -g VNAME_ARRAY[$i]="${BASH_REMATCH[$i]}"
863 done
867 # 3.if not match, use analysis loop
868 if [[ -z "${VNAME_ARRAY[@]}" ]]; then
869 BASH_REMATCH[0]=""
871 while read data; do
872 if [[ "${data:0:1}" == '@' ]]; then
873 vname=${data:2:-1}
874 BASH_REMATCH[$i]="$vname"
876 if [[ -v ${vname}_REGEX ]]; then
877 eval ${vname}="\$(${vname}_REGEX}"
879 # save it to correspondding domain.
880 VNAME_ARRAY[$i]="${vname}"
881 eval $vname="\${!vname}"
883 : $((i++))
884 else
885 rightchar=${data:0:1}
886 if [[ ! -v ${vname}_REGEX ]]; then
887 if [[ "$PAIRCHARS" =~ "${leftchar}${rightchar}" ]]; then
888 eval ${vname}="\[${leftchar}}\]([^\\${rightchar}]*)\[${rightchar}\]}"
889 else
890 eval $vname="([[:alnum:]_]*)"
893 # save it to correspondding domain.
894 VNAME_ARRAY[$i]="${vname}"
895 eval $vname="\${!vname}"
897 leftchar=${data:0:-1}
899 done <<< "$(echo "${STR_FMT}" | sed -e $'s/@/\n@/g' | sed -e $'s/}/}\n/g')"
900 # done <<< "$(echo "${STR_FMT}" | sed -e $'s/\\\(@{[[:alnum:]_]*}\\\)/\n@{\\\1\n}/g'"
904 [[ -z "${VNAME_ARRAY[@]}" ]] && return 1
906 # translate format string to regex string, store in node.
907 STR_FMT_BAK="${STR_FMT}"
909 LKJDS_TMP_FMT=`echo "$STR_FMT" | sed -e "s/\([[:punct:][:blank:]]\)/\\\\\\\\\1/g"`
910 LKJDS_TMP_FMT="$(echo "$LKJDS_TMP_FMT" | sed -e "s/\\\\\([\@\{\}\<\>]\\)/\1/g")"
911 strfmt @LKJDS_TMP_FMT
913 [[ -n "$LKJDS_TMP" ]] && declare -g STR_REGEX="${LKJDS_TMP}"
914 STR_FMT="${STR_FMT_BAK}"
915 unset LKJDS_TMP
919 # fsyntax: array_proc <str-var-name>
920 # fdesc: dispatch given string, and store it to corresponding var
921 # in VNAME_ARRAY.
923 array_proc ()
925 local string=
926 local array=
927 local i=
929 read -r string
931 OLD_IFS="$IFS"
932 IFS="${SEPCHAR}"
933 array=( $string )
934 IFS="$OLD_IFS"
936 i=$(( ${#VNAME_ARRAY[@]} - 1 ))
937 [[ $i -lt 0 ]] && err "array_proc(): array matching err.\n" && return 1
938 for ((; i>=0; i--))
940 eval declare -g "${VNAME_ARRAY[$i]}=\"\${array[${i}]}\""
944 pairchar_proc ()
950 # fsyntax: regexpr_proc <str-var-name>
951 # fdesc: matching given string by $STR_REGEX, and store it to corresponding var
952 # in VNAME_ARRAY.
954 regexpr_proc ()
956 local i=
958 if [[ -n "${STR_REGEX}" ]]; then
960 elif [[ -n "$STR_FMT" ]]; then
961 fmt_str_to_matching_regex
962 else
963 return 1
967 # example:
968 # [[ '<1> [srcdir] TestDir1 "test dir 1"' =~ \<([^\>]*)\>\ \[([^\]]*)\]\ ([[:alnum:]_]*)\ \"([^\"]*)\" ]] && echo BASH_REMATCH[@]=${BASH_REMATCH[@]}
969 # [[ <1> [srcdir] TestDir1 "test dir 1" =~ \<([^\>]*)\>\ \[([^\]]*)\]\ ([[:alnum:]_]*)\ \"([^\"]*)\" ]]
971 # [[ <1> [srcdir] TestDir1 "test dir 1" =~ <"([^>]*)">\ \["([^]]*)"\]\ ([[:alnum:]_]*)\ \""([^"]*)"\" ]]
972 # [[ <1> [srcdir] TestDir1 "test dir 1" =~ <([^>]*)> \[([^]]*)] ([[:alnum:]_]*) "([^"]*)" ]]
975 # echo "\${STR_REGEX}=${STR_REGEX}"
976 # do not use "" for matching format string.
977 if [[ "$1" =~ ${STR_REGEX} ]]; then
978 # echo matching...
979 i=$(( ${#BASH_REMATCH[@]} - 1 ))
980 [[ $i -le 0 ]] && echo xxxxxxxxxxxxxxxxxxxxxxxx && return 1
981 for ((; i>0; i--))
983 eval declare -g "${VNAME_ARRAY[$((i-1))]}=\"${BASH_REMATCH[${i}]}\""
986 return 0
989 return 1
992 output_vname_array ()
994 local size=$(( ${#VNAME_ARRAY[@]} - 1 ))
995 local tmp=
997 for ((i=0; i<=size; i++))
999 eval tmp="${VNAME_ARRAY[$i]}"
1000 echo -ne "$tmp=${!tmp}; "
1002 echo -ne "\n"
1005 METHOD_LIST="array regexpr pairchar "
1008 # this function maybe named as 'strmatch'.
1009 # it can be used to formated file name generate and recognize.
1010 # it also can be used in regular formated single line doc,
1011 # or, it can be used in tui template controller for multi-editor controller.
1015 # fsyntax: str2var <string>
1016 # fdesc: matching the string in paramter $1. it need a reg-expr string var 'STR_REGEX',
1017 # defined in environment variable. if not defined, generate it from STR_FMT with relative
1018 # variable define.
1019 # str2var() is not used for general purpose, it's a formated string matching function.
1020 # normally, the string is a file name, or formated line text, with some sperator.
1022 # input env var:
1023 # SEPCHAR, seperate char used as $IFS.
1024 # STR_REGEX, reg-expr string for matching.
1025 # STR_FMT, if STR_REGEX not exist, use it to generate STR_REGEX.
1026 # STR, if $2 is not specified, use it as input string.
1027 # VNAME_ARRAY[], variable name stored in STR_FMT. if not specified,
1028 # it will be generated from STR_FMT again.
1029 # <VARS>, save variable string matched from $STR to variable named as
1030 # ${VNAME_ARRAY[@]}.
1032 # relative var: STR_FMT, STR_REGEX, SEPCHAR, PAIRCHAR, XXX_REGEX, VNAME_ARRAY
1034 str2var ()
1036 local string=
1038 string="$(attr_get $1)"
1040 # dbgoutd "\$1=${string}\n"
1042 # dbgoutd "STR_FMT=$STR_FMT\n"
1044 # get paramters.
1045 if [[ -n $SEPCHAR ]]; then
1046 METHOD=array
1047 elif [[ -n $STR_REGEX ]]; then
1048 METHOD=regexpr
1049 elif [[ -n $STR_FMT ]]; then
1050 fmt_str_to_matching_regex
1051 METHOD=regexpr
1052 elif [[ -n $PAIRCHAR ]]; then
1053 METHOD=pairchar
1054 else
1055 return 1
1058 if [[ -z ${VNAME_ARRAY[@]} ]]; then
1059 dbgoutd "=======================\n"
1061 dbgoutd "STR_FMT=$STR_FMT\n"
1062 fmt_str_to_matching_regex
1063 # dbgoutd "VNAME_ARRAY=${VNAME_ARRAY[@]}\n"
1066 dbgoutd "\${VNAME_ARRAY[@]}=${VNAME_ARRAY[@]}\n"
1067 if [[ -n ${string} ]]; then
1068 if [[ ${1:0:1} == '@' ]]; then
1069 declare -g STR="$(attr_get $1)"
1070 elif [[ -v ${string} ]]; then
1071 declare -g STR="${!string}"
1072 else
1073 declare -g STR="${string}"
1075 elif [[ -v STR ]]; then
1077 elif [[ "$(readlink /proc/self/fd/0)" =~ 'pipe:[' ]]; then
1078 decleare -g STR=""
1080 # it only read short text string, that re-directed to str2var().
1081 # it can read multiline in a variable.
1083 OLD_IFS="$IFS"
1084 IFS="$'\x00'"
1085 read -d $'\x00' -t 0.1 -r STR
1086 IFS="$OLD_IFS"
1087 else
1088 err "string is not specified in paramter or $STR or stdin.\n"
1089 return 1
1092 # dbgoutd "NCATAID=$NCATAID\n"
1093 # dbgoutd "NTYPE=$NTYPE\n"
1094 # dbgoutd "NNAME=$NNAME\n"
1095 # dbgoutd "NDESC=$NDESC\n"
1097 # dbgoutd "STR=${STR}\n"
1098 # dbgoutd "METHOD=${METHOD}\n"
1099 # dbgoutd "=============================\n"
1101 # using process function
1102 while true; do
1103 ${METHOD}_proc "$string"
1104 if [[ $? != 0 ]]; then
1105 eval METHOD=\${METHOD_LIST#*$METHOD }
1106 [[ -z $METHOD ]] && break
1107 METHOD=${METHOD% *}
1108 else
1109 break;
1111 done <<< "${STR}"
1115 # fsyntax: templete <tmpl-file> [output-file]
1116 # fdesc: 对templete-file进行var-content-insert。
1117 templete ()
1119 local output=$2
1121 if [[ -z $output || $output == "-" ]]; then
1122 cat $1 | strfmt_p2p
1123 else
1124 cat $1 | strfmt_p2p | $output
1128 return
1131 ######################
1132 # section: file tail
1133 ######################
1136 foo ()
1138 local ext=
1139 local fmt=
1141 # get format of string by the ext-name
1142 fmt="${2}"
1144 regexpr_proc "$1"
1146 if [[ -n $fmt ]]; then
1147 strrvsfmt "$1" "$fmt" "$3"
1148 else
1149 fmt=$(attr_get catalog::$(ext)::regex)
1150 if [[ -n "$STR_REGEX" ]]; then
1151 regex_str_dispatch "$1" "$fmt" "$3"
1152 else
1153 str2var_array "$1" catalog::$(ext)::syntax "$3"
1157 str2var_proc
1160 str2var_proc=strrvsfmt
1162 # fsyntax: regex_str_dispatch <str-var>
1163 # fdesc: recogonize string by reg-expr, and output sub-str to variables.
1164 regex_str_dispatch ()
1166 local vname="$1"
1167 local vfmt="${1}_fmt"
1168 local fmt=
1169 local str=
1170 local varlist=
1171 local i;
1173 fmt="$(attr_get $vfmt)"
1174 while true; do
1175 : $(( i++ ))
1176 if [[ "$fmt" =~ @{([[:alnum:]_]*)} ]]; then
1177 varlist[$i]="${BASH_REMATCH[1]}"
1178 str="${BASH_REMATCH[1]}_REGEX"
1179 [[ ! -v $str ]] && str="${BASH_REMATCH[1]}_regex"
1180 [[ ! -v $str ]] && str="GENERAL_REGEX"
1182 eval fmt="\${fmt/${BASH_REMATCH[0]}/${!str}}"
1183 else
1184 break;
1186 done
1188 if [[ "$(attr_get $vname)" =~ "${fmt}" ]]; then
1189 [[ "${BASH_REMATCH[@]}" != "$i" ]] && return 1
1190 for ((; i>0; i--)); do
1191 # TBD: set variable by attr_declare, it save data with domain pfx.
1192 declare -g ${varlist[$i]}="${BASH_REMATCH[$i]}"
1193 done
1196 # if [[ "$(attr_get $vname)" =~ "$(attr_get $vfmt)" ]]; then
1197 # for ((i=0; i<${#BASH_REMATCH[@]}; i++)); do
1198 # attr_create ${3}::BASH_REMATCH_${i} = "${BASH_REMATCH[$i]}"
1199 # done
1200 # fi
1203 # use char to seperate string.
1205 # fsyntax: str2var_array <str-var-name> <sepchar> <attr-var-name>
1206 # fdesc: dispatch given string, and store it to arr-name array.
1208 str2var_array ()
1210 local array=
1211 local sepchar=$(attr_get catalog::$2::sepchar)
1212 local wrap=(attr_get catalog::$2::wrap[@])
1213 local i=
1214 local name=
1216 OLD_IFS=$IFS
1217 IFS="$SEPCHAR"
1218 array=( $1 )
1219 IFS=$OLD_IFS
1221 for $((i=0; i<${#array[@]}; i++)); do
1222 if [[ "${wrap[@]}" =~ ${array[$i]:0:1} && "${wrap[@]}" =~ ${array[$i]:0:-1} ]]; then
1223 array[$i]=${array[$i]:1:-1}
1225 if [[ "${wrap[@]}" =~ ${array[$i]:0:1} && "${wrap[@]}" =~ ${array[$i]:0:-1} ]]; then
1226 array[$i]=${array[$i]:1:-1}
1228 if [[ "${wrap[@]}" =~ ${array[$i]:0:1} && "${wrap[@]}" =~ ${array[$i]:0:-1} ]]; then
1229 array[$i]=${array[$i]:1:-1}
1232 name=$(attr_get ${2}::attr_idx[$i])
1234 attr_set ${3}::${name} = ${array[$i]}
1235 done
1238 # strrvsfmt "<cataid1> <type1> <name1> <desc1> <ftype1>" "<@{cataid}>\\\ <@{type}>\\\ <@{name}>\\\ <@{desc}>\\\ <@{ftype}>" var
1239 # use string format defined for strfmt.
1240 strrvsfmt ()
1242 local i=0
1243 local data
1244 local vnamelist=
1245 local var_fmt="([[:alnum:]_]+)"
1246 local fmt=
1248 # dispatch fmt str, and get the var-name list.
1249 # echo "aaa=${aaa//@/$'\n'}"
1250 # echo "${aaa//@/$'\n'}" | while read data; do echo data=${data}; if [[ "$data" =~ .*\{([[:alnum:]]+)\}.* ]]; then echo MATCH=${BASH_REMATCH[1]}; fi; done
1251 while read data; do
1252 # echo "data=$data"
1253 if [[ "$data" =~ ^\{([[:alnum:]_]+)\} ]]; then
1254 # echo "vnamelist[$i]=${BASH_REMATCH[1]}"
1255 # echo "data=$var_fmt${data#*\}}"
1256 fmt+="$var_fmt${data#*\}}"
1257 # echo "fmt=$fmt"
1258 vnamelist[$i]="${BASH_REMATCH[1]}"
1259 : $(( i++ ))
1260 else
1261 fmt+="$data"
1263 done < <(echo "${2//@/$'\n'}")
1265 # reg-expr matching, and fill to the corresponding var under the domain of attr-var paramter given.
1266 # try this example string if there is some problem.
1267 #[[ "<cataid1> <type1> <name1> <desc1> <ftype1>" =~ \<([[:alnum:]_]+)\>\ \<([[:alnum:]_]+)\>\ \<([[:alnum:]_]+)\>\ \<([[:alnum:]_]+)\>\ \<([[:alnum:]_]+)\> ]] && echo XXXXXXXXXXXXXXXXXXXX
1268 if [[ "${1}" =~ $fmt ]]; then
1269 [[ $(( ${#vnamelist[@]} + 1 )) != ${#BASH_REMATCH[@]} ]] && return
1270 for ((i=1; i<${#BASH_REMATCH[@]}; i++)); do
1271 echo ${3}::${vnamelist[$(($i - 1))]} = "${BASH_REMATCH[$i]}"
1272 # attr_create ${3}::${vnamelist[$i]} = "${BASH_REMATCH[1]}"
1273 done
1278 strfmt_var_name_check_bak ()
1280 # for first loop, it use the idx value defined before.
1281 # for next loop, it use the idx value $i.
1282 # if idx is @ or no idx specified, it uses 0, it's equal
1283 # to i=0 in first loop.
1284 for ((i=0; i<cnt; i++)); do
1285 $vout="$(strfmt_pipe <<< \"\${$vname$idx}\")"
1286 # the first idx is setting before loop if cnt == 1.
1287 idx="[$i]"
1288 done
1290 if [[ ${cnt} == 1 ]]; then
1291 if [[ -z $sfx && $idx == 0 ]]; then
1292 vout="-stdout"
1294 vout=$vname
1295 else
1296 if [[ -z $sfx ]]; then
1297 vout="-stdout"
1299 eval vname=\${var%${BASH_REMATCH}}
1302 eval cnt=\${#$1[@]}
1303 if [[ ${var} =~ "_FMT" ]]; then
1304 var=${var%_FMT}
1306 if [[ $cnt -ge 1 ]]; then
1307 # 不指定数组idx,变量为数组,XXX_FMT[@],输出到XXX[@]
1308 for ((; cnt>=0; cnt--)); do
1309 ## echo "${var}[$cnt]=\${${var}_FMT[$cnt]}"
1310 declare -g ${var}[$cnt]="$(strfmt_p2p < <(eval echo \"\${${var}_FMT[$cnt]}a\"))"
1311 eval ${var}[$cnt]="\${${var}[$cnt]:0:-1}"
1312 done
1313 else
1314 # 指定idx的单个数组的元素,XXX_FMT[$i],输出到XXX[$i]
1315 # 非数组的元素,XXX_FMT,输出到XXX,即XXX_FMT[0],输出到XXX[0]
1316 if [[ -z $array ]]; then
1317 eval array="[\${#${var}_FMT[@]}]"
1319 ## echo "${var}$array=\${${var}_FMT$array}"
1320 declare -g ${var}$array="$(strfmt_p2p < <(eval echo \"\${${var}_FMT$array}a\"))"
1321 eval ${var}$array="\${${var}$array:0:-1}"
1323 elif [[ ${var} =~ "_fmt" ]]; then
1324 var=${var%_fmt}
1326 if [[ $cnt -ge 1 ]]; then
1327 # 不指定数组idx,变量为数组,XXX_FMT[@],输出到XXX[@]
1328 for ((; cnt>=0; cnt--)); do
1329 ## echo "${var}[$cnt]=\${${var}_fmt[$cnt]}"
1330 declare -g ${var}[$cnt]="$(strfmt_p2p < <(eval echo \"\${${var}_fmt[$cnt]}a\"))"
1331 eval ${var}[$cnt]="\${${var}[$cnt]:0:-1}"
1332 done
1333 else
1334 # 指定idx的单个数组的元素,XXX_FMT[$i],输出到XXX[$i]
1335 # 非数组的元素,XXX_FMT,输出到XXX,即XXX_FMT[0],输出到XXX[0]
1336 if [[ -z $array ]]; then
1337 eval array="[\${!${var}_fmt[@]}]"
1339 ## echo "${var}$array=\${${var}_fmt[$cnt]}"
1340 declare -g ${var}$array="$(strfmt_p2p < <(eval echo \"\${${var}_fmt$array}a\"))"
1341 eval ${var}$array="\${${var}${array}:0:-1}"
1343 elif [[ -n ${array} ]]; then
1344 # $cnt == 0
1345 # 指定idx的单个数组的元素,XXX[$i],输出到XXX[0]
1346 # xxx: 对于XXX[0],输出到XXX[0],但实际不应这么使用。
1347 ## echo "${var}[$array]=\${${var}$array}"
1348 declare -g ${var}[0]="$(strfmt_p2p < <(eval echo \"\${${var}${array}}a\"))"
1349 eval ${var}[0]="\${${var}[0]:0:-1}"
1350 else
1351 if [[ ${cnt} == 1 ]]; then
1352 # 包含2个元素的数组,且未指定idx
1353 if [[ -v ${var}[0] ]]; then
1354 # 其中一个idx为0,使用非0的idx值。
1355 # xxx: 通常不应该这么使用
1356 vname="${var}[@]"
1357 eval array=( \${#$var[@]} )
1358 array="[${array[1]}]"
1359 ## echo "${var}[0]=\${${var}$array}"
1360 declare -g ${var}[0]="$(strfmt_p2p < <(eval echo \"\${${var}${array}}a\"))"
1361 eval ${var}[0]="\${${var}[0]:0:-1}"
1362 else
1363 # 两个大于0的idx的元素
1364 ### echo "an array var '$var' without index or _fmt or _FMT surfix cannot be used."
1365 # 当成普通数组输出
1366 eval echo "\${${var}[@]}"
1368 elif [[ ${cnt} -gt 1 ]]; then
1369 # 包含多个元素的数组,且未指定idx
1370 ### echo "an array var '$var' without index or _fmt or _FMT surfix cannot be used."
1371 # 当成普通数组输出
1372 eval echo "\${${var}[@]}"
1373 else
1374 # cnt==0
1375 # 非数组的元素,XXX,输出到stdout
1376 eval array="[\${!${var}[@]}]"
1377 if [[ $array == "[0]" ]]; then
1378 ## echo "${var} ==> stdout"
1379 strfmt_p2p < <(echo "${!var}")
1380 else
1381 # xxx: 这里末尾的字符不需要添加,调试用
1382 ## echo "${var}[$array]=\${${var}$array}"
1383 declare -g ${var}[0]="$(strfmt_p2p < <(eval echo \"\${${var}${array}}a\"))"
1384 eval ${var}[0]="\${${var}[0]:0:-1}"
1391 # fsyntax: strfmt_ln
1392 # fdesc: process one line string to output.
1393 strfmt_ln ()
1395 local idx=$1
1396 local i
1397 local j=0
1398 local cnt
1399 local tmp
1400 local baktmp
1401 local flag=0
1402 local var
1403 local varin=$1
1404 local varout=$2
1405 local line
1406 local id
1407 local str
1408 local bak
1409 local bakline
1411 local vname
1413 eval line="\${MAPFILE${embcnt}[$idx]}"
1415 # todo:这里需要对输出的字符串,进行eval/evaln的处理。
1417 # c-style转义符解析。
1418 # 屏蔽符"\\\\"在shell命令行解析成‘\\‘,所以实际字符串操作是是'\\'转换成'\\\\',但单个的'\'不进行replace。
1419 # line="${line//\\\\/\\\\\\\\}"
1420 # 当字符串转义后,'\\\\'转换成'\\',等同于字符串转义操作对'\\'不进行转义,使得奇数个'\'时进行转义,
1421 # 如果未转义,\后的字符为非转义字符。如果奇数个'\'之后是'&','&'replace成$’\n'再以$’\n'分隔成数组时,
1422 # 可根据'\'为奇数个,表示换行符replace之前被'\'屏蔽,&不作为变量标记符号使用。
1423 # 这里屏蔽'\\'的转义操作。
1424 bakline="$line"
1425 if [[ $STRFMT_EVALN == "eval" ]]; then
1426 ## eval echo "xxxxxxxxxxx=\"$line\""
1427 line="`eval echo -n \"\\\"$line\\\"\"`"
1428 # STRFMT_TRANS=""
1429 ## echo -n${STRFMT_TRANS} "${line[0]}"
1430 elif [[ $STRFMT_EVALN == "evaln" ]]; then
1431 line="`evaln echo -n \"$line\"`"
1434 # echo "STRFMT_TRANS=$STRFMT_TRANS"
1436 # fmt变量以{}之间的字符串为变量,进行字符串插入
1437 OLD_IFS=$IFS
1438 # IFS=$'\n'
1439 IFS='@'
1440 line+="@x"
1441 line=( ${line} )
1442 bakline+="@x"
1443 bakline=( ${bakline} )
1444 IFS=$OLD_IFS
1446 cnt=$((${#line[@]}-1))
1447 # echo cnt=$cnt
1449 echo -n${STRFMT_TRANS} "${line[0]}"
1450 for ((i=1; i<cnt; i++)); do
1451 case "${line[$i]:0:1}" in
1452 '(' )
1453 [[ ! "${line[$i]}" =~ ")" ]] && echo -ne "err($idx): ')' missing.\n" >&2 && break
1454 var="${line[$i]:1}"
1455 var="${var%%\)*}"
1456 reststr="${line[$i]#*\)}"
1457 bakreststr="${bakline[$i]#*\)}"
1458 $var
1460 '{' )
1461 # echo "@@@@@@@@@@"
1462 [[ ! "${line[$i]}" =~ "}" ]] && echo -ne "err($idx): '}' missing.\n" >&2 && break
1463 var="${line[$i]:1}"
1464 var="${var%%\}*}"
1466 # STRFMT_PARAM=( ${var} )
1467 # var=$STRFMT_PARAM
1468 case ${var:0:1} in
1469 '!' )
1470 # 环境变量的引用
1471 var="${var:1}"
1472 var=${!var}
1474 ':' )
1475 if [[ ${var:1:1} != ':' ]]; then
1476 # "::",环境变量
1477 var="${var:2}"
1478 var="${STRFMT_DOMAIN}_${var}"
1479 else
1480 # ":",被dup的section作用域
1481 var="${var:1}"
1482 var="${STRFMT_DOMAIN}${var}"
1486 # 当前section的作用域
1489 esac
1490 # echo -e "\nvar=$var\n"
1491 reststr="${line[$i]#*\}}"
1492 bakreststr="${bakline[$i]#*\}}"
1493 #strfmt_v2p "$var"
1495 # 变量解析后输出对应字符串
1496 # 变量名称中不使用转义符,所以不需要判断行末的'\',存在'\'即是不正确的变量名
1497 if [[ -z "$var" || "${var}" =~ [^[:alnum:]_-]+ || ${#line[$i]} -gt 256 ]]; then
1498 # 非变量有效字符,不进行字符串输出
1499 echo -ne "err: symbol name '$var' is not valid.\n" >&2
1500 return 1
1501 elif [[ ! $var =~ [^0-9] ]]; then
1502 # 如果为数字,表示@{var paramlist}中的参数id。
1503 # 格式字符串使用时的函数参数$1不使用,而@{1}用于格式字符串的参数
1504 if [[ -n ${STRFMT_PARAM[${var}]} ]]; then
1505 # echo "strfmt \${STRFMT_PARAM[${var}]}=${STRFMT_PARAM[${var}]}"
1506 echo -n${STRFMT_TRANS} "${STRFMT_PARAM[${var}]}"
1507 else
1508 echo -ne "warn: paramter ${var} is not valid.\n" >&2
1509 return 2
1511 else
1512 # 变量已定义,则调用strfmt_v2p()函数解析并输出
1513 if [[ -n ${!var} ]]; then
1514 # echo var="strfmt $var"
1515 strfmt "$var"
1516 ret=$?
1517 # echo ret=$ret
1518 [[ $ret != 0 ]] && return 3
1519 else
1520 echo -ne "warn: symbol &${line[$i]}& is not defined.\n" >&2
1521 return 2
1525 esac
1527 # 以未eval的字符串的\进行判断
1528 id=$((${#bakreststr}-1))
1529 tmp="${reststr}"
1530 baktmp="${bakreststr}"
1531 [[ "${baktmp:$id:1}" != "\\" ]] && echo -n${STRFMT_TRANS} "${tmp}" && continue
1533 str=${baktmp##*[^\\]}
1534 if [[ $((${#str}%2)) == 1 ]]; then
1535 # 再将字符串中的'\\'替换为'\',进行'\\'的转义操作。
1536 tmp=${reststr%\\}
1537 echo -n${STRFMT_TRANS} "${tmp}@"
1538 : $(( i++ ))
1539 else
1540 echo -n${STRFMT_TRANS} "${tmp}"
1541 continue
1544 while [[ $i < $cnt ]]; do
1545 id=$((${#line[$i]}-1))
1546 tmp="${line[$i]}"
1547 baktmp="${bakline[$i]}"
1548 [[ "${baktmp:$id:1}" != "\\" ]] && echo -n${STRFMT_TRANS} "${tmp}" && break
1550 str=${baktmp##*[^\\]}
1551 if [[ $((${#str}%2)) == 1 ]]; then
1552 # 再将字符串中的'\\'替换为'\',进行'\\'的转义操作。
1553 tmp=${tmp%\\}
1554 echo -n${STRFMT_TRANS} "${tmp}@"
1555 : $(( i++ ))
1556 else
1557 echo -n${STRFMT_TRANS} "${tmp}"
1558 break
1560 done
1561 done
1565 # fsyntax: cat xxx | strfmt_p2p
1566 # fdesc: 不指定第二个参数时,$1格式解析后输出到$2。第二个参数为-时输出到stdout
1567 strfmt_p2p ()
1569 local idx=0
1570 local data
1571 local flag=0
1572 local vname
1573 local data
1575 embcnt=$(( embcnt+1 ))
1577 vname=MAPFILE${embcnt}
1578 unset $vname
1579 declare -g -a ${vname}
1580 while read -r data; do
1581 eval "${vname}+=( \"\$data\" )"
1583 if [[ $idx -gt 1 ]]; then
1584 echo
1586 if [[ $idx == 0 ]]; then
1587 : $(( idx++ ))
1588 continue
1591 eval data=\${$vname[$((idx-1))]}
1592 if [[ -n $data ]]; then
1593 strfmt_ln $((idx-1))
1596 : $(( idx++ ))
1597 done
1599 [[ $idx -gt 1 ]] && echo
1601 vname="vname[$((idx-1))]"
1602 if [[ -n ${!vname} ]]; then
1603 strfmt_ln $((idx-1)) "${!vname}"
1606 embcnt=$(( embcnt-1 ))
1607 unset MAPFILE${embcnt}
1609 return 0
1613 # fsyntax: strfmt_v2p <str-var>
1614 # fdesc: 不指定第二个参数时,$1格式解析后输出到$2第二个参数为-时输出到stdout
1615 strfmt_v2p ()
1617 # echo strfmt_v2p\($1\)
1618 strfmt_p2p < <(echo -n "${!1}"; echo)
1622 # fsyntax: strfmt <str-var>
1623 # fdesc: $1格式解析后输出到$2。不指定第二个参数时,或第二个参数为-时,输出到stdout
1624 strfmt_bak ()
1626 local var="$@"
1627 local array=""
1628 local vname=
1629 local cnt
1631 # eval vname="\${!${1}*}"
1632 # echo vname=$vname
1634 [[ -z $var ]] && return
1636 # @ 格式的变量名称
1637 if [[ ${var:0:1} == "@" ]]; then
1638 if [[ ${var:1:1} == "(" ]]; then
1639 # TBD: check '@', and check the attr after it.
1640 var="${var:2:-1}"
1641 $var
1642 return $?
1643 elif [[ ${var:1:1} == "{" ]]; then
1644 var="${var:2:-1}"
1645 else
1646 var="${var:1}"
1648 STRFMT_PARAM=( $var )
1649 var=$STRFMT_PARAM
1650 # echo "var=$var"
1651 if [[ ${var:0:1} == ':' ]]; then
1652 if [[ ${var:1:1} == ':' ]]; then
1653 # "::",使用当前domain。
1654 var="${var:2}"
1655 var="${STRFMT_DOMAIN}_${var}"
1656 else
1657 # ":",被dup的section作用域
1658 # :使用定义类型的domain,当前domain之后使用。
1659 var="${var:1}"
1660 var="${STRFMT_DOMAIN}${var}"
1662 elif [[ ${var:0:1} == '!' ]]; then
1663 # 环境变量的引用
1664 var="${var:1}"
1665 var=${!var}
1666 else
1667 # 不使用:,不添加domain,以普通环境变量使用。
1670 var="${var//:/_}"
1673 # 单个item的数组
1674 if [[ ${var} =~ "[" ]]; then
1675 array="${var#*[}"
1676 array="[${array%%]*}]"
1677 [[ "$array" == "[*]" || "$array" == "[@]" ]] && array=""
1678 var="${var%%[*}"
1680 ## vname="${var}${array}"
1681 # echo vname=$vname
1682 if [[ -z $array ]]; then
1683 eval cnt=\$\(\( \${\#${var}[@]} - 1 \)\)
1684 # echo "var=$var"
1685 else
1686 cnt=0
1689 # echo -n "STRFMT_PARAM=${STRFMT_PARAM[@]}"
1690 if [[ ! "$var" =~ [^0-9] ]]; then
1691 # 如果为数字,表示@{var paramlist}中的参数id。
1692 # 格式字符串使用时的函数参数$1不使用,而@{1}用于格式字符串的参数
1693 if [[ -n ${STRFMT_PARAM[${var}]} ]]; then
1694 # echo "strfmt \${STRFMT_PARAM[${var}]}=${STRFMT_PARAM[${var}]}"
1695 echo -n${STRFMT_TRANS} "${STRFMT_PARAM[${var}]}"
1696 return 0
1697 else
1698 echo -ne "warn: paramter ${var} is not valid.\n" >&2
1699 return
1703 ## echo "var=$var"
1704 # 参数指定的变量xxx已定义,或xxx未定义,但xxx[]数组有定义,且只包含一个元素
1705 if [[ -v "${var}" || $cnt -ge 0 ]]; then
1706 # todo:这里需要对输入、输出的变量进行设置。
1708 strfmt_var_name_check "$var"
1709 elif [[ "$(readlink /proc/self/fd/0)" =~ 'pipe:[' ]]; then
1710 cat - | strfmt_p2p
1711 else
1712 echo "no input data or no variable specified."
1713 return 1
1716 return 0
1719 abc="aaaaaaaaaaaaaa"
1720 strfmt_testing ()
1722 local fmt="@abc"
1723 echo XXXXXXXXXXXXXXXXXXXXXXXXXX
1724 strfmt fmt
1727 # use reloadshlib strfmt.shlib to load this lib, and open this invoking to debug
1728 # code in tesing mode.
1729 strfmt_testing