shell 脚本(例如 sed)用于创建带有字符替换的映射

shell 脚本(例如 sed)用于创建带有字符替换的映射

我在 bash 中有一个变量,它保存一个"<topic>...<topic>"表示需要映射到数据库表名称的主题的字符串,通过映射配置替换非法字符:

所需的映射格式是"topic1:table1,topic2:table2"——也就是说,这就是我需要的输出。

对于上下文,这是一个配置条目雪花卡夫卡连接器这有助于将数据从主题流式传输到表中,重要的是,表名称在允许的字符方面受到更多限制。

在这个最简单的情况下,非法字符是连字符,应将其转换为下划线。

例如,对于"foo-bar,boo-baz"输入字符串,所需的答案将是:

"foo-bar:foo_bar,boo-baz:boo_baz"

在 Python 中,这很简单:

import sys
s = sys.argv[1]
print(','.join(p + ':' + p.replace('-', '_') for p in s.split(',')))

我正在寻找一种基于 shell 脚本工具的解决方案,以避免安装额外的软件。

我知道 sed 例如有标签我认为这可以帮助解决这个问题,但我还没有找到解决方案。

答案1

假设您在名为 的变量中有输入TOPICS

使用sed

sed 's/[^,]\+/\0:\0/g; :a s/:\([^-,]\+\)-/:\1_/g; ta' <<<"$TOPICS"
  1. s/[^,]\+/\0:\0/g- 选取每个主题(不包含逗号的单词),并在其后添加相同的单词和冒号。这部分之后的结果将是:foo-bar:foo-bar,boo-baz:boo-baz
  2. :a s/:\([^-,]\+\)-/:\1_/g- 将每个冒号后面的连字符替换为下划线。
  • 此时,它只会替换每个主题中的第一个连字符,因此,例如,如果其中一个主题是foo-bar-baz,并且在第一步之后使用 has foo-bar-baz:foo-bar-baz,则在这一步之后结果将是foo-bar-baz:foo_bar-baz
  1. ta- 如果最近的替换成功(连字符被下划线替换) - 返回标签:a以检查是否有更多替换需要进行。这是为了防止某个主题具有多个连字符。如果在步骤 2 中未进行替换,则不要分支回标签 - 继续到下一行。
  • 例如:foo-bar-baz:foo_bar-baz上一个示例现在将变为foo-bar-baz:foo_bar_baz.

使用awk

awk 'BEGIN {ORS=RS=","} { if (gsub( /\n$/, "" )) ORS="\n"; NEW=$0; gsub("-", "_", NEW); print $0":"NEW}' <<<"$TOPICS"
  1. RS(输入记录分隔符)和ORS(输出记录分隔符)设置为,。这样awk会将每个主题视为单独的一行。
  2. if (gsub( /\n$/, "" )) ORS="\n"- 如果单词的最后一个字符是\n(换行),则将其删除。gsub将返回替换数 (1),然后在最后一个单词上,它不会打印读取单词后的新行,而仅作为最后一个(输出记录分隔符)。
  3. gsub("-", "_", NEW)- 用下划线替换连字符。

使用awk++ sedtr

我们在这里有额外的命令,但它可能更容易阅读:

echo "$TOPICS" \
 | tr ',' '\n' \
 | awk '{NEW=$0; gsub("-", "_", NEW); print $0":"NEW}' \
 | tr '\n' ',' \
 | sed 's/,$/\n/'

  1. tr ',' '\n'- 首先用新行分隔单词。
  2. awk打印映射。
  3. tr '\n' ','- 用逗号替换换行符。
  4. sed 's/,$/\n/'- 除了最后一个逗号,应该用换行符替换。

答案2

假设您只想操作字符串,实现此目的的一种方法如下:

#!/usr/bin/env bash

p="foo-bar,boo-baz"
IFS=',' read -ra arr <<< "$p"
result=()

for item in "${arr[@]}"; do
  result+=("${item}:${item//-/_}")
done

end_result=$(printf '%s,' "${result[@]}")
echo "${end_result%,*}"

如果您想了解字符串操作,请参阅https://mywiki.wooledge.org/BashFAQ/100

答案3

在每个 Unix 机器上的任何 shell 中使用任何 awk:

$ echo 'foo-bar,boo-baz' |
    awk -F',' '{for (i=1; i<=NF; i++) {t=$i; gsub(/-/,"_",t); printf "%s:%s%s", $i, t, (i<NF ? FS : ORS)}}'
foo-bar:foo_bar,boo-baz:boo_baz

或者如果您愿意:

$ echo 'foo-bar,boo-baz' |
    awk -v RS=',' '{t=$1; gsub(/-/,"_",t); printf "%s:%s%s", $1, t, (sub(/\n$/,"") ? ORS : RS)}'
foo-bar:foo_bar,boo-baz:boo_baz

如果输入不以换行符结尾(根据 POSIX,它应该是一个有效的文本文件),第二个将会失败。

相关内容