【问题标题】:Can this filter be implemented using Sed?这个过滤器可以使用 Sed 实现吗?
【发布时间】:2017-08-02 02:04:40
【问题描述】:

我编写了一个脚本来整理我的 .bash_history 文件,从持久化的历史记录中过滤“无趣”的命令,例如 ls

(我知道有 HISTIGNORE 变量,但这也会将此类命令从当前会话的内存历史记录中排除。我发现将它们放在单个会话的范围内很有用,但不会在会话之间持续存在.)

历史文件可以包含带有嵌入换行符的多行历史条目,因此这些条目由时间戳分隔。该脚本采用如下输入文件:

#1501304269
git stash
#1501304270
ls
#1501304318
ls | while IFS= read line; do
echo 'line is: ' $line
done

并过滤掉单行lsmancat命令,产生:

#1501304269
git stash
#1501304318
ls | while IFS= read line; do
echo 'line is: ' $line
done

请注意,多行条目是未经过滤的——我认为如果它们足够有趣以保证多行,那么它们值得记住。

我在 Awk 中实现了它,但我一直在阅读 Sed 的多行功能(NhHx 等),我想为此尝试一下.如果不出意外,我很想比较两者的速度。

这是 awk 脚本:

/^#[[:digit:]]{10}$/ {
  timestamp = $0
  histentry = ""
  next
}
$1 ~ /^(ls?|man|cat)$/ {
  if (! timestamp) {
    print
  } else {
    histentry = $0
  }
  next
}
timestamp {
  print timestamp
  timestamp = ""
}
histentry {
  print histentry
  histentry = ""
}
{ print }

这可以使用 Sed 完成吗?

【问题讨论】:

  • 你知道HISTIGNORE吗? superuser.com/questions/232885/… - 你可以在你的 .bashrc 中使用 export HISTIGNORE=ls:man:cat ,它会做类似的事情而无需编写任何代码。
  • @JohnZwinck 是的,但是这些命令也会从我当前会话的历史记录中被忽略。我经常点击upctrl-p 来回忆最近的ls 并对其进行调整,所以我不想在这种情况下使用HISTIGNORE
  • 如果你是一个受虐狂,你也许可以在sed 中实现。鉴于您在awk 中有一个完美的版本,我会算上我的幸运星并转向其他追求;-)。除非您使用的是 Pentium 3 硬件,否则速度不会有任何显着差异;-)。恕我直言。祝大家好运!。
  • 谢谢@shellter 我知道你可能是对的:D

标签: awk sed


【解决方案1】:

当然可以用 sed 完成。这是一个使用 GNU seds -z 选项的示例,它让我们可以一次处理整个文件,而不是逐行处理:

 sed -rz "s/(#[0-9]{10}\n(cat|ls|man)\n)+(#[0-9]{10}\n|$)/\3/g;" yourfile

如果一切正常并且您有历史文件的备份,您甚至可以使用 GNU sed -i 选项进行就地修改。

-r 选项启用扩展正则表达式,-z 选项在手册中解释如下:

将输入视为一组行,每行以零字节结尾 (ASCII 'NUL' 字符)而不是换行符。这个选项可以 与“sort -z”和“find -print0”等命令一起使用来处理 任意文件名。

基本思路是这样的:一个无趣的命令前后都有一个时间戳(或者它是文件中的最后一行)。

  • 时间戳 RE #[0-9]{10} 取自您的 awk 脚本
  • (#[0-9]{10}\n(cat|ls|man)\n)+ 匹配一个或多个无趣的命令
  • (#[0-9]{10}|$) 第二个时间戳被捕获到 \3 中(由于在第三对括号中)以在替换部分中重复使用,而替换 |$ 适合文件结尾的情况

【讨论】:

  • 哇,这实际上比我尝试过的所有方法都简单得多。我必须安装 gnu-sed,因为我在 Mac 上,而 BSD sed 不支持-z
猜你喜欢
  • 2011-02-24
  • 2011-09-20
  • 1970-01-01
  • 1970-01-01
  • 2021-10-24
  • 2021-02-03
  • 2010-09-14
  • 2014-06-30
相关资源
最近更新 更多