在for循环中使用grep提取特定行时如何保留换行格式
多文件提取指定行时换行丢失的解决办法
需要从多个文件中提取带行号的行,满足两个条件:
- 行内容包含
ClNonZ - 第二个字段为
REAL
示例文件内容
foo1.txt
A TEST 0.959660297 0 0.021231423 -0.0073 -0.0031 MhZisp B REAL 0.180467091 0.800424628 0 0.0566 0.0103 ClNonZ C REAL 0.98089172 0 0 -0.0158 0.0124 MhNonZ D TEST 0.704883227 0.265392781 0.010615711 -0.0087 -0.0092 MhZisp E REAL 0.010615711 0.959660297 0.010615711 0.0476 0.0061 ClNonZ F TEST 0.704883227 0.265392781 0.010458211 0.0865 0.0548 ClNonZ
foo2.txt
A TEST 0.715498938 0 0.265392781 -0.0013 -0.0309 Unkn B REAL 0.927813163 0 0.053078556 -0.0051 -0.0636 MhZisp C TEST 0.55626327 0.222929936 0.201698514 0.0053 -0.0438 MhZisp D REAL 0.492569002 0.350318471 0.138004246 0.0485 0.0088 ClNonZ E REAL 0.704883227 0.265392781 0.010615711 0.0476 0.0061 AbbbbZ F REAL 0.180467091 0.800424628 0 0.0566 0.0103 ClNonZ
单个文件处理(结果正常换行)
直接对单个文件执行命令,结果会正常换行显示:
$ grep -n ClNonZ foo1.txt | awk '$2 == "REAL" {print $0}' 2:B REAL 0.180467091 0.800424628 0 0.0566 0.0103 ClNonZ 5:E REAL 0.010615711 0.959660297 0.010615711 0.0476 0.0061 ClNonZ
使用for循环处理(换行丢失)
用for循环遍历文件时,把命令结果赋值给变量后再输出,会导致换行丢失,所有匹配行挤在一行:
$ for file in `ls foo*` ; do line=`grep -n ClNonZ $file | awk '$2 == "REAL" {print $0}' `; if [[ -n "$line" ]]; then echo $file ; echo $line ; echo " " ; fi ; done foo1.txt 2:B REAL 0.180467091 0.800424628 0 0.0566 0.0103 ClNonZ 5:E REAL 0.010615711 0.959660297 0.010615711 0.0476 0.0061 ClNonZ foo2.txt 4:D REAL 0.492569002 0.350318471 0.138004246 0.0485 0.0088 ClNonZ 6:F REAL 0.180467091 0.800424628 0 0.0566 0.0103 ClNonZ
解决方法
方法1:修正变量赋值后的输出
问题根源是bash在把命令结果赋值给变量时,会将换行符替换成空格。只要在输出变量时用双引号包裹,就能保留换行:
for file in foo*; do line=$(grep -n ClNonZ "$file" | awk '$2 == "REAL"') if [[ -n "$line" ]]; then echo "$file" echo "$line" echo " " fi done
方法2:改用while循环遍历文件
如果想用while循环,正确的写法如下,避免ls的输出问题,同时保留换行:
printf "%s\n" foo* | while read -r file; do line=$(grep -n ClNonZ "$file" | awk '$2 == "REAL"') if [[ -n "$line" ]]; then echo "$file" echo "$line" echo " " fi done
方法3:用awk一次性处理所有文件(最优解)
不需要写循环,直接用awk处理所有目标文件,同时满足两个条件,还能自动处理换行和文件名:
awk ' /ClNonZ/ && $2 == "REAL" { if (CURRENT_FILE != FILENAME) { print FILENAME CURRENT_FILE = FILENAME } print NR ":" $0 print "" } ' foo*
执行后会输出和单个文件处理一致的换行格式,同时自动遍历所有foo*文件。
内容的提问来源于stack exchange,提问作者anton
相关产品推荐
相关产品推荐

