用 sed 替换会忽略空格

Substituting with sed ignores whitespace

假设我有一个名为 m1.txt 的文件,我想将其内容 (- . ... - / -. --- / .----) 从摩尔斯解码为文本。这是我写的:

sed -i 's/.- /A/g' m1.txt
sed -i 's/-... /B/g' m1.txt
sed -i 's/-.-. /C/g' m1.txt

以此类推,包括数字,以后:

sed -i 's:/ : :g' m1.txt
cat m1.txt

为了清除分隔斜线并输出消息。

预期的输出是 TEST NO 1,但是程序输出 D...AE-ED.--A,忽略任何空格并返回错误消息。我做错了什么?

两件事:

  1. 您需要转义 .,因为它将匹配任何字符
  2. 您需要考虑交互,因为 . (E) 会匹配单个点,它也会匹配 (N) -. 的末尾,除非您按正确的顺序考虑它们或保留分隔符(在左侧和右侧)。

(1) 很明显可以解决,只需转义.。 对于(2),我们可以首先将行的开头和结尾映射到空格(这样我们的分隔符在左右两侧一致,然后我们在输出时保留分隔符(以避免交互)。然后,在第二个到最后一行,清理空格。然后,在最后一行中,将斜杠映射到分词空格,这是输出中唯一需要的空格。

我没有为你做整个字母表,只是让你看到这个想法。

这是一个可行的解决方案:

$ cat m1.txt 
- . ... - / -. --- / .----
$ cat morse.sed 
s/^/ /
s/$/ /
s/ - / T /g
s/ \. / E /g
s/ \.\.\. / S /g
s/ -\.\ / N /g
s/ --- / O /g
s/ \.---- / 1 /g
s/[ ]\+//g
s:/: :g
$ cat m1.txt | sed -f morse.sed 
TEST NO 1

注意:这个答案被修改了,因为我一开始没有意识到 () 不是输入的一部分。另外,@Bach Lien 将锚点映射到第一行中的空格的想法很棒,它使事情变得更清晰,所以我已经采纳了这个想法。

莫尔斯码table(莫尔斯码到字符的映射):

$ cat morse-table.txt 
## this is table of char-to-morse-code mapping
## taken from https://en.wikipedia.org/wiki/Morse_code
## dated: 2018 01 23

## characters
A .-
B -...
C -.-.
D -..
E .
F ..-.
G --.
H ....
I ..
J .---
K -.-
L .-..
M --
N -.
O ---
P .--.
Q --.-
R .-.
S ...
T -
U ..-
V ...-
W .--
X -..-
Y -.--
Z --..

## numbers
1 .----
2 ..---
3 ...--
4 ....-
5 .....
6 -....
7 --...
8 ---..
9 ----.
0 -----

## special symbols
## in fact, there is no
## such symbols in traditional morse codes
## this part is fake, just for testing
. .......
\ -------
/ -.-.-.-
? --.--.-
[ .--.--.
] --..--.

将摩尔斯 table 转换为 sed 脚本的程序:

$ cat make-m2t.sh 
#!/bin/bash

t=morse-table.txt                     # morse table
s=m2t                                 # sed script

s1='                                  # s1 = pre-processing
s:\s+:  :g                            # space-gap to TWO space-chars
s:^: :                                # add a space at line beginning
s:$: :                                # add a space at line end
s:/: \n :g                            # change all slash to " \n "
'
s2='                                  # s2 = morse-table to sed-script
s:\s+: :g                             # space-gap to space-char
s:##.*$::                             # remove all comments
s:^ *::                               # remove all leading spaces
s: *$::                               # remove all trailing spaces
/^[^ ] [\.-]+$/!d                     # ignore all invalid lines
s:\.:\.:g                            # add back-slash for dot (escape for dot)
s:^\\. :\. :                         # but not for the char-dot
s:^\ :\\ :                         # add b-slash for b-slash (escape for b-slash)
s:^\/ :\\/ :                         # add b-slash for slash (escape for slash)
s:^([^ ]+) +([^ ]+).*$:s/  /  /g: # morse-map to sed-subsitution
'
s3='                                  # s3 = post-processing
s: ::g                                # remove all spaces
s:\n: :g                              # convert \n to space
'
                                      # now, make the sed script
echo '#!/usr/bin/sed -Ef'      >"$s"  # shebang
sed -E 's:\s*#.*$::' <<<"$s1" >>"$s"  # remove comments from s1
sed -E "$s2" "$t"             >>"$s"  # convert morse to chars
sed -E 's:\s*#.*$::' <<<"$s3" >>"$s"  # remove comments from s3
sed -i -E '/^\s*$/d'            "$s"  # remove all blank lines
chmod +x                        "$s"  # make it executable

测试:

$ ./make-m2t.sh
$ echo '- . ... -/-. ---/.----' | ./m2t
TEST NO 1
$ echo '.--.--. .... . .-.. .-.. --- --..--./....... --.--.-' | ./m2t
[HELLO] .?
$ cat m1.txt 
- . ... - / -. --- / .----
$ ./m2t m1.txt 
TEST NO 1
$ cat m2t
#!/usr/bin/sed -Ef
s:\s+:  :g
s:^: :
s:$: :
s:/: \n :g
s/ \.- / A /g
s/ -\.\.\. / B /g
s/ -\.-\. / C /g
s/ -\.\. / D /g
s/ \. / E /g
s/ \.\.-\. / F /g
s/ --\. / G /g
s/ \.\.\.\. / H /g
s/ \.\. / I /g
s/ \.--- / J /g
s/ -\.- / K /g
s/ \.-\.\. / L /g
s/ -- / M /g
s/ -\. / N /g
s/ --- / O /g
s/ \.--\. / P /g
s/ --\.- / Q /g
s/ \.-\. / R /g
s/ \.\.\. / S /g
s/ - / T /g
s/ \.\.- / U /g
s/ \.\.\.- / V /g
s/ \.-- / W /g
s/ -\.\.- / X /g
s/ -\.-- / Y /g
s/ --\.\. / Z /g
s/ \.---- / 1 /g
s/ \.\.--- / 2 /g
s/ \.\.\.-- / 3 /g
s/ \.\.\.\.- / 4 /g
s/ \.\.\.\.\. / 5 /g
s/ -\.\.\.\. / 6 /g
s/ --\.\.\. / 7 /g
s/ ---\.\. / 8 /g
s/ ----\. / 9 /g
s/ ----- / 0 /g
s/ \.\.\.\.\.\.\. / . /g
s/ ------- / \ /g
s/ -\.-\.-\.- / \/ /g
s/ --\.--\.- / ? /g
s/ \.--\.--\. / [ /g
s/ --\.\.--\. / ] /g
s: ::g
s:\n: :g

注:

  1. 用户只需要定义morse-table.txt,就可以将莫尔斯码映射到字符
  2. 基于莫尔斯码-table,bash 程序会生成 sed 脚本以将莫尔斯电码转换为文本
  3. sed脚本基于@JawguyChooser的解决方案
  4. 因为我们使用space ' '作为莫尔斯块的"delimiter",所以,在预处理时我们必须将所有space间隙转换为两个space-人物;否则会出现错误 'HELLO' (double L)