使用 grep 搜索以值为美元符号的变量开头的行

使用 grep 搜索以值为美元符号的变量开头的行

我发现了很多非常相似的问题,但并不完全是这个。我有一个文本文件,内容如下(无重复,每行固定 4 个字符):

A1234
G1234
$1234
M1234

我尝试使用 grep 搜索以字符 $char 开头的行,该字符已通过使用 $char 定义搜索词输入,并将其后的四个数字分配给 $numbr。它看起来像这样:

numbr=`grep ^"${char}" file.txt | cut -c 2,3,4,5`

这适用于我需要的任何字符,除了美元符号,它使 $numbr 为空。

如果有帮助的话,输入字符 $char 预先定义如下:

char="`dd if=text.txt ibs=1 skip=$skipcount count=1`"

($skipcount 是整数)

我尝试了带和不带 -E 标志的情况,以及我能找到的所有转义 $char 值的方法。我不需要专门使用 grep,到目前为止,我使用它取得了最大的成功。

我被这个问题困扰了好久,所以任何帮助我都会非常感激。如果我重复了帖子,请原谅,感谢所有在这里做出贡献的人,我几乎在一两分钟内就找到了所有问题的解决方案。

编辑:对不起,我无意中删除了评论的那个人。要点是他建议使用 echo 和 pipe,类似这样的(我认为这只是部分内容,我需要补充其余部分):

echo "${char}" | grep '^\$'

对我来说不起作用。我也不够清楚 - 在 .txt 中的每个字符串(例如 A1234)中,只有第一个字符被分配给 $char。$char 后面的 4 个数字是我需要分配给 $numbr 的,并且该字符串可以出现在 .txt 文件中的任何行。每行上不会有其他字节。

答案1

$在 RegEx 中具有特殊含义(行尾)...因此,为了完全匹配它,需要通过例如用反斜杠将其转义来删除其特殊含义\...要动态地执行此操作,可以使用 Bash 的参数扩展,例如${char/\$/\\$}

grep -Po "^${char/\$/\\$}\K[[:digit:]]+" file.txt

... 在哪里:

  • grep的选项-Po将启用erl 风格正则表达式(需要\K)和o仅打印匹配的捕获组。

  • ^将匹配一行的开头。

  • ${char/\$/\\$}双引号内的" ... "将被 shell(Bash)扩展为变量char,用 替换 (从左边)第一次出现的$(如果找到) ,即\$把它传递给命令行并在它前面加上转义符,以便它稍后通过 进行字面匹配grep1

  • Perl\K将从打印中排除其左侧匹配的部分(将在该点重置匹配),这样只会[[:digit:]]+打印其右侧匹配的部分...仍然会评估整个表达式的左侧和右侧,并且必须按照该顺序在输入行中匹配。

  • [[:digit:]]+将匹配一个数字[[:digit:]](包括任何类型的 UNICODE)至少一次+

1) 对于不支持 Bash${var/find/replace}这类参数扩展的其他 shell,您可以在方括号内使用常规参数扩展,例如,[${char}]当该参数在 内扩展时[],生成的字符 eg$将被视为文字。

...并在变量赋值中使用它,如下所示:

numbr=$(grep -Po "^${char/\$/\\$}\K[[:digit:]]+" file.txt)

注意命令替换的旧符号,即反引号“`...`”现在是一个遗留的兼容性特性,并且已被弃用,取而代之的是当前的命令替换符号$(...)......所以,使用后者。


其他无grep解决方案(为了提高可移植性,因为grep-P选项可能并非在所有实现中都存在/受支持)包括:

awk

awk -F"${char}" '$2~"^[[:digit:]]+$" {print $2}' file.txt

... 其中字段分隔符设置为${char},然后如果第二个字段全部是数字$2~"^[[:digit:]]+$",则打印它print $2

sed

sed -nE "s/^([${char}])([[:digit:]]+)$/\2/p" file.txt

...-nE默认为no 打印并启用扩展正则表达式来处理例如[]和捕获组(),然后脚本字符串周围的双引号"..."将允许 shell 进行参数扩展,以便${char}扩展为其值,如果正则表达式在一行中匹配,则匹配的数字被分配给第二个捕获组即([[:digit:]]+)并通过其参考数字调用\2来替换整个匹配,然后使用命令打印p

perl

export char; perl -lne 'print $1 if /^\Q$ENV{char}\E(\d+)$/' file.txt

...-n默认为no 打印,如果 RegEx 匹配,将打印ieprint $1中的第一个捕获组(的缩写)。(...)(\d+)([[:digit:]]+)

注意为了从和export char之间的 Perl 脚本中将该变量作为环境变量调用,这是为了在扩展到和时正确处理 RegEx\Q\Echar$$]可能不起作用...否则,如果$不是 RegEx 的一部分,则类似以下内容应该起作用:

perl -lne "/(?<=^[${char}])([[:digit:]]+)$/ and print $&" file.txt

...-n默认为no 打印和双引号"..."将允许 shell 参数扩展发生,并且后视(?<=...)将匹配但在非捕获(无打印)组中,并将print $&从捕获组打印匹配项。

答案2

有些人在遇到问题时会想“我知道,我会使用正则表达式。” 现在他们有两个问题。1

对于您来说,有两个问题是:

  1. 这个$字符在你的 shell 中在某些情况下是特殊的

  2. $字符在正则表达式的某些上下文中是特殊的

对于perl,还有第三个问题 - 这$在某些情况下也是特殊的 - 这就是为什么将 shell 变量扩展括在括号中的技巧[${char}]在双引号sed表达式中有效,但在类似的双引号perl表达式中无效(因为后者导致 perl 扩展$]Perl 解释器的修订版、版本和颠覆)。


因此,您希望您的 shell 扩展${char}(或$char)到它的值$,但无论是 shell 还是您使用的工具都无法进一步扩展$@Raffa 的全面回答向您展示一些实现该目标的方法。

GNU grep 的一个怪癖是基本正则表达式模式(即没有-E-P命令行开关),$行尾锚点是仅有的当它出现在表达式末尾时,它很特殊。因此,而^$仅匹配空行,^$[[:digit:]]\{4\}甚至^$.$逐字匹配。因此,根据你的例子file.txt,要么

$ grep "^${char}[[:digit:]]\{4\}" file.txt | cut -c 2-
1234

或者更简单

$ grep ^"${char}". file.txt | cut -c 2-
1234

会给出您想要的输出。但是,由于您似乎不需要检查后面的字符串是否$确实是 4 位数字,因此您也可以cut单独使用2

$ cut -sd "${char}" -f2 file.txt
1234

这避免了两个都问题,完全摒弃正则表达式,将任务视为简单的字符串拆分任务。awk3类似:

$ awk -F "${char}" 'NF>1 {print $2}' file.txt
1234

请注意,这些都不会将匹配固定$在行首 - 如果您需要这样做,那么在 awk 中执行此操作的非正则表达式方法可能是

awk 'index($0,ENVIRON["char"]) == 1 {print substr($0,2)}' file.txt

其中ENVIRON类似于 perl 的ENV哈希的数组需要您导出$char,但允许您单引号表达——从而完全避免了“问题#1”。


  1. “现在你有两个问题”是什么意思?

  2. 假设 GNU 实现cut,及其-s, --only-delimited命令行选项

  3. A单个字符字段分隔符在 awk 中不被视为正则表达式

相关内容