从文件中剪切空行(bash脚本)

从文件中剪切空行(bash脚本)

我试图删除文件中的所有空行,但我确实想在每个非空行之后保留“\n”。

问题:如果在 CLI 中使用,命令可以正常工作,但只要我在 bash 脚本中使用任何命令,它就会删除所有“\n”,因此我将所有结果放在一行中,而不是将它们放在单独的行中。

这是我的代码:

#printing second and third word from every line and remove lines that do not contain any digits
    result=$(cat "$output_file" | awk '{print $2" "$3}' | sed 's/[^0-9]*/\\n/')
    echo -e ""$result"" > "$output_file"

#getting rid of all empty lines but what happens is that the whole file becomes one line
    no_empty_lines=$(cat "$output_file" | awk NF)
    echo -e ""$no_empty_lines"" > "$output_file"

要编辑的文件:

> 135.121.62.246 7.4
> 135.121.160.65 7.8
> 135.121.106.56 7.5
>  
>  
> 135.121.106.96 6.2
>  
>  
> 135.121.160.106 10
>   
> 135.121.90.46 命令失败

要求的结果:

要编辑的文件:

> 135.121.46.246 7.4
> 135.121.106.46 7.8
> 135.121.106.56 7.5
> 135.121.106.96 6.2  
> 135.121.160.16 10
> 135.121.90.46 命令失败

答案1

您可以匹配至少包含一个字符的行:

grep . {file}

将其放入替换相关文件的代码中。我们创建一个临时文件,如果创建成功,则用临时文件替换原始文件。最后我们删除临时文件,以防它无法成功替换原始文件。

file=some_file.txt
grep . "$file" >"$file.tmp.$$" && mv -f "$file.tmp.$$" "$file"
rm -f "$file.tmp.$$"

顺便说一句,这就是您在原始代码中丢失换行符的原因:

result=$(cat "$output_file" | awk '{print $2" "$3}' | sed 's/[^0-9]*/\\n/')
echo -e ""$result"" > "$output_file"

$result变量正确包含文本,包括换行符。 (这是一条效率低下的生产线,但我们在它工作时忽略这个问题。)

不过,这条echo线确实很奇怪。我不明白为什么你在""那里 - 它代表一个零长度的带引号的字符串,并且同样可以有效地被删除,留下这个:

echo -e $result > "$output_file"

然后 shell 评估 的内容$result,将空格字符串转换为单个空格。在这种情况下,选项卡和换行符被视为空白。 (hello whole\nworld被读作hello whole world。)

如果您在使用变量时用双引号引起来,则不会出现此问题

echo -e "$result" > "$output_file"

答案2

您的代码已改进:

awk -i inplace '$2 ~ /[0-9]/ || $3 ~ /[0-9]/ { print $2, $3 }' "$output_file"

这假设您使用的是 GNU awk4.1.0 或更高版本(对于该-i inplace选项)。该代码从至少其中一个字段包含数字的任何行中提取第二个和第三个字段。

没有 GNU awk

tmpfile=$(mktemp)
cp "$output_file" "$tmpfile"
awk '$2 ~ /[0-9]/ || $3 ~ /[0-9]/ { print $2, $3 }' "$tmpfile" >"$output_file"
rm -f "$tmpfile"

该程序的另一种表述awk是重置$0第二和第三字段,然后对数字进行测试:

awk -i inplace '{ $0 = $2 " " $3 }; /[0-9]/' "$output_file"

您的代码中有很多问题。您自己提到的事情,最后将所有行都放在一行中,是由于使用了$result不带引号的值echo。扩展$result未加引号,因为无论出于何种原因,您在扩展 的两侧使用两组双引号(两个空字符串)""$result""

当您使用不带引号的变量扩展时,shell 将获取变量的值并将其拆分为任何空格、制表符或换行符以创建多个单词。然后每个单词都会进行文件名通配。然后在代码中使用生成的单词echo -e,该代码输出每个参数,它们之间有空格,最后有一个换行符。

此外,您不需要将命令的输出放入变量中。在这种情况下,只需重定向到文件就可以了。

您的sed命令会在每行的开头插入字符串\n,替换恰好位于该行第一行的所有非数字。它不会删除不包含数字的行。为此,请使用sed表达式/[0-9]/!d.但只要您只输出awk包含数字的脚本行(我上面的代码就是这样做的),就不需要这样做。

令人惊讶的是,管道输入awksed相反的情况并不常见。 能做的awk事都绰绰有余。sed

答案3

您的代码的问题是,您将结果保存在bash变量中:

 no_empty_lines=$(cat "$output_file" | awk NF)

其中(跳过多余的cat),可以看作:

 result=$(command that returns multi-line data)

但是,bash将多行字符串转换为带空格的单行。

可能的方法有这里- 我认为这是您所需要的,但是使用bash,您的结果可能是一个数组:

 no_empty_lines=( $(awk 'NF' "$output_file") )

现在的条目是${no_empty_lines[0]}, ${no_empty_lines[1]}, ...

用循环调用它们

 for ((i=0;i<=${#no_empty_lines[@]}-1;i++)) ; do echo ${no_empty_lines[i]} ; done

再说一遍 - 这只是为了向您展示代码失败的原因bash,我建议使用上述线程中的选项之一。:这个数组会将任何单词放入数组的单独元素中 - 从而完全删除输入的换行结构。

答案4

在@roaima的帮助下,我能够缩小问题范围,

在您的答案中,结果正确保存多行数据。当它打印出来时,问题就出现了:变量没有被引用(rcho ""$result"" 与 echo $result 相同),因此 shell 将结果解析为多个单词,换行符被像任何其他空格一样对待– 罗艾玛 6 小时前

所以这是一个可行的解决方案:

result=$(cat "$output_file"| awk '{print $2" "$3}' | sed 's/[^0-9]*//')
echo -e "$result" | awk NF > "$output_file"

假设变量存储正确,我在回显“$result”时删除了额外的引号,然后将其通过管道传输到“awk NF”,该命令删除空行并将其输出到文件中。

现在结果看起来像这样:

> 135.121.9.256 6.2
> 135.121.160.50 7.5
> 135.121.106.10 10
> 135.121.9.66 命令失败
> 135.121.100.156 命令失败

相关内容