Reputation: 232
I have done a script which define a variable for each letters (for an encryption process), but I want spaces to be in the ouput.
I have this :
eiput_splitted="$(echo $einput | sed -e 's/\(.\)/\1\n/g')"
export eouput=""
for input in $eiput_splitted; do
export eouput=$eouput${!input}
//added code here
done
but I don't know how to detect spaces in the loop. I tried
if [ $input = \ ]
if [ $input = *\ * ]
if [ $input = *"\ "* ]
if [ $input = "*\ *" ]
if [ -z ${!input+x} ] (to detect unset variables, but doesn't seem to work..)
Can you help me please ?
Upvotes: 2
Views: 164
Reputation: 46853
Your design is not very good. You should not use plain variables like so! You'll run into problems if you want to encode special characters like _
, *
, é
, etc.
Instead, you should use associative arrays. Here's a full pure Bash working example:
#!/bin/bash
# read message to encode on stdin and outputs message to stdout
declare -A lookup
lookup=(
[a]=n [b]=o [c]=p [d]=q [e]=r [f]=s [g]=t [h]=u [i]=v [j]=w [k]=x
[l]=y [m]=z [n]=a [o]=b [p]=c [q]=d [r]=e [s]=f [t]=g [u]=h [v]=i
[w]=j [x]=k [y]=l [z]=m
)
while IFS= read -r -d '' -n 1 char; do
if [[ -z $char ]]; then
# null byte
printf '\0'
elif [[ ${lookup["$char"]} ]]; then
# defined character
printf '%s' "${lookup["$char"]}"
elif [[ $char = @(' '|$'\n') ]]; then
# space and newline
printf '%s' "$char"
else
# other characters passed verbatim with warning message
printf >&2 "Warning, character \`%s' not supported\n" "$char"
printf '%s' "$char"
fi
done
I called this script banana
, chmod +x banana
and:
$ ./banana <<< "hello stackoverflow"
uryyb fgnpxbiresybj
$ ./banana <<< "uryyb fgnpxbiresybj"
hello stackoverflow
If you want to encode the content of the variable input
and store the encoded text in the variable output
, just modify the main loop as so:
output=
linput=$input
while [[ $linput ]]; do
char=${linput::1}
linput=${linput:1}
if [[ ${lookup["$char"]} ]]; then
# defined character
output+=${lookup["$char"]}
elif [[ $char = @(' '|$'\n') ]]; then
# space and newline
output+=$char
else
# other characters passed verbatim with warning message
printf >&2 "Warning, character \`%s' not supported\n" "$char"
output+=$char
fi
done
In this case, I omitted the check for the null byte, as a Bash variable can't contain the null byte.
Proceeding like so, you can really encode any character you like (even null bytes—in the first version—and newline characters).
Edit (re your comment).
You want to use this for a Caesar cipher script. Your concern is that you prompt the user for the number used for the shift and you don't know how to use this method in your case. The key is that it's rather easy to generate the lookup table.
Here's a fully working example:
#!/bin/bash
chrv() {
local c
printf -v c '\\%03o' "$1"
printf -v "$2" "$c"
}
ansi_normal=$'\E[0m'
ansi_lightgreen=$'\E[02m'
while true; do
printf '%s' "Encryption key (number from ${ansi_lightgreen}1$ansi_normal to ${ansi_lightgreen}26$ansi_normal): "
read -re ekey
if [[ $ekey = +([[:digit:]]) ]]; then
((ekey=10#$ekey))
((ekey>=1 && ekey<=26)) && break
fi
printf 'Bad number. Try again.\n'
done
# Build the lookup table according to this number
declare -A lookup
for i in {0..25}; do
for u in 65 97; do
chrv "$((i+u))" basechar
chrv "$(((i+ekey-1)%26+u))" "lookup["$basechar"]"
done
done
printf '%s' "Input (only ${ansi_lightgreen}letters$ansi_normal and ${ansi_lightgreen}spaces${ansi_normal}): "
IFS= read -re einput
read -n1 -rep "Do you want output to be uppercase? ${ansi_lightgreen}(y/n)$ansi_normal " oup
[[ ${oup,,} = y ]] && einput=${einput^^}
output=
linput=$einput
while [[ $linput ]]; do
char=${linput::1}
linput=${linput:1}
if [[ ${lookup["$char"]} ]]; then
# defined character
output+=${lookup["$char"]}
elif [[ $char = @(' '|$'\n') ]]; then
# space and newline
output+=$char
else
# other characters passed verbatim with warning message
printf >&2 "Warning, character \`%s' not supported\n" "$char"
output+=$char
fi
done
printf 'Original text: %s\n' "$einput"
printf 'Encoded text: %s\n' "$output"
Pure Bash and no subshells :)
.
Upvotes: 1
Reputation: 189678
Because you don't quote the string, any whitespace in it will be lost. Try this instead.
echo "$einput" |
sed -e 's/\(.\)/\1\n/g' |
while IFS= read -r input; do
eouput="$eouput${!input}"
:
done
Depending on what your further processing looks like, I would perhaps just printf '%s' "${!input}"
inside the loop, and process its output in a pipeline after done
if necessary, to avoid yet another variable.
Upvotes: 1
Reputation: 247012
To iterate over the characters of a string, you can use parameter expansion:
eiput='Hello World!'
H="capital letter 'h'"
l="I appear 3 times"
strlen=${#eiput}
for ((i=0; i<strlen; i++)); do
char=${eiput:i:1}
printf "%2d:%c:%s\n" $i "$char" "${!char}"
done
0:H:capital letter 'h'
1:e:
2:l:I appear 3 times
3:l:I appear 3 times
4:o:
5: :
6:W:
7:o:
8:r:
9:l:I appear 3 times
10:d:
11:!:
Upvotes: 0
Reputation: 785581
You can use this in BASH:
while IFS= read -r ch; do
[[ "$ch" == " " ]] && echo "space character"
done < <(grep -o . <<< "$einput")
grep -o . <<< "$einput"
to split input string character by character< <(...)
is called process substitutionIFS=
to set input field separator to null (to allow space to be read by `read)Upvotes: 1
Reputation: 955
change the internal field seperator from spaces to newlines using IFS=$'\n'
.
Upvotes: 0