Continuing my quest to learn BASH, Bourne, Awk, Grep, etc. on my own through the use of a few books. I've come to an exercise that has me absolutely stumped.
The specifics:
1. Using ONLY BASH scripting commands (not sed, awk, etc.), write a script to convert a string on the command line to Morse code. The script will handle only capital letters and numbers
2. It will convert the string given on the command line as in the
following example:
$ ./morse.bash “CAT IN”
-.-.,.-,-,SP,..,-.,EOT (SP is used for a space, and EOT for end of transmission).
3. An associative array is to be used for the “lookup table” to do the
conversion from a character to Morse Code.
This is my time using an associative array, so this exercise has me stumped. Any help, guidance, suggestions are greatly appreciated. Here's what I have so far:
...
If i did, it was by accident just because i had fun and got excited as it was and is also a challenge to me.
And it is a communication tool, so the 'encoding' must be possible both ways.
Code:
morse SOS. "1 2 3." "THIS IS A TEST."
...;---;...;STOPSP.----;SP..---;SP...--;STOPSP-;....;..;...;SP..;...;SP.-;SP-;.;...;-;STOPSPEOT
Code:
morse -l "...;---;...;STOPSP.----;SP..---;SP...--;STOPSP-;....;..;...;SP..;...;SP.-;SP-;.;...;-;STOPSPEOT"
SOS
STOP
1
2
3
STOP
THIS
IS
A
TEST
STOP
EOT
Ok, vice-versa encoding is working now:
Code:
morse -q SOS. "1 2 3." "THIS IS A TEST."|while read line;do morse -l "$line";done
SOS
STOP
1
2
3
STOP
THIS
IS
A
TEST
STOP
EOT
This stuff is really a good training excercise for shuffeling and parsinge strings and chars.
But by now, WAY too many hours invested in this, mostly just to get the output/handling as it looks now...
Code:
#!/bin/bash
# ------------------------------------------------------------------------
#
# Copyright (c) 2014 by Simon Arjuna Erat (sea) <erat.simon@gmail.com>
# All rights reserved.
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
#
# ------------------------------------------------------------------------
#
# Description: Translates STRING to MORSE and vice-versa
# Resource: https://www.unix.com/shell-programming-and-scripting/253424-morse-code-associative-array.html
# Resource: http://en.wikipedia.org/wiki/Morse_code
# Original author: ksmarine1980
#
# Variables
#
ME=${0##*/}
ME=${ME/.sh/}
script_version=0.6
declare -A morse # Declare associative array
declare -A letter # Declare associative array
declare -u input # Force upper case
SKIP=0 # Skip this many 'chars' :: SP, print space at SKIP=1
STRING="" # Contains the current signs for a char
NL="" # Newline for final linebreak: EOT
LB="" # Fixed linebreak after a word?
EOT=true # Show EOT after each call, very annoying while reading a file with just a word list
morse[A]=".-" ; morse[B]="-..." ; morse[C]="-.-."
morse[D]="-.." ; morse[E]="." ; morse[F]="..-."
morse[G]="--." ; morse[H]="...." ; morse[I]=".."
morse[J]=".---" ; morse[K]="-.-" ; morse[L]=".-.."
morse[M]="--" ; morse[N]="-." ; morse[O]="---"
morse[P]=".--." ; morse[Q]="--.-" ; morse[R]=".-."
morse[S]="..." ; morse[T]="-" ; morse[U]="..-"
morse[V]="...-" ; morse[W]=".--" ; morse[X]="-..-"
morse[Y]="-.--" ; morse[Z]="--.." ; morse[1]=".----"
morse[2]="..---"; morse[3]="...--"; morse[4]="....-"
morse[5]="....."; morse[6]="-...."; morse[7]="--..."
morse[8]="---.."; morse[9]="----."; morse[0]="-----"
morse[ ]="SP" ; morse[.]="STOP"
#
# Options
#
for A in "${@}"
do case "$A" in
-l) LB="\n"
shift ;;
-q) EOT=false
shift ;;
-h|--help)
echo "
$ME ($script_version)
Usage: $ME [options] \"STRING\"
\"STRING\" can be either a word or morse code.
Either way it could also be a line-feed.
Where options are:
[-l] Print one word per line
[-q] Quiet, print no EOT upon exit
"
exit
;;
esac
done
#
# Functions OR the Array
#
# ${letter["MORSECODE"]} vs. $(morse2char "MORSECODE")
# Note that i used the function since i had forgotten to declare the 'letter' array at first,
# so it didnt work upon the first try.
#
morse2char() { # "...--"
# Expects a string with dots and dashes which as whole represents a single character
# Prints the according letter, or a question mark if nothing matches
CODE="$1"
case "$CODE" in
".-") printf A ;;
"-...") printf B ;;
"-.-.") printf C ;;
"-..") printf D ;;
".") printf E ;;
"..-.") printf F ;;
"--.") printf G ;;
"....") printf H ;;
"..") printf I ;;
".---") printf J ;;
"-.-") printf K ;;
".-..") printf L ;;
"--") printf M ;;
"-.") printf N ;;
"---") printf O ;;
".--.") printf P ;;
"--.-") printf Q ;;
".-.") printf R ;;
"...") printf S ;;
"-") printf T ;;
"..-") printf U ;;
"...-") printf V ;;
".--") printf W ;;
"-..-") printf X ;;
"-.--") printf Y ;;
"--..") printf Z ;;
".----") printf 1 ;;
"..---") printf 2 ;;
"...--") printf 3 ;;
"....-") printf 4 ;;
".....") printf 5 ;;
"-....") printf 6 ;;
"--...") printf 7 ;;
"---..") printf 8 ;;
"----.") printf 9 ;;
"-----") printf 0 ;;
"SP") printf " " ;;
# NOTE that for 'style' reasons, i wanted to keep the word 'STOP' when printing words.
#"STOP") printf "." ;;
*) printf "$CODE?" ;;
esac
}
letter[".-"]=A ; letter["-..."]=B; letter["-.-."]=C
letter["-.."]=D ; letter["."]=E ; letter["..-."]=F
letter["--."]=G ; letter["...."]=H; letter[".."]=I
letter[".---"]=J; letter["-.-"]=K ; letter[".-.."]=L
letter["--"]=M ; letter["-."]=N ; letter["---"]=O
letter[".--."]=P; letter["--.-"]=Q; letter[".-."]=R
letter["..."]=S ; letter["-"]=T ; letter["..-"]=U
letter["...-"]=V; letter[".--"]=W ; letter["-..-"]=X
letter["-.--"]=Y; letter["--.."]=Z; letter[".----"]=1
letter["..---"]=2; letter["...--"]=3; letter["....-"]=4
letter["....."]=5; letter["-...."]=6; letter["--..."]=7
letter["---.."]=8; letter["----."]=9; letter["-----"]=0
letter["SP"]=" "; letter["STOP"]="."
#
# Action
#
[[ -z $LB ]] && NL="" || NL="$LB"
while [ $# -gt 0 ];do
# Now that the options are parsed, we can set the input to the current argument
input="$1"
if [[ "." = "${input:0:1}" ]] || [[ "-" = "${input:0:1}" ]]
then # Its morse to letters
for (( i = 0; $i < "${#input}"; i = $i +1 ))
do CHAR="${input:$i:1}"
# Enable NewLine if LineBreak is active
[[ -z $LB ]] && NL="" || NL="$LB"
if [[ $SKIP -gt 0 ]]
then # Skip this many times
[[ -z $LB ]] && NL="" || NL="$LB"
STRING+="$CHAR"
SPACE=" "
# Do action according to SPECIAL word, or part of it.
case "$STRING" in
SP) printf "%s" "$(morse2char $STRING)"
STRING=""
SPACE="" ;;
ST) SKIP=2 ;;
STOP) printf "%s" "$(morse2char $STRING)"
#printf "$NL"
STRING="" ;;
esac
[[ $SKIP -eq 1 ]] && printf "$SPACE$NL" && NL=""
SKIP=$(($SKIP-1))
else # Decide action upon current 'CHAR', or SIGN respectivly
case "$CHAR" in
S) # Make sure there is no accidental line break
SKIP=1
STRING="$CHAR"
;;
E) # Make line break at End Of Transmission
SKIP=3 ; NL="\n"
;;
";") # Word-end, print it and clean it
[[ -z "$STRING" ]] || printf $(morse2char "$STRING")
STRING=""
;;
*) # Add char to word
STRING+="$CHAR"
;;
esac
fi
# Print STOP and optional a new line, if linebreak is active
# Note that for style reasons i want to keep the word STOP, rather than the dot char/sign.
[[ STOP = $STRING ]] && printf "$STRING$NL" && STRING=""
done
# 'WORD' is done...
# Print a new line, if linebreak is active
printf "$NL"
else # Its letters to morse
for (( i = 0; $i < "${#input}"; i = $i +1 ))
do N=${input:$i:1}
case "$N" in
" ") printf "%s$NL" "SP" ;;
".") printf "%s$NL" "STOP" ;;
*) printf "%s" "${morse[$N]};" ;;
esac
done
# Print 'SP'ace and a new line, if linebreak is active
printf "SP$LB"
fi
# Skip to next 'WORD', print a new line, if linebreak is active
printf "$NL"
# Remove current argument $1
shift
done
# All is done, show EOT unless its quiet (helps while reading lines from a text file)
$EOT && printf "$NL%s\n" EOT || printf "\n"
Play with it, change the usage of the function morse2char to be using the letter["..."] array.
Make your own experiments.
Make one script to encode, and another to decode, then try to combine them.
I've run into one small issue with the space (SP) character. On the command line, if I type ./morse.bash "CAT IN" the output is -.-.,.-,-,SP..,-.,EOT. I can't figure out why two .. are following the SP. Here's my code:
This one accepts input from a pipe or interactive stdin or with a filename as argument. See Usage:
I'm struggling with strings as arguments to the command. It throws an error if $1 is not a filename. I managed to get it to accept option "-h", but dictionary words don't fly (yet).
Code:
#!/bin/bash
#
# mors2.sh -- requires Bash 4+
usage ()
{
echo " Usage:
Command line mode -- no quotes needed:
\$ `basename $0` -- Type text/code [Enter]
["=" key] [ctrl+C] [ctrl+D] to exit this mode
Pipe modes:
\$ `basename $0` [filename] [options]
\$ echo [morse string] [text string] | `basename $0`
\$ cat [filename] | `basename $0`
Options:
-h) This message
*)"
exit
}
declare -A alpha #Declare associative array
declare -A morse #Declare associative array
declare -u text #Force upper case
alpha=(
[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]='--..'
[0]='-----' [1]='.----' [2]='..---' [3]='...--' [4]='....-'
[5]='.....' [6]='-....' [7]='--...' [8]='---..' [9]='----.'
)
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'
[-----]='0' [.----]='1' [..---]='2' [...--]='3' [....-]='4'
[.....]='5' [-....]='6' [--...]='7' [---..]='8' [----.]='9'
)
textin ()
{
for text in "$@";
do
for (( i = 0; $i < ${#text}; i = $i +1 ));
do
N=${text:$i:1}
printf "$txt_fmt" "${alpha[$N]}" "$N"
done
done
}
morsein ()
{
for code in "$@";
do
if [ $code == $wrd_sep ]; then
echo 'SP'
else
printf "$cod_fmt" "$code" "${morse[$code]}"
fi
done
}
pipein ()
{
printf " %s\n\n" "#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# Type text and/or morse code.
# When mixing morse code and text, separate words with '+'.
# Enter '=' to exit.
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"
declare -a ary
while read line
do
[[ $line =~ ^$xit ]] && echo "Bye" && exit
ary=($(echo $line | tr ' ' '\n'))
sz=${#ary[*]}
for wrds in ${ary[*]};
do
I=${wrds:0:1}
if [[ "$I" =~ ^[[:alnum:]] ]]; then
textin "$wrds"
[[ $sz -gt 1 || ${wrds: -1} == '.' ]] && echo 'SP'
elif [[ $I =~ ^[-|.|$wrd_sep] ]]; then
morsein "$wrds"
fi
sz=$(( $sz -1 ))
done
printf "%s\n\n" "EOT"
# done < "${1:-/proc/${$}/fd/0}"
done < ${1:-/dev/stdin}
echo " Bye"
}
options ()
{
case "$@" in
-h) usage ;;
-*) usage ;;
esac
}
# Start here
############
txt_fmt=" %s\t%s\n"
cod_fmt=" %s\t%s\n"
wrd_sep='+' # char used to separate words when typing morse code
xit='=' # char to exit interactive console mode
if [[ $1 =~ ^-[[:alpha:]]$ ]]; then
options "$1"
else
pipein "$@"
fi
exit
# eof #
Last edited by ongoto; 12-02-2014 at 04:15 AM..
Reason: re-wrote parts to better handle 'SP'
Barry, i couldnt resist and made some changes
Also, after some tries, figured, i cannot differ signs (.-) from chars (a-z) or words, without the use of some delaying sleep calls...
Code:
time morse -l "Play it again barry." | while read line; do morse-snd "$line";done
You now hear: .--.;.-..;.-;-.--;SP
You now hear: ..;-;SP
You now hear: .-;--.;.-;..;-.;SP
You now hear: -...;.-;.-.;.-.;-.--;STOP
You now hear: SP
You now hear: EOT
real 0m33.488s
user 0m0.220s
sys 0m0.315s
Code:
+ ~ $ morse "Play it again barry."
.--.;.-..;.-;-.--;SP..;-;SP.-;--.;.-;..;-.;SP-...;.-;.-.;.-.;-.--;STOPSPEOT
+ ~ $ morse-snd ".--.;.-..;.-;-.--;SP..;-;SP.-;--.;.-;..;-.;SP-...;.-;.-.;.-.;-.--;STOPSPEOT"
You now hear: .--.;.-..;.-;-.--;SP..;-;SP.-;--.;.-;..;-.;SP-...;.-;.-.;.-.;-.--;STOPSPEOT
+ ~ $
[/CODE]
Code:
#!/bin/bash
# Working idea, Barry walker, G0LCU...
# Modified by Simon Arjuna Erat, sea
# Morse.sh --> morse-sox aka morse-snd
# Sounder for Morse at about 8 WPM...
# Output can be used to modulate an SSB HF transceiver...
# OSX 10.7.5, default bash terminal AND SOX.
# Just as easily do cat /tmp/sample.raw > /dev/dsp for machines with /dev/dsp.
WAIT_SIGN=0.05
WAIT_CHAR=0.5
WAIT_WORD=1.5
WAIT_SPACE=2
SOX=$(which sox||locate sox)
if [[ ! -f /tmp/dit.raw ]]
then data="\\x80\\x26\\x00\\x26\\x7F\\xD9\\xFF\\xD9"
sample=0
# Create files & permission
for F in dit dah;do
> /tmp/$F.raw
chmod 644 /tmp/$F.raw
done
# Increase data string
for sample in {0..6} ; do data=$data$data ;done
# Do not touch Barrys creations
printf "$data" >> /tmp/dit.raw
printf "$data" >> /tmp/dah.raw
printf "$data" >> /tmp/dah.raw
printf "$data" >> /tmp/dah.raw
fi
play_dot(){ $SOX -r 8000 -b 8 -c 1 -e unsigned-integer /tmp/dit.raw -d > /dev/null 2>&1 ; }
play_dash(){ $SOX -r 8000 -b 8 -c 1 -e unsigned-integer /tmp/dah.raw -d > /dev/null 2>&1 ; }
for input in $@;do
printf "You now hear: " #$input"
case "${input:2}" in
"SP") printf "SP"
sleep $WAIT_SPACE ;;
"ST") printf "STOP"
sleep $WAIT_WORD ;;
*) for (( i = 0; $i < "${#input}"; i = $i +1 ))
do printf "${input:$i:1}"
case "${input:$i:1}" in
"-") play_dash ;;
".") play_dot ;;
";") sleep $WAIT_CHAR ;;
esac
sleep $WAIT_SIGN
done
;;
esac
printf "\nNew word..."
sleep $WAIT_WORD
printf "\r \r"
done
Hello.
I have an export of an associative array build using declare -p SOME_ARRAY_NAME > SOME_FILE_NAME.txt.
Producing some thing like declare -A SOME_ARRAY_NAME=( ="some_text" ="a_text" ......... ="another_text" ) in a text file.
I have a stock of functions which are sourced from... (1 Reply)
I am trying to assign indexes to an associative array in a for loop but I have to use an eval command to make it work, this doesn't seem correct I don't have to do this with regular arrays
For example, the following assignment fails without the eval command:
#! /bin/bash
read -d "\0" -a... (19 Replies)
Hello together,
i make something wrong... I want an array that contains information to associate it for further processing.
Here is something from my bash... You will know, what I'm trying to do.
I have to point out in advance, that the variable $SYSOS is changing and not as static as in my... (2 Replies)
Hi all
I have a problem where i have a large list ( up to 1000 of items) and need to have 2 items pulled from it into variables in a bash script
my list is like the following and I could have it as an array or possibly an external text file maintained separately. Every line is different and... (6 Replies)
I have an associative array named table
declare -A table
table="fruit"
table="veggie"
table="GT"
table="eminem"
Now say I have a variable returning the value highway
How do I find corresponding value GT ??
(this value that I find (GT in this case) is supposed to be the name of a mysql... (1 Reply)
Hi All,
I got stuck up with shell script where i use awk. The scenario which i am working on is as below.
I have a file text.txt with contents
COL1 COL2 COL3 COL4
1 A 500 400
1 B 500 400
1 A 500 200
2 A 290 300
2 B 290 280
3 C 100 100
I could able to sum col 3 and col4 based on... (3 Replies)
i have a file like this
< '393200103052';'H3G';'20081204'
< '393200103059';'TIM';'20110111'
< '393200103061';'TIM';'20060206'
< '393200103064';'OPI';'20110623'
> '393200103052';'HKG';'20081204'
> '393200103056';'TIM';'20110111'
> '393200103088';'TIM';'20060206'
Now i have to generate a file... (9 Replies)
Most shells flavors do not have associative arrays a.k.a. maps.
How would you emulate an associative array?
I had this problem once and found a working solution, but I don't want to spoil the game hence I wont tell it.
Wonder if anyone comes up with something better. (5 Replies)
Hi,
When using sort on an associative array:
foreach $key (sort(keys(%opalfabet))){
$value = $opalfabet{$key};
$result .= $value;
}
How does it handle double values?
It seems to me that it removes them, is that true? If so, is there a way to get... (2 Replies)
Hi,
I am trying to make an associative array to use in a popup_menu on a website. Here is what i have:
foreach $entr ( @entries )
{
$temp_uid = $entr->get_value(uid);
$temp_naam = $entr->get_value(sn);
$s++;
}
This is the popup_menu i want to use it in.
popup_menu(-name=>'modcon',... (4 Replies)