Bash编程(6) String操作

1. 拼接

  1) 简单的字符串拼接如:PATH=$PATH:$HOME/bin。如果拼接的字符串包含空格或特殊字符,需要使用双引号括起,如:

var=$HOME/bin  # 注释并不是赋值的一部分
var="$HOME/bin # but this is"

# bash 3.1后,可以使用+=拼接(+=也可用于数组相加)
var=abc

$ var=abc
$ var+=xyz
$ echo "$var"
abcxyz

注意:+=的性能较直接拼接的效率高,测试如下:

$ var=; time for i in {1..10000}; do var=${var}foo; done;

real    0m1.251s
user    0m1.144s
sys    0m0.104s
$ var=; time for i in {1..10000}; do var+=foo; done;

real    0m0.156s
user    0m0.156s
sys    0m0.000s 

2) 重复字符到指定长度

_repeat(){
    #@ 功能:重复字符串到指定长度
    _REPEAT=
    while (( ${#_REPEAT} < $2))
    do
        _REPEAT+=$1
    done
}

$ _repeat % 40
$ printf "%s\n" "$_REPEAT"
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  通过拼接在每个循环中拼接多个实例来提高函数速度:

_repeat(){
    #@ 功能:重复字符串到指定长度
    _REPEAT=$1
    while (( ${#_REPEAT} < $2))
    do
        _REPEAT=$_REPEAT$_REPEAT$_REPEAT
    done
    _REPEAT=${_REPEAT:-:$2}  ##  裁剪指定长度字符串
}

repeat(){
    _repeat "$@"
    printf "%s\n" "$_REPEAT"
}

alert(){ #@ 功能: 打印包含边界及鸣响的警告信息
_repeat "${2:-#}" $(( ${#1} + 8 ))
printf '\a%s\n' "$_REPEAT" ## \a = BEL
printf '%2.2s %s %2.2s\n' "$_REPEAT" "$1" "$_REPEAT"
printf '%s\n' "$_REPEAT"

}

$ alert "Do you really want to detele all your files?"

#################################################################################
## Do you really want to detele all your files? ##
#################################################################################

 2. 字符处理

 没有直接的参数扩展来提供字符串的首字符或尾字符,可借助通配符问号?,以及字符串截取来抽取首字符或尾字符。

$ var=strip
$ allbutfirst=${var#?}  # 去除首字符
$ allbutlast=${var%?}   # 去除尾字符
$ printf "%8s %8s\n" "$allbutfirst" "$allbutlast"
    trip     stri
$ first=${var%"$allbutfirst"} 
$ last=${var#"$allbutlast"}
$ printf "%8s %8s\n" "$first" "$last"
s p

## 小测试
$ while [ -n "$var" ]; do temp=${var#?}; char=${var%"$temp"}; printf "%s\n" "$char"; var=$temp; done ## 将字符串的每个字母按行打印
s
t
r
i
p 
$ while [ -n "$var" ]; do temp=${var%?}; char=${var#"$temp"}; printf "%s\n" "$char"; var=$temp; done  ## 将字符串的每个字母反向打印
p
i
r
t
s 
 也可以通过"%c"获取字符串首字符
$ printf -v first "%c" "$var"  # 将原本输出到标注输出的信息赋值给first
$ echo $first
s

3. 大小写转换

Bourne shell中,字符转换可以通过tr命令完成,tr作用:将第一个参数中的字符转换成对应的第二个参数中的字符

$ echo abcdefgh | tr ceh CEH  ## c=>C,e=>E,h=>H
abCdEfgH
$ echo abcdefgh | tr ceh HEC ## c=>H,e=>E,h->C
abHdEfgC
$ echo touchdown | tr 'a-z' 'A-Z' ## 将小写转换为大写
TOUCHDOWN 

POSIX shell中,可以通过参数扩展来完成。

to_upper(){
    case $1 in
        a*) _UPR=A;; b*) _UPR=B;; c*) _UPR=C;; d*) _UPR=D;;
        e*) _UPR=E;; f*) _UPR=F;; g*) _UPR=G;; h*) _UPR=H;;
        i*) _UPR=I;; j*) _UPR=J;; k*) _UPR=K;; l*) _UPR=L;;
        m*) _UPR=M;; n*) _UPR=N;; o*) _UPR=O;; p*) _UPR=P;;
        q*) _UPR=Q;; r*) _UPR=R;; s*) _UPR=S;; t*) _UPR=T;;
        u*) _UPR=U;; v*) _UPR=V;; w*) _UPR=W;; x*) _UPR=X;;
        y*) _UPR=Y;; z*) _UPR=X;; *) _UPR=${1%${1#?}};;
   esac
}

$word=function
$to_upper "$word"
$printf "%c%s\n" "$_UPR" "${word#?}"
Function

## 将所有字符转换为大写
_upword(){
    local word=$1
    while [ -n "$word" ]  ## 循环直到$word为空
    do
        to_upper "$word"
        _UPWORD=$_UPWORD$_UPR
        word=${word#?}  ## 移除$word中的首字母
    done
}

upword(){
    _upword "$@"
    printf "%s\n" "$_UPWORD"
}

 4. 比较内容且不需考虑大小写

当输入为单个字母,例如请求Y或N,可以使用逻辑运算符或(|)或者方括号([])对大小写进行选择。

read ok
case $ok in
    y|Y) echo "Great!" ;;
    n|N) echo Good-bye; exit 1 ;;
    *)  echo Invalid entry ;;
esac


read ok
case $ok in
    [yY]) echo "Great!" ;;
    [nN]) echo Good-bye; exit 1 ;;
    *)  echo Invalid entry ;;
esac 

 当输入较长时,以上方法需要将所有的可能组合进行展示,这样的方法较为繁琐。

## 针对于"|",需要列出所有组合
jan | jaN | jAn | jAN | Jan | JAn | JAN) echo "Great!" ;;

## 对于"[]",脚本不宜阅读
read monthname
case $monthname in
    [Jj][Aa][Nn]*) month=1 ;;
    [Ff][Ee][Bb]*) month=2 ;;
    ## 输入剩余的月份
    [Dd][Ee][Cc]*) month=12 ;;
    [1-9]1[0-2] month=$monthname ;; 考虑输入数字的情况
    *) echo "Invalid month: $monthname" >&2 ;;
esac 
较好的方法是将输入统一转换为大写或小写再进行比较:
_upword "$monthname"
case _UPWORD in
    JAN*) month=1 ;;
    FEB*) month=2 ;;
   ## 输入剩余的月份
   DEC*) month=12 ;;
   [0-9]|1[0-2]) month=$monthname
   *) echo "Invalid month: $monthname" >&2 ;;
esac
bash 4.*中字符转换为大写,也可采用${monthname^^}执行

 5. 检查变量名的有效性

检查变量名是否满足仅包含字母、数字和下划线,且只能以字母和下划线开头。

validname(){
    case $1 in
        [!a-zA-Z_]* | *[!a-zA-Z0-9_]*) return 1;;
    esac
}

for name in name1 2var first.name first_name last-name
do
    validname "$name" && echo " valid: $name" || echo "invalid: $name"
done

valid: name1
invalid: 2var
invalid: first.name
 valid: first_name
invalid: last-name 

 6. 字符串插入

_insert_string(){ #@功能: 在字符串的指定位置插入字符串
    local insert_string_dflt=2  ## 默认的插入位置
    local string=$1    ## 被插入的字符串
    local i_string=$2  ## 待插入字符串
    local i_pos=${3:-${insert_string_dflt:-2}}  ## 插入位置
    local left right
    left=${string:0:$(( $i_pos -1 ))}
    right=${string:$(( $i_pos -1 ))}
    _insert_string=$left$i_string$right
}

insert_string(){
    _insert_string "$@" && printf "%s\n" "$_insert_string"
}

$ insert_string poplar u 4
popular
$ insert_string show ad 3
shadow
$ insert_string tail ops ## 使用默认位置
topsail

     7. 覆盖

在一个字符串上覆盖另一个字符串。

_overlay(){
    local string=$1
    local sub=$2
    local start=$3
    local left right
    left=${string:0:start-1}
    right=${string:start+${#sub}-1}
    _OVERLAY=$left$sub$right
}

overlay(){
    _overlay "$@" && printf "%s\n" "$_OVERLAY"
}

$ {
> overlay pony b 1
> overlay pony u 2
> overlay pony s 3
> overlay pony d 4
> }
bony
puny
posy
pond 

8. 裁剪不想要的字符

字符串首尾的空格可以通过循环和条件判断完成。

var="    John    "
while : ## 无限循环
do
    case $var in
        ' '*) var=${var#?} ;; ## 如果字符串以空格开始,则移除
        *' ') var=${var%?} ;; ## 如果字符串以空格结尾,则移除
        *) break; ## 当字符串的头部或尾部均无空格,则退出循环
     esac
done 

更有效的方法是找到首尾最长待删除的空格,然后从原始字符串中删除。

var="    John    "
printf "%s|%s\n" "$var" "${#var}"
rightspaces=${var##*[! ]} ## 删除一切直到最后一个非空值
printf "%s|%s\n" "$rightspaces" ${#rightspaces}  ## rightspaces为4个空格
var=${var%"$rightspaces"} ## var目前为"John    "
printf "%s|%s\n" "$var" "${#var}"
leftspaces=${var%%[! ]*} ## 从第一个非空值删除直到结尾
printf "%s|%s\n" ${leftspaces} ${#leftspaces}
var=${var#"$leftspaces"}
printf "%s|%s\n" "$var" "${#var}" 

 进一步封装的方法如下:如果存在第二个参数,则从字符串中删除该参数对应的字符,如果为空,则默认删除空格。

_trim(){ #@ 从$1中删除空格(或$2中的字符)
   local trim_string
    _TRIM=$1
    printf "%s|%s\n" "$_TRIM" ${#_TRIM}
    trim_string=${_TRIM##*[!${2:- }]}
    printf "%s|%s\n" "$trim_string" ${#trim_string}
    _TRIM=${_TRIM%"$trim_string"}
    printf "%s|%s\n" "$_TRIM" ${#_TRIM}
    trim_string=${_TRIM%%[!${2:- }]*}
    printf "%s|%s\n" "$trim_string" ${#trim_string}
    _TRIM=${_TRIM#"$trim_string"}
    printf "%s|%s\n" "$_TRIM" ${#_TRIM}
}

trim(){
    _trim "$@" && printf "%s\n" "$_TRIM"
}

$ trim " S p a c e d o u t "
S p a c e d o u t
$ trim "0002367.45000" 0
2367.45 

9. 索引

定位一个字符串在另一个字符串中的索引位置。

_index(){ #@ $2在$1中的位置保存在$_INDEX
   local idx
   case $1 in
        "") _INDEX=0; return 1 ;;
        *"$2"*) idx=${1%%"$2"*}  ## 提取匹配位置的起始
                _INDEX=$(( ${#idx} + 1 )) ;;
        *) _INDEX=0;  return 1 ;;
   esac
}

index(){
    _index "$@" && printf "%d\n" "$_INDEX"
}

 

例:基于月份的前3个字母,打印出对应的数值

_month2num(){
    local month=JAN.FEB.MAR.APR.MAY.JUN.JUL.AUG.SEP.OCT.NOV.DEC
    _upword "${1:0:3}" ## 提取$1中的前3个字母,并转换为大写
    _index "$month" "$_UPWORD" || return 1
    _MONTH2NUM=$(( $_INDEX / 4 + 1 ))
}

month2num(){
    _month2num "$@" && printf "%s\n" "$_MONTH2NUM"
}
posted @ 2019-02-14 23:27  mengrennwpu  阅读(1025)  评论(0编辑  收藏  举报