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")
我正在尝试 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")