Problem Converting Morse Code to Text using sed - bash

I am kinda new to bash scripting so bear with me.
I have a task to convert text to morse code and vice versa from a given .txt file.
I successfully did the first part but when i try to run the second script to translate from morse to text i get this error:
sed: can't read s/.-/A/g: No such file or directory
S
My original code:
#!/bin/bash
sed 's/.-/A/g' -e 's/-.../B/g' -e 's/-.-./C/g' -e 's/-../D/g' -e 's/./E/g' -e 's/..-./F/g' -e 's/-
-./G/g' -e 's/..../H/g' -e 's/../I/g' -e 's/.---/J/g' -e 's/-.-/K/g' -e 's/.-../L/g' -e 's/--/M/g' -e
's/-./N/g' -e 's/---/O/g' -e 's/.--./P/g' -e 's/--.-/Q/g' -e 's/.-./R/g' -e 's/.../S/g' -e 's/-/T/g'
-e 's/..-/U/g' -e 's/...-/V/g' -e 's/.--/W/g' -e 's/-..-/X/g' -e 's/-.--/Y/g' -e 's/--../Z/g' -e
's/...../1/g' -e 's/....-/2/g' -e 's/...--/3/g' -e 's/....-/4/g' -e 's/...../5/g' -e 's/-..../6/g' -e
's/--.../7/g' -e 's/---../8/g' -e 's/----./9/g' -e 's/-----/0/g' Morse_Text.txt
The Morse_Text.txt file:
-- --- .-. … . -.- --- -.-. .

You have to escape (prepend a \) the . characters, as they have a special meaning in regular expressions.
Like this sed 's/\.-/A/g
Have a look at https://en.wikipedia.org/wiki/Regular_expression
Also you are missing the first -e after your sed command:
░ tamasgal#greybox.local:~
░ 16:37:45 > echo '... --- ...'| sed -e 's/\.\.\./S/g' -e 's/---/O/g'
S O S
Btw. you will hit more issues due to the fact sed works, it will replace parts of other morse codes during the stages.
You should consider including a leading space or new line after each group of morse command.
Here is an example in stages (decoding SOS9):
░ tamasgal#greybox.local:~
░ 16:43:47 > echo '... --- ... ----.'| sed -e 's/\.\.\./S/g'
S --- S ----.
░ tamasgal#greybox.local:~
░ 16:43:56 > echo '... --- ... ----.'| sed -e 's/\.\.\./S/g' -e 's/---/O/g'
S O S O-.
░ tamasgal#greybox.local:~
░ 16:44:00 > echo '... --- ... ----.'| sed -e 's/\.\.\./S/g' -e 's/---/O/g' -e 's/----\./9/g'
S O S O-.
One solution is to include a space or end of line character ($) to each group, like this:
░ tamasgal#greybox.local:~
░ 16:44:02 > echo '... --- ... ----. '| sed -e 's/\.\.\.[ $]/S/g' -e 's/---[ $]/O/g' -e 's/----\.[ $]/9/g'
SOS9
Btw. this is by far an ugly solution, but I'll not do your homework ;)

You have to make sure that the largest matches are replaced first.
It is convenient to store the commands in morse.sed like this:
s/-----/0/g
s/\.\.\.\.\./5/g
s/\.\.\.\.\./1/g
s/\.\.\.\.-/4/g
s/\.\.\.\.-/2/g
s/\.\.\.--/3/g
s/-\.\.\.\./6/g
s/--\.\.\./7/g
s/---\.\./8/g
s/----\./9/g
s/\.\.\.\./H/g
s/\.\.\.-/V/g
s/\.\.-\./F/g
s/\.-\.\./L/g
s/\.--\./P/g
s/\.---/J/g
s/-\.\.\./B/g
s/-\.\.-/X/g
s/-\.-\./C/g
s/-\.--/Y/g
s/--\.\./Z/g
s/--\.-/Q/g
s/\.\.\./S/g
s/\.\.-/U/g
s/\.-\./R/g
s/\.--/W/g
s/-\.\./D/g
s/-\.-/K/g
s/--\./G/g
s/---/O/g
s/\.\./I/g
s/\.-/A/g
s/-\./N/g
s/--/M/g
s/\./E/g
s/-/T/g
Now you can use
sed -Ef morse.sed Morse_Text.txt

Related

Escape sed command in Jenkins bash script

I have a Bash script working fine locally, now I am trying to put it in Jenkinsfile to run as its pipeline:
stage('Update Cloudfront'){
steps {
sh '''
#!/bin/bash
YAML_FILE="path/to/values.yaml"
DATE="$(date '+%d-%m-%Y')"
wget https://www.cloudflare.com/ips-v4 && wget https://www.cloudflare.com/ips-v6
CLOUDFLARE_NEW=$(awk '{printf fmt,$1}' fmt="%s\n" ips-v4 ips-v6 | paste -sd, -)
CLOUDFLARE_OLD=$(yq -r .controller.config.proxy-real-ip-cidr $YAML_FILE | sed -E 's/\,37\.16\.11\.30\/32//')
if [[ "$CLOUDFLARE_NEW" == "$CLOUDFLARE_OLD" ]]; then
echo "No need to do anything"
else
echo "Cloudflare IP ranges change detected, updating Nginx value file"
CLOUDFLARE_NEW=$(awk '{printf fmt,$1}' fmt="%s\n" ips-v4 ips-v6 | paste -sd, -) yq e '.controller.config.proxy-real-ip-cidr = env(CLOUDFLARE_NEW)' -i $YAML_FILE
echo "Add third party IP range"
yq e '.controller.config.proxy-real-ip-cidr +=",1.2.3.4/32"' -i $YAML_FILE
fi
'''
}
}//end stage('Update Cloudfront')
Unfortunately it won't work:
WorkflowScript: 73: unexpected char: '\' # line 73, column 113.
cidr $YAML_FILE | sed -E \\"s/\,37\.16\.
^
I've tried to escape it with \\"s/\,37\.16\.11\.30\/32//\\" etc. but it doesn't work either. I've tried with double and single quotes with no luck.
You can avoid all the escaping by using a character class and different regex delimiters, like so:
sed -e 's#,37[.]16[.]11[.]30/32##'
In the event you do need to escape something though, simply doubling the backslash should do it:
sed -e 's/,37\\.16\\.11\\.30\\/32//'
Though, given the number of levels involved here, it might need double escaping:
sed -e 's/,37\\\\.16\\\\.11\\\\.30\\\\/32//'

How do I include a variable in a ksh command subtitution?

I'm trying to find a number of lines that match a regex pattern in grep received as a variable. When I do the grep with the pattern directly in the command substitution, it works. When I use a variable for the pattern, it doesn't.
#!/bin/bash
pattern="'^\\\".*\\\"$'"
echo "pattern : $(echo $pattern)"
NB=$(grep -c -E -v -e ${pattern} abc.txt)
NB2=$(grep -v -c -E -e '^\".*\"$' abc.txt)
echo " -> $NB , $NB2"
Besides what's in the code, I've tried:
NB=$(grep -c -E -v -e $(echo $pattern) abc.txt)
No success.
cmd="grep -c -E -v -e ${pattern} abc.txt"
NB="$($cmd)"
No success.
In the example, abc.txt file contains 3 lines:
"abc"
"abc
abc"
The pattern in the variable seems ok:
pattern : '^\".*\"$'
I'm expecting that the 2 numbers in NB and NB2 are the same. If you look in the code, the actual result is:
pattern : '^\".*\"$'
-> 3 , 2
I expect:
pattern : '^\".*\"$'
-> 2 , 2
NB2=$(grep -v -c -E -e '^\".*\"$' abc.txt)
If that works, then assign that exact regex to $pattern. Don't add more backslashes and quotes.
pattern='^\".*\"$'
It's always a good idea to quote variable expansions to prevent unwanted wildcard expansion and word splitting.
NB=$(grep -c -E -v -e "${pattern}" abc.txt)
# ^ ^

Bash curly expansion with space in the string

I would like to use curly expansion to save some typing.
My desire expansion is:
-e uncore_imc0/cas_count_read/ -e uncore_imc1/cas_count_read/ -e uncore_imc2/cas_count_read/ -e uncore_imc3/cas_count_read/ -e uncore_imc4/cas_count_read/ -e uncore_imc5/cas_count_read/ -e uncore_imc6/cas_count_read/ -e uncore_imc7/cas_count_read/
I've tried:
-e uncore_imc{0..7}/cas_count_read/
but this only expand to (with -e only in the beginning)
-e uncore_imc0/cas_count_read/ uncore_imc1/cas_count_read/ uncore_imc2/cas_count_read/ uncore_imc3/cas_count_read/ uncore_imc4/cas_count_read/ uncore_imc5/cas_count_read/ uncore_imc6/cas_count_read/ uncore_imc7/cas_count_read/
If I tried:
{-e, uncore_imc{0..7}/cas_count_read/}
or
"-e uncore_imc{0..7}/cas_count_read/"
Neither would expand.
You can use printf:
printf -- '-e uncore_imc%d/cas_count_read/ ' {0..7}; echo
-e uncore_imc0/cas_count_read/ -e uncore_imc1/cas_count_read/ -e uncore_imc2/cas_count_read/ -e uncore_imc3/cas_count_read/ -e uncore_imc4/cas_count_read/ -e uncore_imc5/cas_count_read/ -e uncore_imc6/cas_count_read/ -e uncore_imc7/cas_count_read/
You can also store this expansion in a variable like this:
printf -v arg -- '-e uncore_imc%d/cas_count_read/ ' {0..7}
If this is really for a script, don't use brace expansion. You only have to write the code once, so readability should be a higher priority. Create an array instead:
opts=()
for((i=0; i < 8; i++)); do
opts+=(-e "uncore_imc$i/cas_count_read/")
done
someCommand "${opts[#]}"

Appending '-e' to a string in a bash script

I'm trying to write a bash script that iterates over the arguments and builds a string like the following:
Usage:
./myScript a b c d
Expected output:
-e "a" -e "b" -e "c" -e "d"
The script looks like the following:
#!/bin/bash
pattern=""
for arg in "$#" do
pattern=$pattern" -e \"$arg\""
done
echo $pattern
The actual output misses the first -e, i.e., the output is:
"a" -e "b" -e "c" -e "d"
What am I doing wrong? What is the correct way to append -e?
You are doing nothing wrong. It is just that echo takes -e as an argument *.
$ pattern='-e asdf -e ghjk'
$ echo $pattern
asdf -e ghjk
If you quote the variable it works as expected.
$ echo "$pattern"
-e asdf -e ghjk
* man echo
-e enable interpretation of backslash escapes

Using sed to replace a string with the contents of a variable, even if it's an escape character

I'm using
sed -e "s/\*DIVIDER\*/$DIVIDER/g" to replace *DIVIDER* with a user-specified string, which is stored in $DIVIDER. The problem is that I want them to be able to specify escape characters as their divider, like \n or \t. When I try this, I just end up with the letter n or t, or so on.
Does anyone have any ideas on how to do this? It will be greatly appreciated!
EDIT: Here's the meat of the script, I must be missing something.
curl --silent "$URL" > tweets.txt
if [[ `cat tweets.txt` == *\<error\>* ]]; then
grep -E '(error>)' tweets.txt | \
sed -e 's/<error>//' -e 's/<\/error>//' |
sed -e 's/<[^>]*>//g' |
head $headarg | sed G | fmt
else
echo $REPLACE | awk '{gsub(".", "\\\\&");print}'
grep -E '(description>)' tweets.txt | \
sed -n '2,$p' | \
sed -e 's/<description>//' -e 's/<\/description>//' |
sed -e 's/<[^>]*>//g' |
sed -e 's/\&amp\;/\&/g' |
sed -e 's/\&lt\;/\</g' |
sed -e 's/\&gt\;/\>/g' |
sed -e 's/\&quot\;/\"/g' |
sed -e 's/\&....\;/\?/g' |
sed -e 's/\&.....\;/\?/g' |
sed -e 's/^ *//g' |
sed -e :a -e '$!N;s/\n/\*DIVIDER\*/;ta' | # Replace newlines with *divider*.
sed -e "s/\*DIVIDER\*/${DIVIDER//\\/\\\\}/g" | # Replace *DIVIDER* with the actual divider.
head $headarg | sed G
fi
The long list of sed lines are replacing characters from an XML source, and the last two are the ones that are supposed to replace the newlines with the specified character. I know it seems redundant to replace a newline with another newline, but it was the easiest way I could come up with to let them pick their own divider. The divider replacement works great with normal characters.
You can use bash to escape the backslash like this:
sed -e "s/\*DIVIDER\*/${DIVIDER//\\/\\\\}/g"
The syntax is ${name/pattern/string}. If pattern begins with /, every occurence of pattern in name is replaced by string. Otherwise only the first occurence is replaced.
Maybe:
case "$DIVIDER" in
(*\\*) DIVIDER=$(echo "$DIVIDER" | sed 's/\\/\\\\/g');;
esac
I played with this script:
for DIVIDER in 'xx\n' 'xxx\\ddd' "xxx"
do
echo "In: <<$DIVIDER>>"
case "$DIVIDER" in (*\\*) DIVIDER=$(echo "$DIVIDER" | sed 's/\\/\\\\/g');;
esac
echo "Out: <<$DIVIDER>>"
done
Run with 'ksh' or 'bash' (but not 'sh') on MacOS X:
In: <<xx\n>>
Out: <<xx\\n>>
In: <<xxx\\ddd>>
Out: <<xxx\\\\ddd>>
In: <<xxx>>
Out: <<xxx>>
It seems to be a simple substitution:
$ d='\n'
$ echo "a*DIVIDER*b" | sed "s/\*DIVIDER\*/$d/"
a
b
Maybe I don't understand what you're trying to accomplish.
Then maybe this step could take the place of the last two of yours:
sed -n ":a;$ {s/\n/$DIVIDER/g;p;b};N;ba"
Note the space after the dollar sign. It prevents the shell from interpreting "${s..." as a variable name.
And as ghostdog74 suggested, you have way too many calls to sed. You may be able to change a lot of the pipe characters to backslashes (line continuation) and delete "sed" from all but the first one (leave the "-e" everywhere). (untested)
You just need to escape the escape char.
\n will match \n
\ will match \
\\ will match \
Using FreeBSD sed (e.g. on Mac OS X) you have to preprocess the $DIVIDER user input:
d='\n'
d='\t'
NL=$'\\\n'
TAB=$'\\\t'
d="${d/\\n/${NL}}"
d="${d/\\t/${TAB}}"
echo "a*DIVIDER*b" | sed -E -e "s/\*DIVIDER\*/${d}/"

Resources