【问题标题】:sed repeat string on every linesed 在每一行重复字符串
【发布时间】:2013-01-11 14:04:33
【问题描述】:

我有一个文件inventory.txt,其中包含数百行。它列出了与客户 ID/名称、库存 ID/名称和产品 ID/名称相关的数据。在任何给定行上可能会出现customerId=123 的文件的一般设置。在此行之后,将出现 inventoryId=abc 行。这个文件看起来像这样:

<> START OF FILE
Customer ID=9000, Customer Name=Acme, Inc
Inventory ID=INV_ID1, Inventory Name=Acme_INV1
Product ID=100, Product Name=Banana
Product ID=200, Product Name=Apple
Inventory ID=INV_ID2, Inventory Name=Acme_INV2
Product ID=100, Product Name=Banana
Product ID=300, Product Name=Kiwi
Customer ID=7500, Customer Name=Anvil, Corp
Inventory ID=INV_ID3, Inventory Name=Anvil_INV1
Product ID=200, Product Name=Apple
<> END OF FILE

我想使用 SED 或任何效果很好的替代方法来创建一个 CSV 格式的文件,其中包含每个客户/库存组合的单行数据,其中仅包括客户 ID/名称和库存ID/名称字段。所以输出看起来像:

"9000", "Acme, Inc.", "INV_ID1", "Acme_INV1"
"9000", "Acme, Inc.", "INV_ID2", "Acme_INV2"
"7500", "Anvil, Inc.", "INV_ID3", "Anvil_INV1"

我了解如何使用 SED 将输入数据格式化为带有逗号和引号的 CSV 文件输出,但我无法弄清楚如何强制 Customer IDCustomer Name 在每个开头重复Inventory IDInventory Name 行。

【问题讨论】:

  • 这在 sed 中很棘手,而且可能是不可能的。我一直在尝试设想一种方法来使用 sed 的“hold space”来保留您的客户 ID 和名称的副本,然后对于每个 Inventory 行,将相关数据附加到 hold空间 并打印,但似乎任何使用 保留空间也会修改它,因此对于特定客户的第二个(或更多)库存行使其无效。虽然在sed 中努力解决此问题的方法很有趣且具有教育意义,但如果您只是想完成工作,steve 的 awk 单线看起来会奏效。

标签: linux bash shell sed grep


【解决方案1】:

这是使用awk的一种方式:

awk -F= '{ sub(/,.*/,"",$2) } /^Customer ID/ { r = $2 OFS $3 } /^Inventory ID/ { print "\"" r, $2, $3 "\"" }' OFS="\", \"" inventory.txt

sed 解决方案:

sed -n '/^Customer ID/ h; /^Inventory ID/ { G; s/.*=\([^,]*\).*=\([^\n]*\).*=\([^,]*\).*=\(.*\)/"\3", "\4", "\1", "\2"/; p }' inventory.txt

结果:

"9000", "Acme, Inc", "INV_ID1", "Acme_INV1"
"9000", "Acme, Inc", "INV_ID2", "Acme_INV2"
"7500", "Anvil, Corp", "INV_ID3", "Anvil_INV1"

awk解释:

 OFS="\", \""          # set the output field separator to: ", "

-F=                    # split the line into three fields using the '=' character

{ sub(/,.*/,"",$2) }   # one each line of input, remove everything trailing a
                       # comma from field two.

/^Customer ID/ { ... } # if the line starts with 'Customer ID'; do

r = $2 OFS $3          # build a record using field two and three separated by 'OFS'

/^Inventory ID/ {...}  # if the line starts with 'Inventory ID'; do

print "\"" r, $2, $3 "\""   # print out a double-quote, the record, OFS, $2, OFS, 
                            # $3 and lastly a double quote

sed解释:

使用-n 标志禁用默认打印。当一行以“客户 ID”开头时,复制该行以保留空间。当找到以“Inventory ID”开头的行时,将保留空间附加到当前行。使用一些神奇的正则表达式重新排列不同的字段并修复格式。

【讨论】:

  • 您应该考虑客户名称有逗号。
  • 该死,你又打败了我。 +1.
  • 您能评论一下您的代码在做什么吗?我对 BASH 脚本比较陌生。提前致谢!
  • 当然,我会在一分钟内添加有关awk 解决方案的说明。但如您所见,我添加了sed 解决方案。我只是想看看我是否可以先减少正则表达式以使其更具可读性。我认为可以做到。
  • 这太棒了!!非常感谢您提供 2 个解决方案。我正在为我的需要配置 SED 解决方案。我非常感谢您的帮助!! :)
【解决方案2】:

Perl 解决方案:

#!/usr/bin/perl
use warnings;
use strict;
use feature qw(say);

my ($customer, $name);
while (<>) {
    if (/Customer ID=(.*), Customer Name=(.*)/) {
        ($customer, $name) = ($1, $2);
    } elsif (/Inventory ID=(.*), Inventory Name=(.*)/) {
        say join ', ' => map qq("$_"), $customer, $name, $1, $2;
    }
}

【讨论】:

    【解决方案3】:

    另一个不使用 FS 的 awk 单行

    awk -vq="\"" '/^(Cus|Inv)/{f=$0~/^Cus/;gsub(/[^,]*=/,q);sub(/,/,q",");c=f?$0q:c;if(!f)print c","$0q}' file
    

    测试:

    kent$  echo "Customer ID=9000, Customer Name=Acme, Inc
    Inventory ID=INV_ID1, Inventory Name=Acme_INV1
    Product ID=100, Product Name=Banana
    Product ID=200, Product Name=Apple
    Inventory ID=INV_ID2, Inventory Name=Acme_INV2
    Product ID=100, Product Name=Banana
    Product ID=300, Product Name=Kiwi
    Customer ID=7500, Customer Name=Anvil, Corp
    Inventory ID=INV_ID3, Inventory Name=Anvil_INV1
    Product ID=200, Product Name=Apple"|awk -vq="\"" '/^(Cus|Inv)/{f=$0~/^Cus/;gsub(/[^,]*=/,q);sub(/,/,q",");c=f?$0q:c;if(!f)print c","$0q}'                                   
    "9000","Acme, Inc","INV_ID1","Acme_INV1"
    "9000","Acme, Inc","INV_ID2","Acme_INV2"
    "7500","Anvil, Corp","INV_ID3","Anvil_INV1"
    

    【讨论】:

      【解决方案4】:

      这可能对你有用(GNU sed):

      sed -r '/^Customer/{h;d};/^Inventory/!d;G;s/.*=([^,]*).*=([^\n]*).*=([^,]*).*=(.*)/"\3", "\4", "\1", "\2"/' file
      

      【讨论】:

        【解决方案5】:

        match() 函数使用 gawk 扩展

        gawk '
            match($0, /^Customer ID=([^,]+), Customer Name=(.*)/, cust) {
                c_id=cust[1]; c_name=cust[2]
                next
            }
            match($0, /^Inventory ID=([^,]+), Inventory Name=(.*)/, inv) {
                printf "\"%s\",\"%s\",\"%s\",\"%s\"\n", c_id, c_name, inv[1], inv[2]
            }
        ' filename
        

        输出

        "9000","Acme, Inc","INV_ID1","Acme_INV1"
        "9000","Acme, Inc","INV_ID2","Acme_INV2"
        "7500","Anvil, Corp","INV_ID3","Anvil_INV1"
        

        【讨论】:

          猜你喜欢
          • 2011-02-20
          • 2021-08-19
          • 2013-09-13
          • 2011-04-28
          • 1970-01-01
          • 2015-04-12
          • 1970-01-01
          • 2018-12-07
          • 1970-01-01
          相关资源
          最近更新 更多