【发布时间】:2014-06-04 21:40:17
【问题描述】:
我正在尝试使用 sed 从文件中删除最后一次出现的 }。到目前为止,我有这个:
sed -i 's/\(.*\)}/\1/' file
但这会删除与文件末尾一样多的 }。因此,如果我的文件如下所示:
foo
bar
}
}
}
该命令将删除所有 3 个 } 字符。如何将其限制为最后一次出现?
【问题讨论】:
我正在尝试使用 sed 从文件中删除最后一次出现的 }。到目前为止,我有这个:
sed -i 's/\(.*\)}/\1/' file
但这会删除与文件末尾一样多的 }。因此,如果我的文件如下所示:
foo
bar
}
}
}
该命令将删除所有 3 个 } 字符。如何将其限制为最后一次出现?
【问题讨论】:
有人把这个当作解决方案来欺骗我
sed -i '1h;1!H;$!d;g;s/\(.*\)}/\1/' file
我只是不确定它是否与上述 awk 解决方案一样好。
【讨论】:
}而不是整行包含它。所以我认为它更好。编辑:sed 有 E 开关以启用更具可读性的 RE 语法:sed -iE '1h;1!H;$!d;g;s/(.*)}/\1/' file
sed -r '1h;1!H;$!d;g;s/(.*)}\n/\1/' file 可能更合适。
sed -E '1h;1!H;$!d;g;s/(.*)}\n/\1/' <<<$'a}\nb\n}'(注意<<<总是附加\n,所以有效的输入确实以@结尾987654329@)。 - 我认为原因是缓冲区的last 行not 以\n 结尾。如果你想修剪尾随换行符,你可以添加; s/\n+$//。
} 之外,删除前面的换行符,或者如果删除发生,则将 msg 打印到 stderr,或者其他任何事情)并且会告诉你它是否和 awk 解决方案一样好。如果您的下一个问题的要求略有不同或对这一更改的要求 - 您是否希望新解决方案的起点是像这样的 sed 脚本作为构建的基础,还是一个简单、可读的 awk 脚本。
sed 是在单行上进行简单替换的出色工具。对于其他任何事情,只需使用 awk,例如使用 GNU awk 进行 gensub() 和多字符 RS:
$ cat file1
foo
bar
}
}
}
$
$ cat file2
foo
bar
}}}
$
gawk -v RS='^$' -v ORS= '{$0=gensub(/\n?}([^}]*)$/,"\\1","")}1' file1
foo
bar
}
}
$
$ gawk -v RS='^$' -v ORS= '{$0=gensub(/\n?}([^}]*)$/,"\\1","")}1' file2
foo
bar
}}
$
请注意,上面将删除最后一个 } 字符和前面的换行符,如果我认为这可能是您真正想要的,但如果您只想删除 } 并在这些情况下留下尾随换行符(如我认为所有当前发布的 sed 解决方案都可以),然后从匹配的 RE 中删除 \n?:
$ gawk -v RS='^$' -v ORS= '{$0=gensub(/}([^}]*)$/,"\\1","")}1' file1
foo
bar
}
}
$
如果您想更改原始文件而不手动指定 tmp 文件,只需使用 -i inplace 参数:
$ gawk -i inplace -v RS='^$' -v ORS= '{$0=gensub(/}([^}]*)$/,"\\1","")}1' file1
$ cat file1
foo
bar
}
}
$
【讨论】:
awk '...' file file > tmp && mv tmp file。 nbd...
}}} 可以出现在输入中吗?如果出现,预期的输出是什么?
-i inplace 而不是显式的 tmp 文件展示了这个解决方案。
使用缓冲区可以直接修改文件:
awk 'BEGIN{file=ARGV[1]}{a[NR]=$0}/}/{skip=NR}END{for(i=1;i<=NR;++i)if(i!=skip)print a[i]>file}' file
【讨论】:
FILENAME,因此您不需要自己的file 变量,您可能应该在开始循环之前将close() END 部分开头的文件虽然可能 awk 会为你做这件事,但写信给它,idk。
END{} 已经在另一个上下文中。实际的编程实践是关闭每个会话的文件(来自参数的输入),而不是在另一个会话之前关闭文件。至于 FILENAME,awk 的手册并没有明确说它会在输入END 之前保留它的值。如果您担心文件是否会默认关闭等默认行为,那么您也应该考虑这一点。不过还是可以用的。
Inside an END action the value shall be the name of the last input file processed.。
感谢@jthill 对 1 行文件问题的评论
sed ':a
$ !{N
ba
}
$ s/}\([^}]*\)$/\1/' YourFile
需要先加载缓冲区中的文件。如果 } 单独在一行上,这不会删除新行
【讨论】:
:a;$!{N;ba}。
当我读到“用last ...做某事”时,我认为“反转文件,用first做一些事情...,重新反转文件”
tac file | awk '!seen && /}/ {$0 = gensub(/(.*)}/, "\\\1", 1); seen = 1} 1' | tac
【讨论】: