【问题标题】:How to get the line number of a string in another string in Shell如何在Shell中获取另一个字符串中的字符串的行号
【发布时间】:2021-05-13 08:42:55
【问题描述】:

给定

str="hij";
sourceStr="abc\nefg\nhij\nlmn\nhij";

我想得到$str$sourceStr中第一次出现的行号,应该是3

我不知道该怎么做。 我试过了:

awk 'match($0, v) { print NR; exit }' v=$str <<<$sourceStr
grep -n $str <<< $sourceStr | grep -Eo '^[^:]+';
grep -n $str <<< $sourceStr | cut -f1 -d: | sort -ug
grep -n $str <<< $sourceStr | awk -F: '{ print $1 }' | sort -u

所有输出1,而不是3

如何获取$sourceStr$str的行号?

谢谢!

【问题讨论】:

  • 您创建了一个变量,其中包含一行多次包含文字字符串\n。您是否打算将 sourceStr 用作多行字符串?
  • 您想要字符串比较还是正则表达式比较?
  • 嗨@WilliamPursell,我的意思是多行字符串。我认为 Shell 会将其解释为多行字符串。感谢您指出并给出答案。我今天学到了新东西!
  • 嗨@EdMorton,我如何实现目标并不重要。我只是想获取源字符串中匹配行的行号。
  • 对,我的问题是——目标是什么?是进行字符串比较还是正则表达式比较?例如,如果va.c,它应该只匹配输入中的a.c(就像它与字符串比较一样)还是它也应该匹配abc(就像它与正则表达式比较一样)?有关不同类型比较的更多信息,请参阅how-do-i-find-the-text-that-matches-a-pattern

标签: shell awk grep


【解决方案1】:

您可以在bash 中使用此awk + printf

awk -v s="$str" '$0 == s {print NR; exit}' <(printf "%b\n" "$sourceStr")

3

甚至这个awk 没有任何bash 支持:

awk -v s="$str" -v source="$sourceStr" 'BEGIN {
split(source, a); for (i=1; i in a; ++i) if (a[i] == s) {print i; exit}}'

3

你也可以使用这个sed

sed -n "/^$str$/{=;q;}" <(printf "%b\n" "$sourceStr")

3

或者这个grep + cut

printf "%b\n" "$sourceStr" | grep -nxF -m 1 "$str" | cut -d: -f1

3

【讨论】:

  • 嗨@anubhava,是的。它们都在 Mac 上的 Terminal.app 中工作。当我使用执行 Shell 脚本在 Keyboard Maestro 中尝试时,只有最后一个有效。我收到一个错误报告:意外标记 `(' 附近的语法错误。但这没关系。我只需要一个有效的!感谢您提供这么多解决方案,以便我可以选择!
  • printf "%b\n" "$sourceStr" | awk -v s="$str" '$0 == s {print NR; exit}' 也应该可以工作
  • 嗨@anubhava,完成!谢谢!
【解决方案2】:

不清楚您是否刚刚犯了剪切粘贴错误,但您的 sourceStr 不是多行字符串(如下所示)。此外,您确实需要引用您的此处字符串(如下所示)。也许你只是想要:

$ sourceStr="abc\nefg\nhij\nlmn\nhij"
$ echo "$sourceStr"
abc\nefg\nhij\nlmn\nhij
$ sourceStr=$'abc\nefg\nhij\nlmn\nhij'
$ echo "$sourceStr"
abc
efg
hij
lmn
hij
$ cat <<< $sourceStr 
abc efg hij lmn hij
$ cat <<< "$sourceStr" 
abc
efg
hij
lmn  
hij
$ str=hij
$ awk "/${str}/ {print NR; exit}" <<< "$sourceStr"
3

【讨论】:

  • /hij/ 也会在@hijkl 中找到hij== 更安全
  • @anubhava 同意,但 OP 使用 match($0, v) 所以这只是在模仿。但我认为这里的关键问题是字符串的分配,而不是 awk。
  • 在任何脚本或字符串周围使用双引号会打开一罐蠕虫,以确保稳健性和安全性。
  • 嗨@WilliamPursell,谢谢你给我看这个。我根本不知道这件事。所以这两个都会输出相同的结果:``` sourceStr=$"abc\nefg\nhij\nlmn\nhij"; echo $sourceStr sourceStr="abc\nefg\nhij\nlmn\nhij"; echo "$sourceStr" ``` 但是,这将输出1: ``` str=hij; sourceStr=$"abc\nefg\nhij\nlmn\nhij"; awk "/${str}/ {print NR; exit}" 3 ``` str=hij; sourceStr=$'abc\nefg\nhij\nlmn\nhij'; awk "/${str}/ {print NR; exit}"
  • 对不起。编码格式不好。按 Enter 不能更改行。我必须使用“Shift + Enter”来制作新行。但是,它们不会反映在评论中。
【解决方案3】:

要做到这一点总是有困难的:

str="hij";
sourceStr="abc\nefg\nhij\nlmn\nhij";
echo -e $sourceStr | nl | grep $str | head -1 | gawk '{ print $1 }'

或者,更高效一点:

str="hij";
sourceStr="abc\nefg\nhij\nlmn\nhij";
echo -e $sourceStr | gawk '/'$str/'{ print NR; exit }'

【讨论】:

  • '/'$str/'...'$str 暴露给外壳以进行通配、工作拆分等,因此它很脆弱并且存在安全问题。要安全地做到这一点,'/'"$str"/'...' 仍然存在其他问题,所以永远不要让 shell 变量扩展成为这样的 awk 脚本文本的一部分,有关详细信息,请参阅stackoverflow.com/questions/19075671/…echo -e $sourceStrgrep $str 应该是 echo -e "$sourceStr"grep "$str" 顺便说一句 - 请参阅 mywiki.wooledge.org/Quotes 了解何时可以安全/适当地删除 shell 中的引号
  • 嗨@Luuk,感谢您的回复。奇怪的是,为什么您的代码不需要双引号来解释 \n 符号。我对安全问题知之甚少。但是,由于@EdMorton 提到了它,我认为在您回复之前最好不要投票给您的答案。再次感谢!
  • \n 被解释是因为回显后的-e。 (是的,Ed 是对的!)
【解决方案4】:

只需使用sed

printf 'abc\nefg\nhij\nlmn\nhij\n' \
| sed -n '/hij/ { =; q; }'

解释:如果sed 遇到包含“hij”的行(正则表达式/hij/),它会打印行号(= 命令)并退出(q 命令)。否则它不会打印任何东西(-n 开关)并继续下一行。


[update] 嗯,抱歉,我刚刚注意到您的“所有输出 1,而不是 3”。

您的命令不输出 3 的主要原因是 sourceStr="abc\nefg\nhij\nlmn\nhij" 不会自动将您的 \n 更改为新行,因此它最终成为一行,这就是您的命令始终显示 1 的原因.

如果你想要一个多行字符串,这里有两个解决方案bash

  • printf -v sourceStr "abc\nefg\nhij\nlmn\nhij"
  • sourceStr=$'abc\nefg\nhij\nlmn\nhij'

现在您的变量包含空格字符(换行符),正如 William Pursell 所说,为了保留它们,您必须用双引号将您的 $sourceStr 括起来:

grep -n "$str" <<< "$sourceStr" | ...

【讨论】:

  • 嗨@xhienne,感谢您的详细解释。您指出了我的代码与@WilliamPurshell 指出的相同的问题。我在 Terminal.app 中尝试了你的命令,但它不起作用。它给出了一个错误代码:sed: 1: "/hij/ { =; q }": extra characters at the end of q command。我没有看到任何额外的字符,所以我不知道发生了什么。
  • 嗯,抱歉,我忘记了命令分隔符;。答案已更新。
  • 谢谢!它现在正在工作!抱歉,Stackoverflow 似乎只允许一个已检查的解决方案,我已将其提供给第一个回复。但你的也很有帮助。我只能检查它是否有用。
  • 没问题@Martin,你接受的那个非常好(详细且有教育意义),我也投了赞成票
猜你喜欢
  • 2013-07-06
  • 1970-01-01
  • 1970-01-01
  • 2017-12-24
  • 2020-07-05
  • 1970-01-01
  • 1970-01-01
  • 2018-06-04
  • 1970-01-01
相关资源
最近更新 更多