我有一个包含一些行的文本文件,并使用以下命令将文本文件保存到数组中
readarray -t array < Textfile
文本文件包含以下内容:
123
456
789
现在我想使用 grep 命令使用数组在另一个文本文件中查找结果,因此打印出出现“123”、“456”或“789”的行。为了测试数组,我尝试让 grep 在同一文本文件中查找“123”、“456”和“789”,并输出匹配的行(如果三个字符系列中至少有一个出现)。
我试过
grep "${array[*]}" Textfile
但这没有显示任何结果。我究竟做错了什么?
答案1
首先,您可以只使用文件本身。这比尝试使用 shell 数组要简单得多:
grep -f file1.txt file2.txt
这将打印与file2.txt
中任何行匹配的任何行file1.txt
。
如果你有由于某种原因使用数组,事情会变得更加复杂。您不能只是这样做,grep "${array[*]}" Textfile
因为"${array[*]}"
将扩展到数组中由空格分隔的元素列表:
$ array=("foo" "bar" "baz")
$ echo "${array[*]}"
foo bar baz
这意味着您的grep
命令将变为:
grep 'foo bar baz' file
foo bar baz
这意味着“在文件中查找file
”。你想做的是 grep foo
,或者 bar
,或者 baz
。这可以使用 -E 选项grep
并加入您想要搜索的模式来完成|
:
grep -E 'foo|bar|baz' file
为此,您需要执行一些复杂的操作,例如:
grep -E "$(printf '%s|' "${array[@]}" | sed 's/|$//')" file
也许:
grep -E "$(export IFS="|"; echo "${array[*]}")" file
总的来说,使用文件并忘记数组会更好、更快、更容易。
答案2
grep "${array[*]}" Textfile
只要您设置IFS
为换行符(或以换行符开头的任何内容),并使用--
或-e
确保它仍然有效,即使第一个元素以 开头,它就可以工作-
。
"${array[*]}"
在类似 Korn 的 shell 中,就像"$*"
在 POSIX shell 中一样,扩展为与 的第一个字符连接的元素列表$IFS
。$IFS
的默认值为<SPC><TAB><NL>
( <SPC><TAB><NL><NUL>
in zsh
),因此默认情况下,您将获得使用 SPC 字符连接的元素。对于grep
,您需要将不同的正则表达式以换行符分隔,以便grep
依次循环每个正则表达式。
IFS=$'\n'
grep -e "${array[*]}" file
在 中zsh
,执行grep -e "${(pj:\n:)array}" file
(显式j
使用换行符而不是$IFS
全局修改)或(以/样式grep -e$^array file
扩展数组,这成为 向 提供多种模式的另一种方式)会更干净。fish
rc
grep -efirst -esecond file
grep
另一种选择是执行以下操作:
printf '%s\n' "${array[@]}" | grep -f - file
grep
这次通过 的stdin 而不是通过参数传递以换行符分隔的模式列表。
-F
无论您的模式是固定字符串(使用)、扩展正则表达式(使用-E
)还是基本正则表达式(默认),这些方法都适用。
您可能需要确保模式列表不为空:
(( ${#array[@]} > 0 )) && grep ...
grep
使用空模式调用会产生不同的结果,具体取决于grep
实现,并且通常不是您想要的结果。