2016-05-23 26 views
1

bash中有一种方法可以在保留颜色代码的同时获取特定长度的子字符串(在终端中占用#个字符)吗?Bash - 使用颜色代码获取子字符串

我想我可以解释一下我的意思是一些代码更好(假装大胆的手段绿色):

 
$ # One string is green and another is normal. 
$ green_string="\e[1;32mFoo bar baz buzz.\e[0m" 
$ normal_string=`echo -e $green_string | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g"` 
$ 
$ # Echo both of them out to show the difference 
$ echo -e $green_string 
Foo bar baz buzz. 
$ echo -e $normal_string 
Foo bar baz buzz. 
$ 
$ # Get a substring of both. 
$ green_sub=${green_string:0:11} 
$ normal_sub=${normal_string:0:11} 
$ 
$ # Since the special characters are a part of the string, the colored 
$ # substring (rightfully) has fewer printable characters in it. 
$ echo -e "$green_sub\e[0m" 
Foo 
$ echo -e $normal_sub 
Foo bar baz 
$ 
$ # Is there any built in way of doing something like this: 
$ green_sub=`some_method $green_string 11` 
$ normal_sub=`some_method $normal_string 11` 
$ echo -e "$green_sub\e[0m"; echo -e $normal_sub 
Foo bar baz 
Foo bar baz 

对于我这样的人,这里的复制/粘贴版本:

green_string="\e[1;32mFoo bar baz buzz.\e[0m" 
normal_string=`echo -e $green_string | sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[mGK]//g"` 
echo -e $green_string 
echo -e $normal_string 
green_sub=${green_string:0:11} 
normal_sub=${normal_string:0:11} 
echo -e "$green_sub\e[0m" 
echo -e $normal_sub 
# green_sub=`some_method $green_string 11` 
# normal_sub=`some_method $normal_string 11` 
# echo -e "$green_sub\e[0m"; echo -e $normal_sub 

我为了复制/粘贴演示的目的,这个功能需要ls的输出并使其填满整行终端(或更短):

function lsline { 
    color_out=$(ls --color=always | tr "\n" " ") 
    max_len=`tput cols` 
    cur_len=0 
    is_esc=0 
    # This is the build of the final string that will be printed out. 
    build="" 
    # This is the build of any escape sequence so I know not to 
    # include it in the substring count 
    esc_build="" 
    for ((i=0; i<${#color_out}; i++)); do 
     char="${color_out:$i:1}" 
     # Is there an easier way to check if a char is an escape character? 
     code=$(printf %x "'$char") 

     # Currently in an escape sequence, so don't count towards max length. 
     if [ "$is_esc" -eq "1" ]; then 
      esc_build="$esc_build$char" 
      if [ "$char" = "m" ]; then 
       is_esc=0 
       build="$build$esc_build" 
       esc_build="" 
      fi 
     elif [ "$code" = "1b" ]; then 
      # 27 is escape character. 
      is_esc=1 
      esc_build="$char" 
     else 
      # Not an escape sequence, so build our normal string and update 
      # the current substring length. 
      build="$build$char" 
      ((cur_len++)) 
      if [ "$cur_len" -eq "$max_len" ]; then 
       build="$build$(tput sgr0)" 
       break 
      fi 
     fi 
    done 

    echo "$build" 
} 

此代码有效,但它是sooo slow

请告诉我这样做有一个更快/更简单的方法!

回答

0

使用正则表达式跳过ANSI序列,仅捕获“正常”文本。下面是一个正则表达式的例子,它捕获(可见)字符串的前5个字符,应用于常规字符串和绿色字符串。

$ regex='\e\[[^m]*m(.{5}).*\e\[0m' 
$ for str in '\e[1;32mFoo bar baz\e[0m' 'Foo bar baz'; do 
> [[ $str =~ $regex ]] && substr=${BASH_REMATCH[1]} 
> echo "$substr" 
> done 
Foo b 
Foo b 
+0

这怎么能适用于有多个颜色代码的字符串呢? “\ e [1; 32mGreen \ e [0m和\ e [1; 31mRed \ e [0m” –

+0

)实质上,您需要解析字符串,这不是任何shell语言的强项。对于彩色字符串应该是什么样子没有特别的标准。这些字节不是标记标签;它们只是终端的指令,以改变其显示以下文本的方式。 – chepner