简体   繁体   中英

Replace specific string using sed

I have a code.txt file that contains morse code for example

.- .-.

I have a function called decode inside a bash file called morse as this:

decode (){ 
   sed -i 's/ \.-/A/g' $1
   sed -i 's/ \.-./R/g' $1
   cat $1
}

When I type in terminal $bash morse decode code.txt

I receive:

AA.

The output I want is :

AR

How can it see separate that the string .- is A and the .-. is R ?

If your intention is to encode and decode Morse messages with any tool then something like this will do :

#!/usr/local/bin/python3
import re

alphabet = { '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':'--..', '1':'.----', '2':'..---', '3':'...--', '4':'....-', '5':'.....', '6':'-....', '7':'--...', '8':'---..', '9':'----.', '0':'-----', ', ':'--..--', '.':'.-.-.-', '?':'..--..', '/':'-..-.', '-':'-....-', '(':'-.--.', ')':'-.--.-',' ':'  '} 

def encode(message): 
    return "".join([ ( alphabet[letter.upper()] + ' ' ) if letter != ' ' else '  ' for letter in message])

def decode(message):
    return "".join([ list(alphabet.keys())[list(alphabet.values()).index(item if item != '|' else '  ')] for item in re.sub(r' {2,}', ' | ',message).split(' ')])

print(encode('THIS IS FINE'))
print(decode('- .... .. ...   .. ...   ..-. .. -. .'))

Hope it helps too.

Wow interesting idea! Based on @MatiasBarrios alphabet i made this.

#!/bin/bash

string=$1

declare -A morse=(
    [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]='--..'

    [1]='.----'  [2]='..---'  [3]='...--'  [4]='....-'  [5]='.....'
    [6]='-....'  [7]='--...'  [8]='---..'  [9]='----.'  [0]='-----'
    [(]='-.--.'  [)]='-.--.-' [/]='-..-.'  [-]='-....-' [+]='.-.-.'
    [.]='.-.-.-' [,]='--..--' [?]='..--..' [!]='-.-.--' [ ]='  '
)

morse () {
    while [[ "$string" ]]; do
        symbol="${string::1}"
        printf -- "${morse["${symbol^}"]} "
        string="${string:1}"
    done
}

demorse () {
    declare -A demorse
    for item in "${!morse[@]}"; { demorse["${morse["$item"]}"]="$item"; }
    while [[ $@ ]]; do
        printf -- "${demorse["$1"],}"
        shift
    done
}

case $string in
    demorse) shift; demorse "$@";;
    *      )          morse     ;;
esac

Usage

$ ./morse 'hello world!'
.... . .-.. .-.. ---    .-- --- .-. .-.. -.. -.-.--

Demorse also worsk but, spaces have to be printed like this ' '

$ ./morse demorse .... . .-.. .-.. --- '  ' .-- --- .-. .-.. -.. -.-.--
hello world!

You need to run s/ \\.-\\./R/g replacement first. Note the second . must be escaped to only match a dot.

Hence, use

sed 's/ \.-\./R/g;s/ \.-/A/g' file

See the online demo

Or, another way:

sed -e 's/ \.-\./R/g' -e 's/ \.-/A/g' file

Replace the file with "$1" in your code.

UPDATE

Here is the translation of encoding / decoding Python function posted by Matias below:

#!/bin/bash

### Encoding:

declare -A MORSE=( [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]='--..' [1]='.----' [2]='..---' [3]='...--' [4]='....-' [5]='.....' [6]='-....' [7]='--...' [8]='---..' [9]='----.' [0]='-----' [',']='--..--' ['.']='.-.-.-' [';']='-.-.-.' [':']='---...' ['?']='..--..' ['!']='-.-.--' ['/']='-..-.' ['-']='-....-' ['+']='.-.-.' ['(']='-.--.' [')']='-.--.-' ['_']='..--.-' ['"']='.-..-.' ["'"]='.----.' ['$']='...-..-' ['@']='.--.-.' ['&']='.-...' ['  ']=' '  )

function encode {
  res=''
  s="$1"
  for (( i=0; i<${#s}; i++ )); do
    letter="${s:$i:1}"
    if [[ "$letter" == ' ' ]]; then
      res="${res}  "
    else
      res="${res}${MORSE[${letter^^}]} ";
    fi
  done
  printf "%s" "$res"
}

echo "$(encode "THIS IS FINE")"

### Now, decoding

declare -A MORSEDEC=( ['-.--.-']=')' ['..--..']='?' ['--..--']=', ' ['-....-']='-' ['.-.-.-']='.' ['...--']='3' ['-.--.']='(' ['---..']='8' ['-..-.']='/' ['....-']='4' ['-....']='6' ['----.']='9' ['.----']='1' ['..---']='2' ['.....']='5' ['--...']='7' ['-----']='0' ['-...']='B' ['-..-']='X' ['-.-.']='C' ['--..']='Z' ['--.-']='Q' ['.-..']='L' ['-.--']='Y' ['..-.']='F' ['.--.']='P' ['.---']='J' ['...-']='V' ['....']='H' ['-..']='D' ['---']='O' ['..-']='U' ['...']='S' ['.--']='W' ['-.-']='K' ['.-.']='R' ['--.']='G' ['-.']='N' ['..']='I' ['--']='M' ['.-']='A' ['  ']=' ' ['.']='E' ['-']='T' )

function decode {
  res=''
  tmp="$(sed 's/ \{2,\}/ | /g' <<< "$1")";
  for word in $tmp; do
    if [[ "$word" == '|' ]]; then
      res="${res}${MORSEDEC['  ']}";
    else
      res="${res}${MORSEDEC[$word]}";
    fi
  done
  printf "%s" "$res"
}
echo "$(decode "- .... .. ...   .. ...   ..-. .. -. .")"

See Bash demo online .

The easy answer in RE engines that support look-ahead and look-behind would be to treat the spaces as look-ahead and look-behind triggers, but sed does not support this.

Another option that avoids needing to order the letters is to inject extra symbols to help you mark each letter. Say we inject = round each space, then we can replace delimited sequences in any order, and finally get rid of the delimiters:

echo  .- .-.|sed -e 's/^\(.*\)$/=\1=/;s/ /= =/g' -e 's/=\.-\.=/=R=/g;s/=\.-=/=A=/g' -e 's/= =//g;s/^=//;s/=$//'

If you have rules that need to preserve multiple spaces, then that can be accommodated.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM