Letter Index script in bash - bash

I am trying to write a script in bash to achieve a requirement described in the following example:
Eg: User inputs a sentence : "this is a book"
The Output must be "20 8 9 19 9 19 1 2 15 15 11" which is based on the alphabetical sequence.
Following is the code to achieve the following:
#!/bin/bash
echo "enter a sentence"
read sent
alpha=(" " {a..z})
for i in $(seq 27)
do
[ $i -eq 27 ] && break
eval ${alpha[$i]}=$i
done
eval echo $sent | tr -d [:punct:][:space:] | tr [:upper:] [:lower:] | fold -w1 | paste -sd " "| sed -e 's/\</\$/g'
Could someone guide me as to where i am going wrong or if there is a better way to approach the problem.

I know we should avoid doing full programs, but this was not a bad exercise:
#!/bin/bash
read -p "enter a sentence: "
sentence="$(echo $REPLY | tr '[A-Z]' '[a-z]' | tr -dc [:alpha:])"
for (( i=0; i<${#sentence}; i++ ));
do echo -n "$(($(printf "%d " "'${sentence:$i:1}") - 96 )) "
done
echo

Related

How can I use 'echo' output as an operand for the 'seq' command within a terminal?

I have an excercise where I need to sum together every digit up until a given number like this:
Suppose I have the number 12, I need to do 1+2+3+4+5+6+7+8+9+1+0+1+1+1+2.
(numbers past 9 are split up into their separate digits eg. 11 = 1+1, 234 = 2+3+4, etc.)
I know I can just use:
seq -s '' 12
which outputs 123456789101112 and then add them all together with '+' in between and then pipe to 'bc' BUT I have to specifically do :
echo 12 | ...
as the first step (because the online IDE fills it in as the unchangeable first step for every testcase) and when I do this I start to have problems with seq
I tried
echo 12 | seq -s '' $1
### or just ###
echo 12 | seq -s ''
but can't get it to work as this just gives back a missing operand error for seq (because I'm in the terminal, not a script and the '12' isn't just assigned to $1 I assume), any recommendations on how to avoid it or how to get seq to interpret the 12 from echo as operand or alternative ways to go?
seq -s '' $(cat)
full solution:
echo "12" | seq -s '' $(cat) | sed 's/./&+/g; s/$/0/' | bc
Or
echo 12 | { echo $(( $({ seq -s '' $(< /dev/stdin); echo; } | sed -E 's/([[:digit:]])/\1+/g; s/$/0/') )); }
without sed:
d=$(echo 12 | { seq -s '' $(< /dev/stdin); echo; }); echo $(( "${d//?/&+}0" ))
echo 12 | awk '{
cnt=0
for(i=1;i<=$1;i++) {
cnt+=i
printf("%s%s",i,i<$1?"+":"=")
}
print cnt
}'
Prints:
1+2+3+4+5+6+7+8+9+10+11+12=78
If it is supposed to be just the digits added up:
echo 12 | awk '{s=""
for(i=1;i<=$1;i++) s=s i
split(s,ch,"")
for(i=1;i<=length(ch); i++) cnt+=ch[i]
print cnt
}'
51
Or a POSIX pipeline:
$ echo 12 | seq -s '' "$(cat)" | sed -E 's/([0-9])/\1+/g; s/$/0/' | bc
51

How to find all non-dictionary words in a file in bash/zsh?

I'm trying to find all words in a file that don't exist in the dictionary. If I look for a single word the following works
b=ther; look $b | grep -i "^$b$" | ifne -n echo $b => ther
b=there; look $b | grep -i "^$b$" | ifne -n echo $b => [no output]
However if I try to run a "while read" loop
while read a; do look $a | grep -i "^$a$" | ifne -n echo "$a"; done < <(tr -s '[[:punct:][:space:]]' '\n' <lotr.txt |tr '[:upper:]' '[:lower:]')
The output seems to contain all (?) words in the file. Why doesn't this loop only output non-dictionary words?
Regarding ifne
If stdin is non-empty, ifne -n reprints stdin to stdout. From the manpage:
-n Reverse operation. Run the command if the standard input is empty
Note that if the standard input is not empty, it is passed through
ifne in this case.
strace on ifne confirms this behavior.
Alternative
Perhaps, as an alternative:
1 #!/bin/bash -e
2
3 export PATH=/bin:/sbin:/usr/bin:/usr/sbin
4
5 while read a; do
6 look "$a" | grep -qi "^$a$" || echo "$a"
7 done < <(
8 tr -s '[[:punct:][:space:]]' '\n' < lotr.txt \
9 | tr '[A-Z]' '[a-z]' \
10 | sort -u \
11 | grep .
12 )

How to cut variables which are beteween quotes from a string

I had problem with cut variables from string in " quotes. I have some scripts to write for my sys classes, I had a problem with a script in which I had to read input from the user in the form of (a="var1", b="var2")
I tried the code below
#!/bin/bash
read input
a=$($input | cut -d '"' -f3)
echo $a
it returns me a error "not found a command" on line 3 I tried to double brackets like
a=$(($input | cut -d '"' -f3)
but it's still wrong.
In a comment the OP gave a working answer (should post it as an answer):
#!/bin/bash
read input
a=$(echo $input | cut -d '"' -f2)
b=$(echo $input | cut -d '"' -f4)
echo sum: $(( a + b))
echo difference: $(( a - b))
This will work for user input that is exactly like a="8", b="5".
Never trust input.
You might want to add the check
if [[ ${input} =~ ^[a-z]+=\"[0-9]+\",\ [a-z]+=\"[0-9]+\"$ ]]; then
echo "Use your code"
else
echo "Incorrect input"
fi
And when you add a check, you might want to execute the input (after replacing the comma with a semicolon).
input='testa="8", testb="5"'
if [[ ${input} =~ ^[a-z]+=\"[0-9]+\",\ [a-z]+=\"[0-9]+\"$ ]];
then
eval $(tr "," ";" <<< ${input})
set | grep -E "^test[ab]="
else
echo no
fi
EDIT:
#PesaThe commented correctly about BASH_REMATCH:
When you use bash and a test on the input you can use
if [[ ${input} =~ ^[a-z]+=\"([0-9]+)\",\ [a-z]+=\"([0-9])+\"$ ]];
then
a="${BASH_REMATCH[1]}"
b="${BASH_REMATCH[2]}"
fi
To extract the digit 1 from a string "var1" you would use a Bash substring replacement most likely:
$ s="var1"
$ echo "${s//[^0-9]/}"
1
Or,
$ a="${s//[^0-9]/}"
$ echo "$a"
1
This works by replacing any non digits in a string with nothing. Which works in your example with a single number field in the string but may not be what you need if you have multiple number fields:
$ s2="1 and a 2 and 3"
$ echo "${s2//[^0-9]/}"
123
In this case, you would use sed or grep awk or a Bash regex to capture the individual number fields and keep them distinct:
$ echo "$s2" | grep -o -E '[[:digit:]]+'
1
2
3

Is there a command that works for command line arguments like the sort command does for files?

I am trying to write a script in BASH that will take between 1 and 5 command line arguments from the user and report them back in reverse numerical order to standard output. The only command I know that would work similarly to this is the sort command, but this only works for files. Is there a similar command for sorting command line arguments? Here is what I have so far.
#!/bin/bash
if [ $# -lt 1 ] || [ $# -gt 5 ];
then echo "Incorrect number of arguments!"
else
sorted=sort -rn $*
echo "SORTED: $sorted"
fi
Try:
sorted=$( printf '%s\n' "$#" | sort -rn )
printf '%s\n' "${sorted//$'\n'/ }"
You can give the sort command values from standard input. It expects every value on its own line, which you can achieve by combining echo and tr:
sorted=$(echo $* | tr ' ' '\n' | sort -rn - | tr '\n' ' ')
The last invocation of tr is only necessary if you want the result to be space-delimited again and not newline-delimited.
#!/bin/bash
if [ $# -lt 1 ] || [ $# -gt 5 ];
then echo "Incorrect number of arguments!"
else
sorted=$(echo $* | tr ' ' '\n' | sort -rn | tr '\n' ' ')
echo "SORTED: $sorted"
fi
echo $* | tr ' ' '\n' | sort -rn | tr '\n' ' '
You need to use command substitution $(...) to capture the output of a command like that.
#!/bin/bash
if [ $# -lt 1 ] || [ $# -gt 5 ]; then
echo "Incorrect number of arguments!"
else
sorted=$(for var in "$#"; do echo "$var"; done | sort -rn | tr -d '\n')
echo "SORTED: $sorted"
fi
$ ./test 1 2 3 4 5
SORTED: 5 4 3 2 1
$ ./test 5 4 3 2 1
SORTED: 5 4 3 2 1

Bash - invalid arithmetic operator

I'm trying to study for a test and one of the subjects are bash scripts.
I have the following txt file :
123456 100
654321 50
203374111 86
I need to get the averages of the scores (the numbers in the second column).
This is what I have written :
cat $course_name$end | while read line; do
sum=`echo $line | cut -f2 -d" "`
let total+=$sum
done
I have tried with
while read -a line
and then
let sum+=${line[1]}
But I'm still getting the same error mentioned in the header.
I love AWK:
awk -F\* '{sum+=$3} END {print sum/NR}' x.txt
So in x.txt are values are stored. Please note that many answers don't actually compute the average, as they need to divide by the line numbers in the end. Often it will be performed by a wc -l < x.txt but in my solution you will get it almost for free.
cat your_file_name.txt | cut -f2 -d" " | paste -sd+ | bc
This should do the job!
You are very close, this works for me:
while read line; do
sum=$(echo $line | cut -f2 -d" ")
echo "sum is $sum"
let total+=$sum
echo "total is $total"
done < file
echo "total is $total"
As you can see, there is no need to use cat $course_name$end, it is enough to do
while read line
do
done < file
Also, it is more recommendable to use
sum=$(echo $line | cut -f2 -d" ")
rather than
sum=`echo $line | cut -f2 -d" "`
Or even
sum=$(cut -f2 -d" " <<< "$line")
There's no need to use cat as well as read; you can redirect the contents of the file into the loop. You also don't need to use let for arithmetic.
sum = 0
count = 0
while read id score; do
(( sum += score )) && (( ++count ))
done < "$course_name$end"
echo $(( sum / count ))
This will give you an integer result, as bash doesn't do floating point arithmetic. To get a floating point result, you could use bc:
bc <<< "scale=2;$a/$b"
This will give you a result correct to 2 decimal places.

Resources