【问题标题】:Linux Bash shell: Leaving some ANSI codes (mostly color) and not interpreting some others from string in a function callLinux Bash shell:在函数调用中保留一些 ANSI 代码(主要是颜色)而不从字符串中解释其他一些代码
【发布时间】:2015-01-22 17:51:02
【问题描述】:

这是我的案例功能的一个例子:

function SendToScreen(){
echo -e "$*"
}

所以我叫它:

SendToScreen "Hello"

如果我想添加颜色代码

VioletForeGroundColor="\033[38;5;99m"
NormalColor="\033[0m"
SendToScreen "Hello"$VioletForeGroundColor" violet "$NormalColor" word."

这给了我一个正确的答案:


但是如果我想发送一些 DOS 类型的路径(包括\ 斜杠),就会出现问题

VioletForeGroundColor="\033[38;5;99m"
NormalColor="\033[0m"
MyDOSPath="d:\vivisector"
SendToScreen "Hello"$VioletForeGroundColor" violet "$NormalColor" word. The path is $MyDOSPath"

因为\v是某种ANSI代码,所以这次我得到:


我需要我的函数来输出彩色文本(粗体、草书、下划线......等),所以我必须使用echo -e

我如何解决问题,让这种烦人的控制代码与\v 这样的字符发生冲突(我想还会有其他的)?

我想通过修改函数来修复这个问题,但我不确定这是不是正确的方法。

谢谢。

EDIT-1:我们将选择 \033 也称为 \e 作为唯一需要保留的 ANSI 代码。

【问题讨论】:

  • 您必须安排$MyDOSPath 被转义,以便echo -e 打印您想要的内容。我可能使用 C 程序来做到这一点;我不想为这项工作编写外壳。我会使用... The path is $(escape "$MyDOSPath")"。顺便说一句,您可以使用 SendToScreen "Hello ${VioletForeGroundColor}violet ${NormalColor}word." 将单个参数发送给函数,同时正确解释变量。
  • 该程序已经用 bash 编写,现在将所有内容迁移到 C 有点长。但我认为转义变量是个好主意。 escape 函数可以将每个 \v 替换为 \\v。此功能也可用于转义任何其他冲突字符。感谢@JonathanLeffler 的建议。
  • 我的意思是“一个程序来转换包含可能的转义序列的字符串,以便它们对echo -e 是安全的”,而不是重写更大的 Bash 脚本。

标签: linux bash shell ansi-escape


【解决方案1】:

新答案:

function SendToScreen() {
    echo -e $(echo "${*//\\/\\\\}" | sed 's/\\\\033\[/\\033\[/g');
}

这个会转义所有内容,然后取消转义任何看起来像颜色序列的内容 (\033[)。将文件名作为颜色序列发送的可能性大大降低。您可以通过仅将您希望允许的颜色序列列入白名单并将 sed 命令更改为不转义这些确切序列的 sed 命令序列来进一步减少它。

旧答案: 假设你想逃避 \v 和 \n,你可以这样做:

function SendToScreen(){
    a="${*//\\v/\\\\v}"
    a="${a//\\n/\\\\n}"
    echo -e "$a"
}

您可以使用您不想处理的任何其他转义来扩展它。

【讨论】:

  • 这似乎是个好主意。我对shell替换不是很有经验:如果我发现其他一些唠叨` code, say \n`,这个方法是否可以轻松升级以实现它?
  • \t 怎么样。 \n\007\r\f\a\b等?
  • 答案已更新以显示如何扩展到其他转义序列
  • 边界线......充其量。您无法在SendToScreen 函数中分辨出哪些反斜杠需要由echo -e 扩展(与颜色转义序列相关的那些),哪些不能扩展(DOS 文件名的一部分)。所以映射必须在函数的调用中完成,而不是在函数本身中。
  • @JonathanLeffler,我不明白您所说的“您无法在 SendToScreen 函数中说明哪些反斜杠需要通过 echo -e 扩展”。颜色转义序列是八进制或十六进制,因此它们以“\x”或“\0”开头。因此,您可以使用此方法转义您提到的所有其他转义,\007 除外。因此,会有一些逃逸通过,但常见的逃逸(DOS 风格的路径)会被捕获。
【解决方案2】:

echo -e 仅解释以反斜杠开头的序列,因此您只需确保 $MyDOSPath 参数的所有反斜杠都加倍。那可能是:

SendToScreen "Hello ${VioletForeGroundColor}violet${NormalColor} word." \
             "The path is ${MyDOSPath//\\/\\\\}"

使用'substitute' parameter expansion// 表示“将每个反斜杠更改为双反斜杠”。


正如各种 cmets 中所讨论的,SendToScreen 的设计可能不是最佳的。一种可能的替代设计用途:

SendToScreen [-e "string-to-expand"][-p "plain-string"] [-- "plain strings"]

需要扩展的参数是,不应该扩展的参数不是。默认情况下,它们不是。所以,示例用法:

$ VioletForeGroundColor="\033[38;5;99m"
$ NormalColor="\033[0m"
$ MyDOSPath="C:\new\table\value\alert\form\033.txt"
$ echo "$MyDOSPath"
C:\new\table\value\alert\form\033.txt
$ bash SendToScreen.sh -e "${VioletForeGroundColor}violet${NormalColor}" -e "The path is ${MyDOSPath//\\/\\\\}" -p "Or $MyDOSPath" "Plain $MyDOSPath"
violet The path is C:\new\table\value\alert\form\033.txt Or C:\new\table\value\alert\form\033.txt Plain C:\new\table\value\alert\form\033.txt
$  bash SendToScreen.sh -e "${VioletForeGroundColor}violet${NormalColor}" -e "The path is ${MyDOSPath//\\/\\\\}" -p "Or $MyDOSPath" -e "Oops! $MyDOSPath" "Plain $MyDOSPath"
violet The path is C:\new\table\value\alert\form\033.txt Or C:\new\table\value\alert\form\033.txt Oops! C: ew able
              aluelert
                      orm.txt Plain C:\new\table\value\alert\form\033.txt
$

最后一批输出的十六进制转储是:

0x0000: 1B 5B 33 38 3B 35 3B 39 39 6D 76 69 6F 6C 65 74   .[38;5;99mviolet
0x0010: 1B 5B 30 6D 20 54 68 65 20 70 61 74 68 20 69 73   .[0m The path is
0x0020: 20 43 3A 5C 6E 65 77 5C 74 61 62 6C 65 5C 76 61    C:\new\table\va
0x0030: 6C 75 65 5C 61 6C 65 72 74 5C 66 6F 72 6D 5C 30   lue\alert\form\0
0x0040: 33 33 2E 74 78 74 20 4F 72 20 43 3A 5C 6E 65 77   33.txt Or C:\new
0x0050: 5C 74 61 62 6C 65 5C 76 61 6C 75 65 5C 61 6C 65   \table\value\ale
0x0060: 72 74 5C 66 6F 72 6D 5C 30 33 33 2E 74 78 74 20   rt\form\033.txt 
0x0070: 4F 6F 70 73 21 20 43 3A 20 65 77 20 61 62 6C 65   Oops! C: ew able
0x0080: 0B 61 6C 75 65 07 6C 65 72 74 0C 6F 72 6D 1B 2E   .alue.lert.orm..
0x0090: 74 78 74 20 50 6C 61 69 6E 20 43 3A 5C 6E 65 77   txt Plain C:\new
0x00A0: 5C 74 61 62 6C 65 5C 76 61 6C 75 65 5C 61 6C 65   \table\value\ale
0x00B0: 72 74 5C 66 6F 72 6D 5C 30 33 33 2E 74 78 74 0A   rt\form\033.txt.
0x00C0:

violet 出现在紫罗兰色中,你必须相信我的话。

显然,SendToScreen 的用户(调用者)必须知道哪些参数应该扩展,哪些不应该扩展。但是,它使它非常明确。

这是我用作脚本的代码。将重新包装为函数留给读者作为练习。扩展它以添加-c colour(或者可能是-f foreground-b background)是读者的练习。

#!/bin/bash

output=()

while getopts "p:e:" opt
do
  case "$opt" in
    (e) output+=( $(echo -e "$OPTARG") );;
    (p) output+=( "$OPTARG" );;
  esac
done

shift $(($OPTIND - 1))

echo "${output[@]}" "$@"

玩得开心!

【讨论】:

  • 不幸的是,这给 SendToScreen 的用户带来了负担,要弄清楚哪些东西要转义,哪些不能转义。例如,如果他们有自己包含颜色信息的变量怎么办?例如。 ExamplePath="C:\new\table\${Red}your_project${Normal}"
  • @MarkkuK。是的,确实如此,但正如您在 cmets 中对您的回答所讨论的那样,函数无法知道哪些反斜杠序列将由 echo -e 解释,哪些应该保持不变。而echo -e 只是改变了一切。所以,调用者必须小心,因为函数不能。或者必须以某种方式重新设计该功能,以便使用echo -e 处理颜色更改序列,而无需echo -e 处理其他序列。在当前设计下,传递给函数的所有内容都受echo -e 的约束;只有调用者代码才能知道不应该是什么。
猜你喜欢
  • 1970-01-01
  • 2012-04-07
  • 1970-01-01
  • 2016-07-21
  • 1970-01-01
  • 2019-08-03
  • 1970-01-01
  • 2016-08-03
  • 2018-09-25
相关资源
最近更新 更多