转换为大写,转义字符除外

转换为大写,转义字符除外

我发现的方法还通过影响换行符来进一步破坏线路。
例如...

$ message="First Line\nSecond Line"; 
$ echo "${message^^}"
FIRST LINE\NSECOND LINE

是否有一种优雅的方法将字符串转换为大写,但保留转义字符,以获得以下输出?

FIRST LINE\nSECOND LINE

我可以做一些复杂的事情,比如将“\n”更改为 0001 或类似的操作,应用转换,然后将 0001 返回到“\n”。但也许有更好的方法。

答案1

zsh而不是bash

$ message="First Line\nSecond Line"
$ set -o extendedglob
$ print -r -- ${message//(#b)((\\?)|(?))/$match[2]$match[3]:u}
FIRST LINE\nSECOND LINE

bash(或任何 shell)中,使用 的 GNU 实现sed,您可以执行相同的操作:

$ printf '%s\n' "$message" | sed -E 's/(\\.)|(.)/\1\u\2/g'
FIRST LINE\nSECOND LINE

一些可能更有效的变体,因为它们最大限度地减少了替换数量:

  • zsh

    print -r -- ${message//(#b)((\\?)|([^\\]##))/$match[2]$match[3]:u}
    

    或者

    print -r -- ${message//(#b)((\\?)#)([^\\]##)/$match[1]$match[3]:u}
    
  • 他们的 GNUsed翻译:

    printf '%s\n' "$message" | sed -E 's/(\\.)|([^\\]+)/\1\U\2/g'
    

    或者

    printf '%s\n' "$message" | sed -E 's/((\\.)*)([^\\]+)/\1\U\3/g'
    

请注意,它们会将(Meta-x,例如s\Mx支持的转义序列,并扩展为 0xf8 字节 ('x' + 0x80))转换为(0xd8)。它们也转换为或到或到,但这应该不是问题,因为它们扩展为相同的。zshprint\MX\x7a\x7A\u007a\u007A\Cx\CX

答案2

我很想将转义序列解释为文字字符:

message="First Line\nSecond Line"
declare -u Message                       # uppercase on assignment
printf -v Message -- "${message//%/%%}"  # assign
declare -p Message                       # inspect

结果

declare -u msg="FIRST LINE
SECOND LINE"

答案3

echo "$message"  |  sed -e 's/^[[:lower:]]/\u&/' -e 's/\([^\]\)\([[:lower:]]\)/\1\u\2/g' \
                                                 -e 's/\([^\]\)\([[:lower:]]\)/\1\u\2/g'
  • -e 's/^[[:lower:]]/\u&/'  如果字符串中的第一个字符(或者更一般地说,一行中的第一个字符)是小写字母,请将其大写。因为一行中的第一个字符无法转义。呃。这是理所当然的。

  • -e 's/\([^\]\)\([[:lower:]]\)/\1\u\2/g'  一次查看该行两个字符。如果小写字母前面不是反斜杠,则保留前面的字符,并将小写字母大写。

    您可能认为这足以处理整条线。不幸的是,由于它一次处理该行两个字符,所以它只获取每隔一个的字母:

    $ echo "first line\nsecond line" | sed -e 's/\([^\]\)\([[:lower:]]\)/\1\u\2/g'
    fIrSt LiNe\nSeCoNd LiNe
    

    所以,

  • -e 's/\([^\]\)\([[:lower:]]\)/\1\u\2/g'  第二次做同样的事情。这将拾取第一次传递时跳过的字母。


替代版本:

echo "$message" | sed -e 's/^[[:lower:]]/\u&/' \
                                  -e ': loop; s/\([^\]\)\([[:lower:]]\)/\1\u\2/g; t loop'

与第一个版本基本相同,但不是重复第二个版本s命令,它用循环迭代它。


不幸的是,这对于双反斜杠将无法正常工作:  即使 应该大写,也会foo\\bar变成,因为是转义的反斜杠,因此不应导致被转义。FOO\\bARb\\b

答案4

该变量可以逐行迭代。然后再次连接输出。

重击:

$ message="First Line\nSecond Line";
$ message=$(echo -e ${message} |while read -r line; do echo -n "${line^^}\n" ; done) && message=${message%??}
$ echo ${message} 
FIRST LINE\nSECOND LINE

相关内容