【问题标题】:How to preserve new lines while printing to a text file in shell?如何在 shell 中打印到文本文件时保留新行?
【发布时间】:2016-07-08 06:27:46
【问题描述】:

我必须在 txt 文件中打印出一些值。 它们具有以下格式

input="Sno;Name;Field1;Field2"

但是输出必须是:

Sno-Name
FIELDS ALLOCATED:
Field1
Field2

我是这样做的:

 echo $input | $(awk -F';' '{print $1"-"$2}') >>$txtfile
 echo "FIELDS ALLOCATED:">>$txtfile
 echo "$input" | cut -d';' -f 3,4 >>$txtfile 

这很容易。但是,问题是 Field1 或 Field2 可以包含新行。每当发生这种情况时,cut 或 awk 都不会读取字段编号 4 并将其视为新行。请帮助我如何从给定的输入格式打印两个字段(保留新行)。

【问题讨论】:

  • 字段数是固定的吗?您对@tripleee 的评论暗示不是。我问的原因是,否则似乎没有字段分隔符。在您的第一个示例中,您在字段之间显示分号,但在第二个示例中,您显示换行符。如果换行符是字段分隔符,而您不知道有多少字段,那么如何判断一个字段何时结束,下一个字段何时开始?
  • @cdarke 输入有“;”作为分隔符,我现在必须将其转换为用新行分隔的输出,同时还要保留原始换行符。如您所见,Field1、Field2 位于彼此下方,因此与多少字段无关。例如,Field1: xxx \n yyy Field2: zzzz \n aaaa 它应该打印 xxx \n yyy \n zzzz \n aaaa
  • 嗯,好的,所以输入字段分隔符是分号。

标签: shell awk


【解决方案1】:

如果输入格式正确,则可以收集输入行,直到有四个字段。

awk -F ';' 'r { $0 = r ORS $0 }
    NR<4 { next }
    {   print $1 "-" $2
        print "FIELDS ALLOCATED:"
        print $3; print $4
        print ""; r="" }' file

【讨论】:

  • $0 = r 意味着什么?如果输入可以有多个字段 FIELD1,FIELD2,FIELD3 而我们无法确定有多少?
  • 赋值$0 = r ORS $0将当前行粘合到r的末尾,前面有一个换行符,并重新计算字段计数;所以我们在r 中收集行,直到它包含四个字段。
  • 如果您不知道其中有多少个字段并且其中一些可能包含换行符,则无法解析您的数据,因为格式不明确。也许有例如一种允许我们识别最后一个字段的模式,但是根据您提供的信息,您的问题无法解决可变字段长度的问题。
  • 如果你在打开文件时知道有多少字段,你可以使用awk -v f=4 '... NF&lt;f { next } ...}
  • ... 虽然如果您知道您的输入恰好是一条记录,那么这又很容易。我写这个答案的假设是你有一个分号分隔的输入文件,它可能包含多个记录,但如果它总是恰好一个,那应该是合理的。
【解决方案2】:

单个gnu-awk 可以用FPAT 和空RS 完成工作:

input=$'Sno;Name;Field1\nFoo;Field2'

awk -v RS= -v FPAT='[^;]+' '{
    printf "%s-%s\nFIELDS ALLOCATED:\n%s\n%s\n", $1, $2, $3, $4}' <<< "$input"

Sno-Name
FIELDS ALLOCATED:
Field1
Foo
Field2

【讨论】:

  • 这不能处理字段值中嵌入的换行符,因为默认情况下 awk 一次读取一行。
  • @anubhava 如果两个换行符在一起,它仍然会中断。
  • IMO 任何 CSV 都必须定义记录分隔符。相同的\n 不能作为记录分隔符,也不能出现在未引用的单元格值中。
  • 我尝试了相同的命令,复制粘贴,它不起作用,我得到Sno;Name;Field1-Foo;Field2 和下一行:FIELDS ALOOCATED
  • FPAT 仅在 gnu-awk 4.0+ 中可用,可能 op 没有。
【解决方案3】:

只需更改awk - RS 中的输入记录分隔符。为了清楚起见,在每个字段周围添加了 。

编辑:通过添加 ';' 删除额外的尾随换行符在 here-doc 数据的末尾,加上另一个条件。

input="Sno;Name;Fie
ld1;Fi
eld2"

awk 'BEGIN{RS=";"} NR==1{f1=$0}; 
     NR==2{print f1 "-" $0; print "FIELDS ALLOCATED:"}
     $0=="\n"{next}
     NR>2{print "<" $0 ">"}' <<< "$input;"

给予:

Sno-Name
FIELDS ALLOCATED:
<Fie
ld1>
<Fi
eld2>

【讨论】:

  • 为什么field2末尾多了一个换行符?
  • 不确定是 here-doc 还是 awk,但很好(编辑)。
  • 这不是这里的字符串。必须是 awk。
  • 在您的帮助下我得到了解决方案,谢谢,但是我做的有点不同
【解决方案4】:
input=$'Sno;Name;Field1\nFoo;Field2'

awk 'BEGIN{ RS = "\n\n+" ; FS = ";" } { print $1"-"$2; for(i=3;i<=NF;i++) {print $i}}' <<<"$input"

由于它不知道我可以给出多少字段,所以我添加了一个 for 循环直到 NF 并将 RS 更改为空行而不是换行符。

【讨论】:

    猜你喜欢
    • 2015-11-03
    • 1970-01-01
    • 1970-01-01
    • 2021-06-17
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多