我正在尝试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
不是一个字符串数组(在我的例子中是行)?
答案 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
,并且您可能希望将其重置为旧值。正如您所看到的,这种方法很可能不是您问题的最佳方法,但我在此处发布以回答您的原始问题