grep "^$1"
这类作品,但是我该如何逃脱,"$1"
以便grep不会特别解释其中的任何字符?
或者,还有更好的方法?
编辑:
我不想搜索,'^$1'
而是要搜索动态插入的固定字符串,该字符串仅在行首时才应匹配。这就是我的意思$1
。
grep
但您必须首先在字符串中转义任何特殊字符,例如printf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
grep "^$1"
这类作品,但是我该如何逃脱,"$1"
以便grep不会特别解释其中的任何字符?
或者,还有更好的方法?
编辑:
我不想搜索,'^$1'
而是要搜索动态插入的固定字符串,该字符串仅在行首时才应匹配。这就是我的意思$1
。
grep
但您必须首先在字符串中转义任何特殊字符,例如printf %s ^;printf %s "$1" | sed 's/[][\.*^$]/\\&/g'; } | grep -f- infile
Answers:
我想不出一种使用此方法的方法grep
;^
本身是正则表达式的一部分,因此使用它需要对正则表达式进行解释。它使用子串匹配的琐碎awk
,perl
或什么:
awk -v search="$1" 'substr($0, 1, length(search)) == search { print }'
要处理包含的搜索字符串\
,您可以使用与123的答案相同的技巧:
search="$1" awk 'substr($0, 1, length(ENVIRON["search"])) == ENVIRON["search"] { print }'
\/
\\\/\/\/\\\\/
像\\///\\/
程序中那样的复杂字符串,仍然会失败。据我所知,除非您事先知道将使用多少反斜杠,否则无法正确地避开awk中的反斜杠。
如果只需要检查是否找到匹配项,则将所有输入行剪切为所需前缀($1
)的长度,然后使用固定模式grep:
if cut -c 1-"${#1}" | grep -qF "$1"; then
echo "found"
else
echo "not found"
fi
获取匹配行数也很容易:
cut -c 1-"${#1}" | grep -cF "$1"
或所有匹配行的行号(行号从1开始):
cut -c 1-"${#1}" | grep -nF "$1" | cut -d : -f 1
您可以将行号输入到head
并tail
获取匹配行的全文,但是到那时,仅使用Python或Ruby之类的现代脚本语言就更容易了。
(以上示例假定Posix grep和cut。它们假定要搜索的文件来自标准输入,但可以很容易地改成采用文件名。)
编辑:您还应确保模式($1
)不是零长度的字符串。否则cut
失败说values may not include zero
。另外,如果使用Bash,请使用set -o pipefail
来捕获错误退出cut
。
如果你$1
是纯ASCII和你grep
有-P
选项(启用PCRE),你可以这样做:
#!/bin/bash
line_start="$1"
line_start_raw=$(printf '%s' "$line_start" | od -v -t x1 -An)
line_start_hex=$(printf '\\x%s' $line_start_raw)
grep -P "^$line_start_hex"
这里的想法是grep -P
允许正则表达式\xXX
指定文字字符,其中XX
该字符的十六进制ASCII值。即使是特殊的正则表达式字符,该字符也将按字面值进行匹配。
od
用于将预期的行开头转换为十六进制值的列表,然后将它们串在一起,每个前缀以\x
printf 为前缀。 ^
然后在此字符串之前添加以构建所需的正则表达式。
如果您$1
是unicode,则这会变得相当困难,因为通过所输出的字符与十六进制字节之间没有1:1的对应关系od
。
作为过滤器:
perl -ne 'BEGIN {$pat = shift} print if /^\Q$pat/' search-pattern
在一个或多个文件上运行:
perl -ne 'BEGIN {$pat = shift} print if /^\Q$pat/' search-pattern file..
引用元字符
在Perl反斜杠元字符是字母数字,例如
\b
,\w
,\n
。与其他一些正则表达式语言不同,没有不是字母数字的反斜杠符号。所以,什么是像\\
,\(
,\)
,\[
,\]
,\{
,或\}
总是被解释为一个文字字符,而不是一个元字符。曾经在一个常见的习惯用法中使用它来禁用或引用要用于模式的字符串中正则表达式元字符的特殊含义。只需引用所有非“单词”字符:$pattern =~ s/(\W)/\\$1/g;
(如果
use locale
已设置,则取决于当前的语言环境。)如今,更常见的是使用quotemeta
函数或元\Q
引号转义序列来禁用所有元字符的特殊含义,如下所示:/$unquoted\Q$quoted\E$unquoted/
请注意,如果在
\Q
和之间放置文字反斜杠(那些不在插补变量中)\E
,则双引号反斜杠插补可能会导致混淆的结果。如果需要在其中使用文字反斜杠\Q...\E
,请参阅perlop中的“解析引用结构的详细信息”。
quotemeta
并\Q
在quotemeta中进行了充分描述。
如果有不使用的字符,则可以使用该字符标记行的开头。例如$'\a'
(ASCII 007)。很难看,但是可以用:
{ echo 'this is a line to match'; echo 'but this is not'; } >file.txt
stuffing=$'\a' # Guaranteed never to appear in your source text
required='this' # What we want to match that beginning of a line
match=$(sed "s/^/$stuffing/" file.txt | grep -F "$stuffing$required" | sed "s/^$stuffing//")
if [[ -n "$match" ]]
then
echo "Yay. We have a match: $match"
fi
如果不需要匹配的行,则可以删除尾随sed
并使用grep -qF
。但是使用awk
(或perl
)要容易得多...
当您要查找不带循环
的文件时,可以使用:使用搜索字符串的长度剪切文件
cut -c1-${#1} < file
查找固定的字符串并返回行号
grep -Fn "$1" <(cut -c1-${#1} < file)
将行号用于类似 sed -n '3p;11p' file
sed -n "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed 's/:.*/p;/' | tr -d '\n')" file
当您要删除这些行时,请使用
sed "$(grep -Fn "$1" <(cut -c1-${#1} < file) | sed 's/:.*/d;/' | tr -d '\n')" file
grep '^$1'
?还是不是要防止$1
外壳扩展外壳程序?