Bash脚本:从文件打印grep' d行

时间:2015-04-05 01:39:51

标签: bash grep

我正在尝试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不是一个字符串数组(在我的例子中是行)?

2 个答案:

答案 0 :(得分:3)

  

Isn&#t; t $排列一个字符串数组(在我的情况下是行)?

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

  

为什么案例1不会在一行上打印整行而不是在每一行上打印该行中的单个单词?

因为您引用了变量$lines 未加引号的,这意味着它受分词(以及其他所谓的{{3)的影响}})。

单词拆分意味着输入按空格(甚至跨行)拆分为标记,每个标记分别传递给for循环。


使用单个输入字符串,即使您将$IFS设置为$ '\n',也无法使用for对其行进行迭代,因为这些行仍然是主题到路径名扩展(globbing);即,如果一行包含恰好是有效的glob(文件名模式,例如*)的子字符串,它将被扩展为匹配的文件名。

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


您有两个选择,两个选项都使用shell expansions来捕获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")

答案 1 :(得分:1)

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

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

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

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

请注意,您现在已经为shell更改了IFS,并且您可能希望将其重置为旧值。正如您所看到的,这种方法很可能不是您问题的最佳方法,但我在此处发布以回答您的原始问题