如何循环linux shell中模式的字符串?

如何循环linux shell中模式的字符串?,linux,string,bash,awk,split,Linux,String,Bash,Awk,Split,我有一个脚本,可以在目录中的文件中查找字符串,如:tagName:,它可以很好地用于单个:tag:,但不能用于多个:tagOne:tag2:tag3:标记 我当前的脚本包括: 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 com

我有一个脚本,可以在目录中的文件中查找字符串,如
:tagName:
,它可以很好地用于单个
:tag:
,但不能用于多个
:tagOne:tag2:tag3:
标记

我当前的脚本包括:

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:
的目标是将其放入单个
:标记的列表中:

同样,问题是,如果一行有多个标记,那么该行根本不会出现在输出中(而问题仅仅是只显示该行的第一个标记)。显然,
| sed|存在问题

**我想将
:tagOne:tagTwo:etcTag:
转换为:

:tagOne:
:tagTwo:
:etcTag:
政治:暴力:
等等

冒号不是必需的,
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:
标记的最后一个字符,并且“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
…这与
的问题相同:领导力:
标签丢失等。 就像

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
…它根本不分离标签,只打印如下内容:
:特鲁阿玛:领导力:商业:毒性


我应该采取其他方法吗?在循环内使用不同的实用程序(可能是
cut
)?也许用python来做这件事(我有一些python脚本,但不太懂这门语言,但这样做可能很容易)?每次我看到
awk
我都会想“EEK!”,所以我更喜欢非awk解决方案,更喜欢坚持我使用过的范例,以便更好地学习它们。

grep
中使用PCRE(如果可用)和积极回顾:


OPs初始
grep生成的样本数据

$ cat tags.raw
:politics:violence:
:positivity:
:positivity:somewhat:
:psychology:
:socialServices:family:
:strategy:
:tech:
:therapy:babylon:
:trauma:
:triggered:
:truama:leadership:business:toxicity:
:unfurling:
:tagOne:tagTwo:etcTag:
$ cat tags.raw
:politics:violence:
:positivity:
:positivity:somewhat:
:psychology:
:socialServices:family:
:strategy:
:tech:
:therapy:babylon:
:trauma:
:triggered:
:truama:leadership:business:toxicity:
:unfurling:
:tagOne:tagTwo:etcTag:
一个
while/for/printf
idea基于关联数组:

unset arr
typeset -A arr                          # declare array named 'arr' as associative

while read -r line                      # for each line from tags.raw ...
do
    for word in ${line//:/ }            # replace ":" with space and process each 'word' separately
    do
        arr[${word}]=1                  # create/overwrite arr[$word] with value 1;
                                        # objective is to make sure we have a single entry in arr[] for $word;
                                        # this eliminates duplicates
    done
done < tags.raw

printf ":%s:\n" "${!arr[@]}" | sort     # pass array indices (ie, our unique list of words) to printf;
                                        # per OPs desired output we'll bracket each word with a pair of ':';
                                        # then sort
上述两种情况都会产生:

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

使用awk的另一个想法


OPs初始
grep生成的样本数据

$ cat tags.raw
:politics:violence:
:positivity:
:positivity:somewhat:
:psychology:
:socialServices:family:
:strategy:
:tech:
:therapy:babylon:
:trauma:
:triggered:
:truama:leadership:business:toxicity:
:unfurling:
:tagOne:tagTwo:etcTag:
$ cat tags.raw
:politics:violence:
:positivity:
:positivity:somewhat:
:psychology:
:socialServices:family:
:strategy:
:tech:
:therapy:babylon:
:trauma:
:triggered:
:truama:leadership:business:toxicity:
:unfurling:
:tagOne:tagTwo:etcTag:
一个
awk
idea:

awk '
    { split($0,tmp,":")                     # split input on colon;
                                            # NOTE: fields #1 and #NF are the empty string - see END block
      for ( x in tmp )                      # loop through tmp[] indices
          { arr[tmp[x]] }                   # store tmp[] values as  arr[] indices; this eliminates duplicates
    }
END { delete arr[""]                        # remove the empty string from arr[]
      for ( i in arr )                      # loop through arr[] indices
          { printf ":%s:\n", i }            # print each tag on separate line leading/trailing colons
    }
' tags.raw | sort                           # sort final output
注意:我对
awk对数组进行内部排序(从而消除外部
排序
调用)的能力不太了解,因此我对建议持开放态度(或者有人可以将此答案复制到新答案并使用所述能力进行更新?)

上述因素还产生:

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

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

grep -hx -- ':[:[:alnum:]]*:' ~/Documents/wiki{,/diary}/*.mkd | tr -s ':' '\n'
这也将删除冒号,并且输出中将出现一个空行(易于修复,请注意,由于前导的
,空行始终是第一行)。添加
sort-u
对重复项进行排序和删除,或添加
awk'!查看[$0]+'
以删除重复项而不进行排序

使用
sed
的方法:

sed '/^:/!d;s///;/:$/!d;s///;y/:/\n/' ~/Documents/wiki{,/diary}/*.mkd
这也会删除冒号,但避免添加空行(在使用
y
将剩余的
音译为
之前,先用
s
删除前导/尾随的
)。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

awk-F:'/^://&&&&&&:$/{for(i=2;i您提供了一些示例输出(您想要什么;您的脚本生成什么),但您没有提供任何示例输入;请使用与您的输出相对应的示例输入更新问题sok,提供了一些示例输入谢谢:)越来越近,但是…有12行示例输入,但是您的输出(当前,预期)与这12行不匹配,例如,
tagOne
tagTwo
etcTag
显示在您的输出中,但不显示在输入中;如果问题被更新,则输出(当前,预期)会有所帮助与这12行示例输入相对应;如中所述,您需要向我们提供重现问题以及解决方案所需的详细信息。现在,请认为一切都有意义:)eeek!所有awk答案都是什么?!!!您能解释为什么需要使用perl风格的正则表达式而不仅仅是
-e
人吗grep
:-P--将模式解释为与Perl兼容的正则表达式(PCREs)
-e
的目的实际上只是为了消除正则表达式以破折号开头时的歧义。eek:)向上投票,谢谢!数组是必要的吗?我喜欢看循环示例,谢谢。是否可以简单地在for循环中自己的行上打印
单词
排序-u
可以确保它们是唯一的)是的,这是可行的;我添加阵列(自动删除DUP)的唯一原因是我不知道您计划对下游数据做什么,即,虽然Q&A要求只显示唯一列表,但它没有说明您计划对列表做什么(例如,您是否需要使用此列表进行后续处理)因此,数组为您提供了一组数据,您可以在以后“重复使用”,而无需重新处理原始数据;给我一分钟时间,我将添加另一个部分来消除数组,以支持
打印/排序
。@alec是的,目的是用您的命令替换
标记。原始
,但您需要正确的格式;