下面的两个 sed 命令有啥区别?
Posted
技术标签:
【中文标题】下面的两个 sed 命令有啥区别?【英文标题】:What is the difference b/w two sed commands below?下面的两个 sed 命令有什么区别? 【发布时间】:2016-07-13 15:37:15 【问题描述】:关于我工作环境的信息:
$ uname -a
AIX prd231 1 6 00C6B1F74C00
$ oslevel -s
6100-03-10-1119
代码块 A
( grep schdCycCleanup $DCCS_LOG_FILE | sed 's/[~]/ \
/g' | grep 'Move(s) Exist for cycle' | sed 's/[^0-9]*//g' ) > cycleA.txt
代码块 B
( grep schdCycCleanup $DCCS_LOG_FILE | sed 's/[~]/ \n/g' | grep 'Move(s) Exist for cycle' | sed 's/[^0-9]*//g' ) > cycleB.txt
我有两个代码块(如上所示),它们使用 sed 将输入修剪到 6 位,但一个命令的行为与我的预期不同。
两个代码块的输入示例
Mar 25 14:06:16 prd231 ajbtux[33423660]: 20160325140616:~schd_cem_svr:1:0:SCHD-MSG-MOVEEXISTCYCLE:200705008:AUDIT:~schdCycCleanup - /apps/dccs/ajbtux/source/SCHD/schd_cycle_cleanup.c - line 341~ SCHD_CYCLE_CLEANUP - Move(s) Exist for cycle 389210~
当上面的示例输入通过两个代码块时,我得到以下输出。
cycleA.txt 内容
389210
cycleB.txt 内容
25140616231334236602016032514061610200705008341389210
我知道我的最后一个管道 sed 命令 (sed 's/[^0-9]*//g') 正在删除除数字以外的所有字符,所以我从块代码中省略了它,并将输出放在另外两个文件。我得到以下输出。
cycleA1.txt 内容
SCHD_CYCLE_CLEANUP - Move(s) Exist for cycle 389210
cycleB1.txt 内容
Mar 25 15:27:58 prd231 ajbtux[33423660]: 20160325152758: nschd_cem_svr:1:0:SCHD-MSG-MOVEEXISTCYCLE:200705008:AUDIT: nschdCycCleanup - /apps/dccs/ajbtux/source/SCHD/schd_cycle_cleanup.c - line 341 n SCHD_CYCLE_CLEANUP - Move(s) Exist for cycle 389210 n
我可以看到第一个代码块正在删除除 (SCHD_CYCLE_CLEANUP - Move(s) Exist for cycle 389210) 之外的所有内容,并且正在使用波浪号,但第二个代码块只是将波浪号替换为字符 n。我还可以看到,在此之后的第一个代码块中需要换行(sed 's/[~]/),这就是为什么我虽然有 \n 会模拟换行但事实并非如此。我认为我不同的输出结果是因为使用正则表达式的方式。我试图研究正则表达式并在***上搜索它们,但没有得到我想要的东西。有人能解释一下我如何从代码块 B 中获得与代码块 A 相同的结果,而无需将我的部分代码放在第二行吗?
提前谢谢你
【问题讨论】:
您使用的是什么版本的sed
?我有 4.2.1 版本,并且使用 sed
表达式得到相同的结果。
您能否使用代码格式而不是引号格式重新格式化示例输入和输出,以确保换行符/等。显示正确吗?
这是一个很好的问题,但它被埋没了。请考虑创建一个MCVE (Minimal, Complete, and Verifiable Example)。
你好 lurker,我尝试了 sed -V 和 -version 但没有得到任何东西,所以我寻找我的 unix 机器的版本:uname -a AIX prd231 1 6 00C6B1F74C00
和 oslevel -s 6100-03-10-1119
你好,Etan,完成了。
【参考方案1】:
Etan Reisner's helpful answer 解释了问题并提供了基于 ANSI C-quoted string ($'...'
) 的单行解决方案,这是适当的,因为您最初标记了您的问题 bash。 (Ed Morton's helpful answer 向您展示了如何绕过您的问题,使用一种更简单、更高效的不同方法。)
但是,听起来您的 shell 实际上有些不同 - 大概是 ksh88
,Korn shell 的旧版本,它是 AIX 6.1 上默认的 sh
- 其中这些字符串是不是 支持[1]
(ANSI C 引用的字符串是在 ksh93
中引入的,并且不仅在 bash
中受支持,而且在 zsh
中也受支持。
因此,您有以下选项:
使用您当前的 shell,您必须坚持使用包含 (\
-escaped) 实际 换行符的 两行 解决方案 ,就像在你的代码块 A 中一样。
$(printf '\n')
创建换行符不起作用,因为命令替换总是修剪所有尾随换行符,在这种情况下导致 空 字符串。李>
使用支持 ANSI C 引用字符串的更现代的 shell,并使用 Etan 的答案。 http://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.cmds3/ksh.htm 告诉我 ksh93
可用作 AIX 6.1 上的替代 shell,如 /usr/bin/ksh93
。
如果可行:安装 GNU sed
,它本机理解替换字符串中的转义序列,例如 \n
。
[1] 至于当您在不支持 $'...'
的类似 POSIX 的 shell 中尝试 echo 'foo~bar~baz' | sed $'s/[~]/\\\n/g'
时实际发生的情况:$
保持原样, 因为后面的不是有效的变量名,sed
最终会看到文字 $s/[~]/\\\n/g
,其中 $
被解释为应用于 last 输入行的上下文地址 - 这不会在这里没什么区别,因为只有 1 行。 \\
被解释为普通的\
,\n
被解释为普通的n
,有效地将~
实例替换为literal \n
序列。
【讨论】:
感谢您的回复。我碰巧有你在目录/usr/bin/ksh93 中所说的ksh93。如果我想在脚本中测试 Etan 的答案,将脚本中的第一行从 #!/bin/ksh 更改为 #!/bin/ksh93 就足够了? @ManaFive:使用#!/usr/bin/ksh93
是安全的,即二进制文件的完整路径(有些平台,例如Fedora,其中/bin
是指向/usr/bin
,但通常情况并非如此)。另一种方法是使用#!/usr/bin/env ksh93
,它可以让标准env
实用程序在$PATH
中找到二进制文件(即使在这种情况下,它恰好与env
本身位于同一目录中。)【参考方案2】:
这是 XY 问题的一个示例 (http://xyproblem.info/)。您正在寻求帮助以实施对您的问题的错误解决方案。你为什么要将~
s 更改为换行符等,而你所需要的只是给出你发布的示例输入和预期输出:
$ sed -n 's/.*schdCycCleanup.* \([0-9]*\).*/\1/p' file
389210
或:
$ awk -F'[ ~]' '/schdCycCleanup/print $(NF-1)' file
389210
如果这还不是您所需要的,那么请编辑您的问题,以阐明您对 WHAT 的要求(而不是您尝试做的方式),因为您当前的方法只是错了。
【讨论】:
你好 Ed,我真的很想知道为什么代码块 B 没有产生与代码块 A 相同的结果。你们都能够启发我,'\n' 不能是在我当前的 shell 中用作新行。我感谢所有投入时间的人,并因此获得了更好的理解。 @ManaFive 那个问题的答案与你的 shell 无关,而是\n
在 sed 版本之间是不可移植的,而你正在运行的 sed 版本不支持它。
【参考方案3】:
GNU sed 以您期望的方式处理 \n
。
OS X(可能是 BSD)sed 没有。它把它当作一个普通的转义字符,只是将它转义为n
。 (虽然我目前在手册中没有看到这一点。)
如果需要,您可以使用 $''
引用来使用 \n
作为文字换行符。
echo 'foo~bar~baz' | sed $'s/[~]/\\\n/g'
【讨论】:
++; BSD/OS Xsed
保持接近 POSIX 规范。 (唯一提到的扩展是-E
、-a
和-i
),而POSIX spec.实际上在替换字符串unspecified中声明了\n
的解释(因为n
是一个不需要转义的字符)。在实践中,我可以确认最近版本的 FreeBSD/PC-BSD/OS X 将 \n
解释为普通的 n
。
您好 Etan,我在 unix 机器上运行了您的示例,并得到以下输出 echo 'foo~bar~baz' | sed $'s/[~]/\\\n/g' foo\nbar\nbaz
波浪线被替换为 \n
@ProudAtheist $'..'
我相信这是一种 bash 主义。您正在运行 ksh,而不是 bash,所以.... 顺便说一下,用户名的选择很有趣 - 没有什么比对每个人大喊大叫来营造一个愉快的环境了。
@EdMorton 感谢您的回复。对不起,冒犯的用户名,我年轻的时候做了我的用户名。我已经将它更新为我现在使用的。
@EdMorton:Korn shell 是第一个引入$'...'
引用的,即在ksh93
中(而OP 的默认shell 可能是ksh88
);后来被bash
(v2) 和zsh
(v3.1) 采用。以上是关于下面的两个 sed 命令有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章
下面的 React 代码有啥区别?哪一种最有效或最有效?或者它们实际上是一样的?