【问题标题】:Find, Replace or Insert - command Line查找、替换或插入 - 命令行
【发布时间】:2015-09-19 15:52:36
【问题描述】:

我有一个类似的列表:

What do you want to do?
Import a text file by opening it in Excel
Import a text file by connecting to it
Export data to a text file by saving it
Change the delimiter that is used in a text file
Change the separator in all .csv text files

使用 SED 我可以在“连接”上找到匹配项并替换该行:

 sed 's^.*connecting.*^Import a text file by opening it^g' crontab

这应该将上面的列表更改为:

What do you want to do?
Import a text file by opening it in Excel
Import a text file by opening it
Export data to a text file by saving it
Change the delimiter that is used in a text file
Change the separator in all .csv text files

但是我需要做的是:

如果存在包含单词connecting 的行,则替换该行, 如果该行不存在,则将其作为新行添加到列表的末尾。

我知道我可以做echo "Import a text file by opening it" >> list,这会将行添加到列表的末尾,但是无论如何我可以在一个命令中做到这一点吗?还是可以在一个实例中运行的命令?

谢谢

【问题讨论】:

  • 包含“connecting”的行是否不止一行?在这种情况下会发生什么?
  • 嗨 - 文件中只会出现一次“连接”。

标签: linux replace sed insert find


【解决方案1】:

一个简单的方法是使用awk:

awk 'BEGIN { s = "Import a text file by opening it" } /connecting/ { $0 = s; n = 1 } 1; END { if(!n) print s }' filename

工作原理如下:

BEGIN {                                    # Before anything else:
  s = "Import a text file by opening it"   # Remember the string by a shorter
                                           # name so we don't have to repeat it
}
/connecting/ {                             # If a line contains "connecting",
  $0 = s                                   # replace the line with that string
  n = 1                                    # and raise a flag that we've done so.
}
1                                          # print
END {                                      # in the end:
  if(!n) {                                 # If the string wasn't yet printed,
    print s                                # do it now.
  }
}

或者,您可以使用sed 的保持缓冲区。例如:

sed '1 { x; s/.*/Import a text file by opening it/; x; }; /connecting/ { s/.*//; x; }; $ { G; s/\n$//; }' filename

它的工作原理如下:

1 {                                        # while processing the first line
  x                                        # swap hold buffer, pattern space
  s/.*/Import a text file by opening it/   # write text to pattern space
  x                                        # swap back.
}                                          # Now the hold buffer contains the
                                           # line we want to insert, and the
                                           # pattern space the first line.

/connecting/ {                             # For all lines: If a line contains
                                           # "connecting"
  s/.*//                                   # empty the pattern space
  x                                        # swap in hold buffer.
                                           # If this happened, the hold buffer
                                           # will be empty and the pattern space
                                           # will contain "Import a ..."
}
$ {                                        # Last line:
  G                                        # Append hold buffer to pattern space.
                                           # If the hold buffer is empty (i.e.,
                                           # was used somewhere else), this
                                           # appends a newline, so
  s/\n$//                                  # remove it if that happened.
}

请注意,sed 代码取决于只有一行包含“连接”这一事实。如果有更多这样的行,它们将被替换为空行,因为当第二行出现时,保持缓冲区是空的。可以处理这种情况,但您必须决定其中应该发生什么。由于您在 cmets 中回答只有这样的一行,所以我觉得没有必要猜测。

【讨论】:

  • 感谢非常详细的回复。当我运行该命令时,我得到 SIGSEGV 并且没有任何改变。任何想法为什么?
  • sed 段错误?无论您提供什么代码,这都不应该发生。嗯。好吧,SIGSEGV 可能意味着sed 中的一个错误。您使用的是哪个版本? (sed --version 应该告诉你)。除此之外:awk 解决方案对您有用吗?
  • 感谢 awk 似乎可以工作并将正确的信息输出到屏幕,但它不会更新实际文件。应该吗?
  • 没有。如果您有 GNU awk,请使用 awk -i inplace,否则使用临时文件并在完成后将其重命名为原始文件。顺便说一句,sed 也不这样做,除非你给它 -i 标志。
【解决方案2】:

您可以尝试,它使用与 类似的正则表达式语法,但对这些问题更强大。当至少完成一次替换时,它只是设置一个标志。解析整个文件后,在END {}块中,如果$flag变量还没有设置,添加注释:

perl -pe '
    s/^.*connecting.*$/Import a text file by opening it/ and $done = 1;
    END { printf qq|%s\n|, q|Import a text file by opening it| unless $done }
' infile

当找到该行时,它产生:

What do you want to do?
Import a text file by opening it in Excel
Import a text file by opening it
Export data to a text file by saving it
Change the delimiter that is used in a text file
Change the separator in all .csv text files

当它没有找到时,它产生:

What do you want to do?
Import a text file by opening it in Excel
Export data to a text file by saving it
Change the delimiter that is used in a text file
Change the separator in all .csv text files
Import a text file by opening it

如果出现不止一次,则同时更改,但不附加任何内容:

What do you want to do?
Import a text file by opening it in Excel
Import a text file by opening it
Export data to a text file by saving it
Change the delimiter that is used in a text file
Import a text file by opening it
Change the separator in all .csv text files

【讨论】:

  • 感谢您的回答 - 我无权访问 perl,也没有安装选项
  • @MacMan:linux 标签看起来很奇怪,但你可以翻译成awk。与gsub() 的逻辑和语法非常相似。
猜你喜欢
  • 2013-02-09
  • 1970-01-01
  • 2022-01-09
  • 2017-03-21
  • 1970-01-01
  • 2018-06-25
  • 2014-09-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多