Warm tip: This article is reproduced from serverfault.com, please click

其他-如何遍历字符串从Linux外壳模式?

(其他 - how to loop through string for patterns from linux shell?)

发布于 2020-11-28 18:03:46

我有一个脚本,该脚本可以在目录中的文件中查找字符串,例如:tagName: 对单个标记有效,:tag:但对多个:tagOne:tagTwo:tagThree:标记无效

我当前的脚本可以:

grep -rh -e '^:\S*:$' ~/Documents/wiki/*.mkd ~/Documents/wiki/diary/*.mkd | \
sed -r 's|.*(:[Aa-Zz]*:)|\1|g' | \
sort -u
printf '\nNote: this fails to display combined :tagOne:tagTwo:etcTag:\n'

第一行正在生成这样的输出:

:politics:violence:
:positivity:
:positivity:somewhat:
:psychology:
:socialServices:family:
:strategy:
:tech:
:therapy:babylon:
:trauma:
:triggered:
:truama:leadership:business:toxicity:
:unfurling:
:tagOne:tagTwo:etcTag:

目标是要获取到的单一列表:tag:的。

同样,问题是如果一行包含多个标签,则该行根本不会出现在输出中(与之相反的问题是,仅显示该行的第一个标签)。显然| sed... |那里是有问题的。

**我想:tagOne:tagTwo:etcTag:变成这样:

:tagOne:
:tagTwo:
:etcTag:

等等用:politics:violence:

冒号不是必需的,tagOne也比冒号好(也许更好,但这是微不足道的):tagOne:

问题是,如果一个行有多个标签,该行根本不会出现在输出(而不是仅仅是只有行的第一个标签被显示的问题)。显然| sed... |那里是有问题的。

所以我应该用sed更好的东西代替...

我已经试过

智慧的sed:

grep -rh -e '^:\S*:$' ~/Documents/wiki/*.mkd ~/Documents/wiki/diary/*.mkd | \
  sed -r 's|(:[Aa-Zz]*:)([Aa-Zz]*:)|\1\r:\2|g' | \
  sed -r 's|(:[Aa-Zz]*:)([Aa-Zz]*:)|\1\r:\2|g' | \
  sed -r 's|(:[Aa-Zz]*:)([Aa-Zz]*:)|\1\r:\2|g' | \
  sort -u

...适用(仅适用于有限数量的标签),但它会产生奇怪的结果,例如:

:toxicity:p:
:somewhat:y:
:people:n:

...在某些标签的末尾放置奇怪的随机字母,其中:p:最后一个字符是:leadership:标签,“领导力”不再出现在列表中。:y:相同:n:

我还尝试了几种方式使用循环...

grep -rh -e '^:\S*:$' ~/Documents/wiki/*.mkd ~/Documents/wiki/diary/*.mkd | \
  sed -r 's|(:[Aa-Zz]*:)([Aa-Zz]*:)|\1\r:\2|g' | \
  sed -r 's|(:[Aa-Zz]*:)([Aa-Zz]*:)|\1\r:\2|g' | \
  sed -r 's|(:[Aa-Zz]*:)([Aa-Zz]*:)|\1\r:\2|g' | \
  sort -u | grep lead

...也有:leadership:丢失标签等问题。就像...

for m in $(grep -rh -e '^:\S*:$' ~/Documents/wiki/*.mkd ~/Documents/wiki/diary/*.mkd); do
  for t in $(echo $m | grep -e ':[Aa-Zz]*:'); do
    printf "$t\n";
  done
done | sort -u

...根本不分隔标签,只打印类似以下内容的内容: :truama:leadership:business:toxicity

我应该采取其他方法吗?使用其他实用程序(也许cut在循环内)?也许是在python中做的(我有一些python脚本,但是对语言不太了解,但是这样做很容易)?每次看到时,awk我都会想“ EEK!” 因此,我希望使用非简单的解决方案,而是坚持使用我所使用的范例以更好地学习它们。

Questioner
alec
Viewed
1
rowboat 2020-11-29 11:02:13

直通tr可以将这些字符串拆分为单独的行:

grep -hx -- ':[:[:alnum:]]*:' ~/Documents/wiki{,/diary}/*.mkd | tr -s ':' '\n'

这也将删除冒号,并且输出中将出现一个空行(易于修复,请注意,由于前导,该空行将始终是第一个:)。添加sort -u以排序并删除重复项,或awk '!seen[$0]++'删除不进行排序的重复项。

使用以下方法sed

sed '/^:/!d;s///;/:$/!d;s///;y/:/\n/' ~/Documents/wiki{,/diary}/*.mkd

这也消除冒号,但避免了添加空行(通过除去前/后:s使用前y音译剩余:<newline>)。sed可以与tr结合使用:

sed '/:$/!d;/^:/!d;s///' ~/Documents/wiki{,/diary}/*.mkd | tr -s ':' '\n'

使用awk与工作:分开场,删除重复:

awk -F: '/^:/ && /:$/ {for (i=2; i<NF; ++i) if (!seen[$i]++) print $i}' \
~/Documents/wiki{,/diary}/*.mkd