sed高尔夫技巧


19

您在sed中打高尔夫球有哪些一般提示?我正在寻找可以应用于代码高尔夫球问题并且至少在某种程度上特定于sed的想法(例如,“删除注释”不是答案)。

请为每个答案发布一个提示。


4
不是一个真正的高尔夫球提示(但仍打高尔夫球小费):换行符消耗同样多字节分号,所以你可以保持你的代码的短可读性。
丹尼斯

也不是技巧,而是一个问题:我已经使用了GNU sed,但是该F命令从未起作用。有人知道为什么吗?
seshoumara

@seshoumara F在我的GNU sed(Debian测试)上工作。-当然,如果从stdin中读取,它只会打印出来,但这是预料之中的。你从那里得到sed -e 'F;Q' /etc/hostname什么?
Toby Speight

@TobySpeight给出此错误:char 1: unknown command: F。我可能必须更新sed;你有什么版本?该L命令也不起作用,但是自从-l n存在以来,它还是无用的。GNU sed网站上提到的所有其他内容均有效。
seshoumara

1
bash, sed and dc为所有想要交谈和询问这些语言的人打开了聊天室。让我们建立一个社区!
seshoumara

Answers:


11

如果您需要使用标签,那么可以肯定的是,您希望标签名称尽可能短。实际上,您甚至可以使用空字符串作为标签名称:

:    # define label ""
p    # print pattern space
b    # infinite loop! - branch to label ""

4
从版本4.3开始,此行为已删除:现在需要一个标签。
凯文(Kevin)

实际上,这也是实际的git commit link。我想对于PPCG来说,这不会有太大变化,因为我们可以发布GNU sed 4.2.x的答案,但是很高兴知道,尽管遗憾的是,这个技巧不再正式可用。
seshoumara

8

GNU sed的文档描述了s命令,“sed中的瑞士军刀”。但是,如果您要做的只是将一个字符的所有实例替换为另一个,那么该y命令就是您所需要的:

y/a/b/

比以下字符短一个字符:

s/a/b/g

它也更快,并且可以交换字符:y/12/21/
mikeserv

6

考虑使用扩展的正则表达式语法(在GNU sed中)。该-r选项的得分成本为一个字节,但仅使用一次就可以消除一对\(...\)已经使用的反斜杠。


2
附加说明-r似乎是GNU sed特定的。
manatwork 2015年

@manat-添加(但这是社区Wiki的答案,因此您可以进行自己的编辑)。
Toby Speight 2015年

当然。我只是不认为它是小费的一部分,而只是附加说明。
manatwork 2015年

它不断地支付自己使用时+?{}|在正则表达式匹配,因为要么不需要反斜线。
seshoumara

-E如果我没记错的话,它可以作为-r许多sed实现的别名。
phk

6

在循环中多次替换时:

loop:
s/foo/bar/g
tloop

通常不需要全局替换,因为循环最终将替换所有出现的事件:

# GNU sed
:
s/foo/bar/
t

还要注意上面的GNU扩展:标签可以具有一个空名称,从而节省更多的宝贵字节。在其他实现中,标签不能为空,并且没有标签的跳转会将流程转移到脚本的末尾(即与相同n)。


1
空标签名称是特定于GNU的,POSIX要求没有参数的分支跳转到脚本的末尾(似乎是BSD和Busybox中的行为,如果您不添加空的话,在GNU sed中也是如此:
ninjalj

2
无名标签始终是GNU sed中的错误,而不是扩展名,遗憾的是,在4.3版及更高版本中,此错误已得到修复。看这里
seshoumara

5

没有内置的算术运算法则,但是可以使用一进制或以一进制编码的十进制形式进行计算。以下代码将十进制转换为UCD,以x为单位,以0为数字分隔符:

s/[1-9]/0&/g
s/[5-9]/4&/g
y/8/4/
s/9/4&/g
s/4/22/g
s/[37]/2x/g
s/[26]/xx/g
s/[1-9]/x/g

这是回到十进制的转换:

s/0x/-x/g
s/xx/2/g
y/x/1/
s/22/4/g
s/44/8/g
s/81/9/g
s/42/6/g
s/21/3/g
s/61/7/g
s/41/5/g
s/-//g

这些都来自“不使用任何数字将两个数字相乘”的答案

可以使用此循环对将普通的旧一元代码从此答案转换为“ {Curly Numbers};”。,单位在哪里;。我用过vx匹配Roman510; b来自“ bis”。

# unary to decimal
:d
/;/{
s/;;;;;/v/g
s/vv/x/g
/[;v]/!s/x\+/&0/
s/;;/b/g
s/bb/4/
s/b;/3/
s/v;/6/
s/vb/7/
s/v3/8/
s/v4/9/
y/;bvx/125;/
td
}

# Decimal to unary
:u
s/\b9/;8/
s/\b8/;7/
s/\b7/;6/
s/\b6/;5/
s/\b5/;4/
s/\b4/;3/
s/\b3/;2/
s/\b2/;1/
s/\b1/;0/
s/\b0//
/[^;]/s/;/&&&&&&&&&&/g
tu

1
...并且如果您必须使用这两种方法,则几乎可以肯定已经失去了代码优势,尽管您可能仍会与Java答案竞争;-)虽然使用起来仍然很有趣。
Digital Trauma

从普通一元到十进制的转换给出了与十进制格式X0X等效的一元输入等效项的错误答案。对此的负责行是/[;v]/!s/\b/0/2,需要更改/[;v]/!s:x\+:&0:为该行才能起作用。看这里
seshoumara

@seshoumara,您的链接似乎是一个空白页。但是从参考答案中提取代码时我犯了一个错误是完全合理的,所以我将仅应用您的修复程序。
Toby Speight

链接已正确加载,但是我期望带有“ TIO”的灰色页面之外的东西以及类似于Ubuntu徽标的东西-这是什么意思?我指的是我引用的第二个答案(58007),因为这是普通一元样本的来源。
Toby Speight

TIO链接应包含更正后的代码,以及示例输入,一元108。在运行代码时,您应该已经看到正确的结果108,而不是180,这是现在固定的代码行先前生成的结果。更新参考答案完全取决于您。这是社区维基。
seshoumara

4

man sed(GNU)中所述,您可以使用以下语法将任何字符用作正则表达式的分隔符

\%regexp%

%任何字符的占位符在哪里。

这对于像这样的命令很有用

/^http:\/\//

较短的

\%^http://%

什么在提到GNU sed的手册,但不是man sed是,你可以改变的分隔符s///,并y///为好。

例如,命令

ss/ssg

从模式空间中删除所有斜杠。


4

如果问题未明确禁止,则该元问题的共识是数字输入可能是一元的。根据此答案,这将为您节省86个十进制字节到一进制。


sed的元共识不是指普通的旧一元格式吗?我有几个答案,无论哪种情况,UCD中的输入都会对我有帮助。
seshoumara

@seshoumara我的意思是一元,不是UCD
数字创伤

然后,根据所链接的答案,从十进制转换为普通的旧一元可节省126个字节。86个字节用于转换为UCD。
seshoumara

4

扩展此提示答案,关于十进制和滑动元数字格式之间的转换,我提出下面的替代方法,它们的优点和缺点。

十进制到普通一进制: 102 + 1(r标志)= 103字节。我算作\t文字标签,为1个字节。

h
:
s:\w::2g
y:9876543210:87654321\t :
/ /!s:$:@:
/\s/!t
x;s:-?.::;x
G;s:\s::g
/\w/{s:@:&&&&&&&&&&:g;t}

在线尝试!

优点:它短22个字节,并且额外地,它使用负整数作为输入

缺点:它会覆盖保留空间。但是,由于您更有可能需要在程序开始时立即转换输入整数,因此很少会遇到这种限制。

普通一进制到十进制: 102 +1(r标志)= 103字节

s:-?:&0:
/@/{:
s:\b9+:0&:
s:.9*@:/&:
h;s:.*/::
y:0123456789:1234567890:
x;s:/.*::
G;s:\n::
s:@::
/@/t}

在线尝试!

优点:它短14个字节。这次,两个技巧版本都将负整数用作输入。

缺点:它会覆盖保留空间

对于复杂的挑战,除了要转换的数字外,您还必须使这些代码片段与模式空间或保持空间中可能存在的其他信息配合使用。如果您知道仅使用正数,或者仅靠零将不会成为有效的输入/输出,则可以对代码进行更多研究。

我创建并使用了这些代码段的此类挑战性答案的一个示例是数字的倒数(1 / x)


对于单进制到十进制,您可以通过组合最后两个替换项来节省两个字节:s:\n|@$::gtio.run/##K05N@f@/2ErX3krNwIpL30G/…–
乔丹

我在十进制到一进制转换器中做了自己的尝试。这是97个字节:) 在线尝试!(也不需要-r,但是有了新的共识,标志无论如何都不会计入字节数,并且不会占用保留空间)
Kritixi Lithos

实际上,如果您将最后一行从更改/\n/ta/\n/t,则会节省1个字节以获取96
Kritixi Lithos

@Cowsquack谢谢,太好了96!现在没有时间,将在本周末进行检查。
seshoumara

当然可以,然后在聊天时发送ping
通知

3

让我们谈谈tand T命令,尽管它们在手册页中进行了说明,但很容易忘记它并意外引入错误,特别是当代码变得复杂时。

手册页声明t

如果s///自从读取最后一条输入行以及从最后一条t或T命令以来,a 已成功完成替换,则分支到label。

显示我的意思的示例:假设您有一个数字列表,并且想要计算负数。下面的部分代码:

1{x;s/.*/0/;x}                   # initialize the counter to 0 in hold space
s/-/&/                           # check if number is negative
t increment_counter              # if so, jump to 'increment_counter' code block
b                                # else, do nothing (start a next cycle)

:increment_counter
#function code here

看起来还可以,但事实并非如此。如果第一个数字为正,则该代码仍会认为它为负,因为t无论如何,输入的第一行都会通过via 进行跳转,因为s初始化计数器时成功进行了替换!正确的是:/-/b increment_counter

如果这看起来很简单,那么在进行多次来回跳转以模拟功能时,您仍然可能会上当。在我们的示例中increment_counter,代码块肯定会使用很多s命令。返回b main可能会导致“主”中的另一次检查落入同一陷阱。这就是为什么我通常使用来从代码块返回s/.*/&/;t label。这很丑陋,但很有用。


2

如果不使用GNU sed s/.*//,请使用z命令(小写)代替用清除模式空间。除了字节数较少之外,它还有一个优点是它不会像命令d那样启动下一个周期,这在某些情况下很有用。


1
如果您有无效的多字节序列(与不匹配.),也可能会有所帮助。
Toby Speight

2

我知道这是一个旧线程,但是我只是发现那些笨拙的十进制到UCD转换器,几乎有一百个字节,有些甚至弄乱了保持空间或需要特殊的错误 sed版本。

对于UCD,我使用十进制(68字节;以前最好在此处发布87字节)

s/$/\n9876543210/
:a
s/\([1-9]\)\(.*\n.*\)\1\(.\)/\3x\2\1\3/
ta
P;d

UCD到十进制是(也是66个字节;以前最好在此处发布96)

s/$/\n0123456789/
:a      
s/\([0-8]\)x\(.*\n.*\)\1\(.\)/\3\2\1\3/
ta      
P;d
  • \n在更换中不是便携式的。您可以改用其他字符并保存两个字节,但是需要更多字节才能删除附录,而不是P;d;见下句话。或者,如果保留空间为空,则不G;s/$/9876543210/进行字节惩罚。
  • 如果需要进一步处理,则需要更多字节 s/\n.*//代替P;d
  • 您可以为那些有问题的旧GNU节省两个字节 sed版本
  • 不,您不能保存这六个反斜杠,因为扩展的正则表达式不会做反向引用

此线程中没有张贴UCD和后置转换器的十进制数,这些值会使保持空间混乱或需要错误的sed版本。
seshoumara

从4月6日起,您自己的答案将使用黄金空间,并且仅在sed违反POSIX标准的旧版本中运行。
Philippos

我没有将十进制转换为UCD!再次仔细阅读该线程。UCD表示将12转换为0x0xx(由您的答案计算),而普通一元(由我的答案计算)意味着将12转换为xxxxxxxxxxxx。我选择@作为符号,但您明白了。而且,在PPCG上,无需遵守POSIX标准。
seshoumara

如果你高兴的话,警长
腓力

2

一次读取整个输入 -z

通常,您需要一次处理整个输入,而不是一次处理一行。该N命令对此有用:

:
$!{N;b}

...但是通常您可以跳过它并使用 -z标志。

-z标志使sed使用NUL(\0)代替其输入行分隔符\n,因此,如果您知道输入将不包含\0,它将一次读取所有输入作为一条“行”:

$ echo 'foo
> bar
> baz' | sed -z '1y/ao/eu/'
fuu
ber
bez

在线尝试!


2

在一个字节后附加一个换行符

G命令将换行符和保留空间的内容追加到模式空间,因此,如果保留空间为空,请执行以下操作:

s/$/\n/

你可以这样做:

G

在三个字节前添加换行符

H命令将换行符和模式空间的内容追加到保持空间,并x交换两者,因此,如果保持空间为空,则改为:

s/^/\n/

你可以这样做:

H;x

这会污染您的容纳空间,因此只能使用一次。但是,对于另外两个字节,您可以在交换之前清除模式空间,这仍然节省了两个字节:

H;z;x

1

在sed中,最接近的功能是标签。函数很有用,因为您可以多次执行其代码,从而节省大量字节。但是,在sed中,您将需要指定返回标签,因此,您不能像在其他语言中那样简单地在整个代码中多次调用此“函数”。

我使用的解决方法是在两个内存之一中添加一个标志,该标志用于选择返回标签。当功能代码仅需要一个存储空间(另一个)时,这种方法最有效。

示例显示了我的意思:摘自我的一个项目,用sed编写了一个小游戏

# after applying the player's move, I overwrite the pattern space with the flag "P"
s/.*/P/
b check_game_status
:continue_turn_from_player
#code

b calculate_bot_move
:return_bot_move
# here I call the same function 'check_game_status', but with a different flag: "B"
s/.*/B/
b check_game_status
:continue_turn_from_bot
#code (like say 'b update_screen')

:check_game_status   # this needs just the hold space to run
#code
/^P$/b continue_turn_from_player
/^B$/b continue_turn_from_bot

标签当然应该打个字母,我用全名作了更好的解释。


1

空的正则表达式等效于以前遇到的正则表达式

(感谢Rileyanagol提交中发现了这一点)

这是一个示例,其中我们要@在一个空缓冲区中创建100 s。

s/$/@@@@@@@@@@/;s/.*/&&&&&&&&&&/ # 31 bytes
s/.*/@@@@@@@@@@/;s//&&&&&&&&&&/  # 30 bytes

第二个解决方案是短1个字节,并使用最后一个遇到的正则表达式填充空正则表达式的事实。在这里,对于第二个替换,最后一个正则表达式为.*,因此此处的空正则表达式将被填充.*。这也适用于中的正则表达式/conditionals/

请注意,它是先前遇到的正则表达式,因此以下内容也可以使用。

s/.*/@@@@@@@@@@/;/@*/!s/$/@/;s//&&&&&&&&&&/

空的正则表达式将填充,@*而不是$因为s/$/@/从未达到。


是的,很好的答案。我什至使正则表达式更长,以便它们可以像这样重新匹配(从而使程序更短)。
Toby Speight,

0

通常无用的步骤:

y|A-y|B-z|

这只会转化AByz(...和--),但没有别的,所以

sed -e 'y|A-y|B-z|' <<<'Hello world!'

只会返回:

Hello world!

你能确保这将是无用的,对于样品通过使用此上小写十六进制值(只包含0123456789abcdef。)


2
这是您很难找到的吗?;-)
Toby Speight

我喜欢无用的脚本:(sed '; ;/s/b;y|A-y|B-z|;s ;s/ //; ; ;' <<<'Hello world'为什么压制空格?)
F. Hauri 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.