突出显示字符串差异

Highlight String Differences

我正在寻找一种方法来突出显示 2 个字符串之间的差异。这个想法是在终端中显示 iconv 更改了哪些字符。两个字符串都已处理以删除前导和尾随空格,但必须处理内部空格。

RED="$(tput setaf 1)"    ##    Short variables for the tput ->
CYA="$(tput setaf 6)"    ## -> commands to make output strings ->
CLS="$(tput sgr0)"       ## -> easier to read
str1="[String nâmè™]"    # String prior to iconv
str2="[String name[tm]]" # String after iconv -f utf-8 -t ascii//translit

最终我想自动设置差异的格式,以便它们被我可以回显到终端的 tput 颜色代码包围。

${str1} = 以红色突出显示,两个字符串不通用的字符

${str2} = 以青色突出显示,两个字符串不通用的字符

想要的输出:

output1="[String n${RED}â${CLS}m${RED}è™${CLS}]"
output2="[String n${CYA}a${CLS}m${CYA}e[tm]${CLS}]"

我看到的大多数 diff 实用程序都在行或字级别上工作。我正在考虑为第一个 diff 的 byte# 解析 cmp 的输出,但我似乎必须重新解析多个差异。

不管怎么说,我想这似乎是一个复杂的过程,所以我只是想确保我没有遗漏一个明显的解决方案或工具。

现在我认为最简单的方法是格式化每个字符串以在新行上放置一个字节,然后打开我的选项。

nstr1="$(fold -w1 <<< "$(echo "${str1}")")"
nstr2="$(fold -w1 <<< "$(echo "${str2}")")"
diff <(echo -e "${nstr1}") <(echo -e "${nstr2}")

这就是我所了解的,除非我走在正确的轨道上,否则我不想走得更远。我敢肯定有无数种方法可以做到这一点,但是有没有更有效的方法呢?

@Thomas Dickey 在评论中提供的答案是没有比我尝试的方法更容易的工具或过程。

最后,我能够通过以下 diff 行生成足够简单的“ 想要的输出”。

diff --changed-group-format="${RED}%=${CLS}" <(echo -e "${nstr1}") <(echo -e "${nstr2}")|tr -d '\n'
diff --changed-group-format="${CYA}%>${CLS}" <(echo -e "${nstr1}") <(echo -e "${nstr2}")|tr -d '\n'

不幸的是,我还没有想出如何回显输出来解释颜色代码,但这是另一个问题。

总而言之:

#!/usr/bin/env bash

# Using stdin input, outputs each char. on its own line, with actual newlines
# in the input represented as literal '\n'.
toSingleCharLines() {
  sed 's/\(.\)/\'$'\n''/g; s/\n$/\'$'\n''\n/'
}

# Using stdin input, reassembles a string split into 1-character-per-line output
# by toSingleCharLines().
fromSingleCharLines() {
  awk '[=10=]=="\n" { printf "\n"; next} { printf "%s", [=10=] }'
}

# Prints a colored string read from stdin by interpreting embedded color references
# such as '${RED}'.
printColored() {
  local str=$(</dev/stdin)
  local RED="$(tput setaf 1)" CYA="$(tput setaf 6)" RST="$(tput sgr0)"
  str=${str//'${RED}'/${RED}}
  str=${str//'${CYA}'/${CYA}}
  str=${str//'${RST}'/${RST}}
  printf '%s\n' "$str"
}

# The non-ASCII input string.
strOrg='[String nâmè™]'

# Create its ASCII-chars.-only transliteration.
strTransLit=$(iconv -f utf-8 -t ascii//translit <<<"$strOrg")

# Print the ORIGINAL string with the characters that NEED transliteration
# highlighted in RED.
diff --changed-group-format='${RED}%=${RST}' \
  <(toSingleCharLines <<<"$strOrg") <(toSingleCharLines <<<"$strTransLit") |
    fromSingleCharLines | printColored

# Print the TRANSLITERATED string with the characters that RESULT FROM
# transliteration highlighted in CYAN.
diff --changed-group-format='${CYA}%=${RST}' \
  <(toSingleCharLines <<<"$strTransLit") <(toSingleCharLines <<<"$strOrg") |
    fromSingleCharLines | printColored

这产生: