Bash 脚本:从文件中打印 grep 的行

Bash Script: printing grep'd lines from file

我正在尝试 grep 匹配某些模式的行,然后尝试打印那些匹配的行。

#!/bin/bash

file=/path/to/some/file
pattern=socket
if [ -f $file ]; then
    lines=`grep -i "$pattern" $file`
# Case 1
    for x in $lines; do   # <--- isn't this an array
        echo "$x"                                                                                                                                                                                                                                                                
        done
# Case 2
    while read -r line_a; do
        echo "$line_a"
        done <<< "$lines"
fi

输出:
情况 1:不是完整的行,而是在每一行上打印这些行中的单个单词。
情况 2:打印单行。

问题:
为什么案例 1 不在一行上打印整行,而是在每一行上打印该行中的单个单词? $lines 不是一个字符串数组(在我的例子中是行)吗?

您当前正在使用将整个输出视为一个大字符串的反引号捕获输出。如果要将其捕获为数组,请使用以下表示法

lines=($(grep -i "$pattern" $file))

但是,默认的记录分隔符是空格,因此每个数组元素都是一个单词,而不是 grep 输出中的整行。您可以通过(暂时)更改记录分隔符 IFS 以在换行符上拆分来避免这种情况。整个解决方案如下所示

IFS=$'\n'
lines=($(grep -i "$pattern" $file))
for x in ${lines[@]}; do
    echo $x
done

请注意,您现在已经为 shell 更改了 IFS,您可能希望将其重置为旧值。如您所见,这种方法很可能不是最适合您的问题的方法,但我将其发布在这里是为了回答您原来的问题

Isn't $lines an array of strings (lines in my case)?

没有; $lines 是一个 标量 字符串变量,它包含从命令 grep -i "$pattern" $file 捕获的 整个输出 - 换句话说:单个可能包含多行的字符串。

Why doesn't case 1 print the whole line on one line instead of printing individual words from that line on each new line?

因为您正在引用变量 $lines 未加引号,这意味着它受到 分词(以及其他所谓shell expansions).

分词意味着输入被空格(甚至跨行)分割成标记,每个标记被单独传递给for循环。


对于单个输入字符串,即使将 $IFS 设置为 $'\n',也没有安全的方法用 for 遍历它的行,因为这些行是仍然受到路径名扩展(通配);即,如果一行包含恰好是有效 glob 的子字符串(文件名模式,例如 *),它将扩展为匹配的文件名。

for 循环中使用 数组 行确实有效,但需要用 未修改的 输入填充线路;出于与上述相同的原因,使用 lines=($(grep -i "$pattern" "$file")) 填充数组不是一种选择。


您有两个选择,都使用 process substitution 来捕获 grep 命令的输出:

(a) 如果您确实需要预先将所有行读入内存,请将它们稳健地读入数组,如下所示:

IFS=$'\n' read -d '' -ra lines < <(grep -i "$pattern" "$file") 

在 bash 4+ 中,您可以使用 readarray -t lines ... 代替。

然后在 for 循环中按如下方式处理它们:

 for line in "${lines[@]}"; do # double quotes prevent word splitting and globbing
    echo "$line"
 done 

(b)否则,使用while循环直接逐行读取grep的输出:

while IFS= read -r line; do
    echo "$line"
done < <(grep -i "$pattern" "$file")