我发现了很多非常相似的问题,但并不完全是这个。我有一个文本文件,内容如下(无重复,每行固定 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
,用 替换 (从左边)第一次出现的$
(如果找到) ,即\$
把它传递给命令行并在它前面加上转义符,以便它稍后通过 进行字面匹配grep
。1Perl
\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
\E
char
$
$]
可能不起作用...否则,如果$
不是 RegEx 的一部分,则类似以下内容应该起作用:
perl -lne "/(?<=^[${char}])([[:digit:]]+)$/ and print $&" file.txt
...-n
默认为no 打印和双引号"..."
将允许 shell 参数扩展发生,并且后视(?<=...)
将匹配但在非捕获(无打印)组中,并将print $&
从捕获组打印匹配项。
答案2
有些人在遇到问题时会想“我知道,我会使用正则表达式。” 现在他们有两个问题。1
对于您来说,有两个问题是:
这个
$
字符在你的 shell 中在某些情况下是特殊的该
$
字符在正则表达式的某些上下文中是特殊的
对于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
这避免了两个都问题,完全摒弃正则表达式,将任务视为简单的字符串拆分任务。awk
3类似:
$ 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”。
假设 GNU 实现
cut
,及其-s, --only-delimited
命令行选项A单个字符字段分隔符在 awk 中不被视为正则表达式