【问题标题】:How do I write a sed script to grep information from a text file如何编写 sed 脚本以 grep 文本文件中的信息
【发布时间】:2012-02-03 18:35:35
【问题描述】:

我正在尝试完成仅限于使用sed 将输入文件过滤为某种输出格式的作业。这是输入文件(名为stocks):

Symbol;Name;Volume
================================================

BAC;Bank of America Corporation Com;238,059,612
CSCO;Cisco Systems, Inc.;28,159,455
INTC;Intel Corporation;22,501,784
MSFT;Microsoft Corporation;23,363,118
VZ;Verizon Communications Inc. Com;5,744,385
KO;Coca-Cola Company (The) Common;3,752,569
MMM;3M Company Common Stock;1,660,453

================================================

输出需要是:

BAC, CSCO, INTC, MSFT, VZ, KO, MMM

我确实想出了一个解决方案,但效率不高。这是我的sed 脚本(命名为try.sed):

/.*;.*;[0-9].*/ { N
N
N
N
N
N
s/\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*\n\(.*\);.*;.*/\1, \2, \3, \4, \5, \6, \7/gp
}

我在 shell 上运行的命令是:

$ sed -nf try.sed stocks

我的问题是,有没有更好的方法使用 sed 来获得相同的结果?我编写的脚本仅适用于 7 行数据。如果数据较长,我需要重新修改我的脚本。我不知道如何才能让它变得更好,所以我在这里寻求帮助!

感谢您的任何建议。

【问题讨论】:

  • +1 承认这是家庭作业,以及你放在那里的那个疯狂的 s/\(.*\);....../ 东西!祝你好运。

标签: linux sed append newline


【解决方案1】:

使用sed的另一种方式:

sed -ne '/^====/,/^====/ { /;/ { s/;.*$// ; H } }; $ { g ; s/\n// ; s/\n/, /g ; p }' stocks

输出:

BAC, CSCO, INTC, MSFT, VZ, KO, MMM

解释:

-ne               # Process each input line without printing and execute next commands...
/^====/,/^====/   # For all lines between these...
{
  /;/             # If line has a semicolon...
  { 
    s/;.*$//      # Remove characters from first semicolon until end of line.
    H             # Append content to 'hold space'.
  }
};
$                 # In last input line...
{
  g               # Copy content of 'hold space' to 'pattern space' to work with it.
  s/\n//          # Remove first newline character.
  s/\n/, /g       # substitute the rest with output separator, comma in this case.
  p               # Print to output.

【讨论】:

  • 哇,谢谢Birei!我不知道我可以做 double {} 并且我忘记了我可以使用替换 w/o g 命令来匹配第一次出现的匹配。我这里还有几个问题。 1.为什么最后一个块在最后一行模式($)上? 2. 对于新行的第二次替换。它的目的是删除空行吗? 2. 最后一个换行符,怎么没有把“MMM”后面的换行符换行?你给了我一个很好的解释,但我仍然不明白 ${} 的目的。希望你能帮助我更多地理解它。非常感谢您的帮助!!
  • @Jaycee:[1] 我在文件处理过程中将所需的字符串保存在“保留空间”中,并且仅在最后一行恢复该内容,修改并打印。 [2] H 命令将\n 加上'pattern space' 的内容添加到'hold space',所以最后一行的内容会像\nBAC\nCSCO\nINTC\nMSFT\nVZ\nKO\nMMM。然后我首先删除 \n 并将其余部分替换为 ,
  • 啊……我现在明白了!!!!非常感谢!!!!!使用 H 和 g.... =) 不知道为什么我的老师没有教我们这些命令。再次感谢你!!!!! ^O^
【解决方案2】:

编辑:我已经编辑了我的算法,因为我忽略了页眉和页脚(我认为它们只是为了我们的利益)。

sed,按照它的设计,访问输入文件的每一行,然后对匹配某个规范(或不匹配)的行执行表达式。如果您将脚本定制为一定数量的行,那么您肯定做错了什么!因为这是家庭作业,所以我不会为您编写脚本,但一种方法的总体思路是编写执行以下操作的脚本。将排序想象成脚本中应该存在的顺序。

  1. 使用d 跳过前三行,这会删除模式空间并立即移至下一行。
  2. 对于不是空行的每一行,请执行以下步骤。 (这都在一组花括号中。)
    1. 使用s(替代)命令将第一个分号 (;) 之后的所有内容替换为逗号和空格 (", ")。
    2. 将当前模式空间追加到hold buffer(查看H)。
    3. 删除模式空间并移至下一行,如步骤 1 所示。
  3. 对于脚本中到达该点的每一行(应该是第一个空行),将保持空间的内容检索到模式空间中。 (这将在上面的花括号之后。)
  4. 将模式空间中的 all 换行替换为空。
  5. 接下来,将模式空间中的最后一个逗号和空格替换为空。
  6. 最后,退出程序,不再处理任何行。我的脚本没有这个就可以工作,但我不能 100% 确定原因。

话虽如此,这只是解决问题的一种方法。 sed 通常提供不同复杂度的不同方法来完成任务。我用这种方法写的一个解决方案是 10 行长。

请注意,我不打扰抑制打印(使用-n)或手动打印(使用p);默认情况下打印每一行。我的脚本是这样运行的:

$ sed -f companies.sed companies 
BAC, CSCO, INTC, MSFT, VZ, KO, MMM

【讨论】:

  • @Jaycee 您在上面的哪个部分遇到了问题?如果可以的话,我想改进我的解释!
  • 嗨,丹,感谢您的提示。第一步,我会得到所有带有逗号和空格的符号。但我在做第二步时遇到了麻烦。如何获得不是最后一行的每一行?从技术上讲,MMM 不是最后一行。 ============ 是最后一行。我很困惑,真的不知道如何进行。能不能详细一点???非常感谢您的帮助!
  • 我可以得到最后一个如下: /[0-9]$/ { N N s/(.*);.*;.*\n\n\=*/\1/ GP }
  • 而第一步如下:/[0-9]$/ { s/(.*);.*;.*/\1, /g }
  • 问题是......我仍然无法将所有部分拼凑在一起。 =(
【解决方案3】:

这个 sed 命令应该会产生你需要的输出:

sed -rn '/[0-9]+$/{s/^([^;]*).*$/\1/p;}' file.txt

或在 Mac 上:

sed -En '/[0-9]+$/{s/^([^;]*).*$/\1/p;}' file.txt

【讨论】:

  • 亲爱的anubhava,我已经运行了你的命令,但输出不在一行中。挑战之一是将所有换行符替换为逗号和 1 个空格,最后一行除外。最后一个后面不能有逗号。
  • 是的,我的脚本的行为与 grep -o 完全一样,因为现在我意识到这是一个家庭作业,我会将脚本的其余部分留给你。
  • @Jaycee 您回答的另一个提示是使用他的回答中建议的标签(如@Dan)以及使用N替换 newlines 和@ 987654325@和space
  • 感谢 Jaypal 和 Anubhava... 我正在尝试阅读有关 b 和 t 命令标签的更多信息。虽然,我还不太明白t在做什么......
【解决方案4】:

这可能对你有用:

sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;q};d' stocks
  • 我们不想要这些标题,所以让我们删除它们。 1d
  • 所有数据项都由; 分隔,所以让我们专注于这些行。 /;/
  • 在上面的内容中,删除从第一个 ; 到行尾的所有内容,然后将其塞入保存空间 (HS) {s/;.*//;H}
  • 当您到达最后一行时,使用g 命令用HS 覆盖它,删除第一个换行符(由H 命令生成),用逗号和空格替换所有后续换行符并打印输出还剩下什么。 ${g;s/.//;s/\n/, /g;q}
  • 删除其他所有内容d

这是一个终端会话,展示了构建 sed 命令的增量改进:

cat <<! >stock # paste the file into a here doc and pass it on to a file
> Symbol;Name;Volume
> ================================================
> 
> BAC;Bank of America Corporation Com;238,059,612
> CSCO;Cisco Systems, Inc.;28,159,455
> INTC;Intel Corporation;22,501,784
> MSFT;Microsoft Corporation;23,363,118
> VZ;Verizon Communications Inc. Com;5,744,385
> KO;Coca-Cola Company (The) Common;3,752,569
> MMM;3M Company Common Stock;1,660,453
> 
> ================================================
> !
sed '1d;/;/!d' stock # delete headings and everything but data lines
BAC;Bank of America Corporation Com;238,059,612
CSCO;Cisco Systems, Inc.;28,159,455
INTC;Intel Corporation;22,501,784
MSFT;Microsoft Corporation;23,363,118
VZ;Verizon Communications Inc. Com;5,744,385
KO;Coca-Cola Company (The) Common;3,752,569
MMM;3M Company Common Stock;1,660,453
sed '1d;/;/{s/;.*//p};d' stock # delete all non essential data
BAC
CSCO
INTC
MSFT
VZ
KO
MMM
sed '1d;/;/{s/;.*//;H};${g;l};d' stock # use the l command to see what's really there!
\nBAC\nCSCO\nINTC\nMSFT\nVZ\nKO\nMMM$
sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;l};d' stock # refine refine
BAC, CSCO, INTC, MSFT, VZ, KO, MMM$
sed '1d;/;/{s/;.*//;H};${g;s/.//;s/\n/, /g;q};d' stock # all done!
BAC, CSCO, INTC, MSFT, VZ, KO, MMM

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-04
    • 1970-01-01
    • 2022-01-12
    • 2021-08-31
    • 1970-01-01
    • 2017-03-31
    • 1970-01-01
    • 2016-07-22
    相关资源
    最近更新 更多