im working to make my code take an input (word) and output the sum of all the letters in the input, the letters would be equal to there numeric value- a=1 b=2 c=3 etc,here is my unfinished code so far:-
echo enter word
read word
for let in $word
do
echo $let
*here is where it should take the input and calculate the output (w+o+r+d = ??)
Here's a solution that uses an associative array to map (English) letters to their ordinal values. Note that associative arrays require bash 4.0 or higher.
#!/usr/bin/env bash
# Declare variables.
declare -i i sum # -i declares integer variables
declare -A letterValues # -A declares associative arrays, which requires bash 4+
declare letter # a regular (string) variable
# Create a mapping between letters and their values
# using an associative array.
# The sequence brace expression {a..z} expands to 'a b c ... z'
# $((++i)) increments variable $i and returns the new value.
i=0
for letter in {a..z}; do
letterValues[$letter]=$((++i))
done
# Prompt for a word.
read -p 'Enter word: ' word
# Loop over all chars. in the word
# and sum up the individual letter values.
sum=0
for (( i = 0; i < ${#word}; i++ )); do
# Extract the substring of length 1 (the letter) at position $i.
# Substring indices are 0-based.
letter=${word:i:1}
# Note that due to having declared $sum with -i,
# surrounding the following statement with (( ... ))
# is optional.
sum+=letterValues[$letter]
done
# Output the result.
echo "Sum of letter values: $sum"
To iterate over the characters of a string, do this:
string="hello world"
for ((i=0; i < ${#string}; i++)); do
char=${string:i:1} # substring starting at $i, of length 1
echo "$i -> '$char'"
done
0 -> 'h'
1 -> 'e'
2 -> 'l'
3 -> 'l'
4 -> 'o'
5 -> ' '
6 -> 'w'
7 -> 'o'
8 -> 'r'
9 -> 'l'
10 -> 'd'
Related
Problem: I need to input two vars on BASH by space:
How to split it by space?
On Python i can use function "split":
a, b = input().split(" ")
Input data:
12 14
1
2
3
...
I can't read "12" and "14"
I want to use "read":
read a
read b
or
read string
a, b = string.split()
I know that in bash split does not works :(
read itself splits the line it reads, using the value of IFS, to produce as many values as needed for the number of arguments given. The default value of IFS will split on arbitrary whitespace.
$ read a b <<< "1 2"
$ echo "$a"
1
$ echo "$b"
2
I have string="1,2,3,4,5,6,7,8" I want to split string in 2 different variable with unique values as below
string_p1="1,2,3,4,5" string_p2="6,7,8"
Here i dont want any specific defined logic while splitting variable any random splitting is okay.
but i need to ensure that i am not missing any number present in variable string
Please suggest bash script to get the above results ?
One idea using an array and some basic bash string manipulation:
string='1,2,3,4,5,6,7,8'
IFS=, arr=( ${string} ) # break on comma delimiter, store in array
Result:
$ typeset -p arr
declare -a arr=([0]="1" [1]="2" [2]="3" [3]="4" [4]="5" [5]="6" [6]="7" [7]="8")
$ len="${#arr[#]}"
8
To generate a random number between 0 and 8 (length of arr[]), eg:
$ n=$(( RANDOM % len ))
$ echo $n
2 # obviously this will vary between 0 and 8 based on value of $RANDOM
Build our final strings:
$ string_p1=${arr[#]:0:n}
$ string_p2=${arr[#]:n}
$ typeset -p string_p1 string_p2
declare -- string_p1="1 2"
declare -- string_p2="3 4 5 6 7 8"
Now replace spaces with commas:
$ string_p1="${string_p1// /,}"
$ string_p2="${string_p2// /,}"
$ typeset -p string_p1 string_p2
declare -- string_p1="1,2"
declare -- string_p2="3,4,5,6,7,8"
NOTE: because the split is based solely on $RANDOM there is a good chance one of the resulting strings can be empty (eg, n=0); OP can add more logic to address this issue as needed (eg, if n=0 then set n=1 to ensure string_p1 is not empty)
Taking for a spin with a different input:
string='3,24,666.83,2,9,0,34,23,45,12,1'
IFS=, arr=( ${string} )
len="${#arr[#]}"
n=$(( RANDOM % len ))
string_p1=${arr[#]:0:n}
string_p2=${arr[#]:n}
string_p1="${string_p1// /,}"
string_p2="${string_p2// /,}"
typeset -p len n string_p1 string_p2
This generates:
declare -- len="11"
declare -- n="8"
declare -- string_p1="3,24,666.83,2,9,0,34,23"
declare -- string_p2="45,12,1"
I tried with this:
string="1,2,3,4,5,6,7,8"
echo ${string:0:${#string}/2}
echo ${string:${#string}/2}
and it splits the string in half, this is the output:
1,2,3,4
,5,6,7,8
I have a long string file (string.txt) (abcdefghijklmnop)
and a vcf table (file.vcf) which lools like that
position 2 4 6 10 n...
name1 a b c d
name2 x y z a
namen...
the table also contain "mis" and "het" and in this case the character should not be replaced
I want to change the characters in the specific location and store all the strings in a new file that will look like this
>name1
aacbecghidklmnop
>name2
axcyezghiaklmnop
is there a way to do it in a bash loop ?
Would you please try the following:
mapfile -t string < <(fold -w1 "string.txt")
# set string to an array of single characters: ("a" "b" "c" "d" ..)
while read -ra ary; do
if [[ ${ary[0]} = "position" ]]; then
# 1st line of file.vcf
declare -a pos=("${ary[#]:1}")
# now the array pos holds: (2 4 6 10 ..)
else
# 2nd line of file.vcf and after
declare -a new=("${string[#]}")
# make a copy of string to modify
for ((i=0; i<${#pos[#]}; i++ )); do
repl="${ary[$i+1]}" # replacement
if [[ $repl != "mis" && $repl != "het" ]]; then
new[${pos[$i]}-1]="$repl"
# modify the position with the replacement
fi
done
echo ">${ary[0]}"
(IFS=""; echo "${new[*]}")
# print the modified array as a concatenated string
fi
done < "file.vcf"
string.txt:
abcdefghijklmnop
file.vcf:
position 2 4 6 10
name1 a b c d
name2 x y z a
name3 i mis k l
Output:
>name1
aacbecghidklmnop
>name2
axcyezghiaklmnop
>name3
aicdekghilklmnop
I have tried to embed explanations as comments in the script above, but
if you still have a question, please feel free to ask.
Hope this helps.
Taking count from file, say if count = 5, I want to print 5 variables. i.e. A B C D E.
If count = 2, Print 2 variables A B, etc.
I have tried using the ASCII values but couldn't go through it.
for i in {1..5}; do
count=5; a=0;
printf "\x$(printf %x '65+$a')";
count=count+1;
done
if count = 5, I want to print 5 variables. i.e. A B C D E. If count = 2, Print 2 variables A B, etc.
Here's a program that matches your style that does what you are looking for:
a=0
for i in {1..5}; do
printf "\x$(printf %x $(( 65 + a )) )";
a=$((a+1));
done
The first thing to note is that in order to do math in bash, you'll need to use the $(( )) operation. Above, you can see I replaced you '65+$a' with $(( 65 + a )) . That's the big news that you need to get math done.
There were a couple of other little issues, but you were stuck on the $(()) stuff so they weren't clear yet. Incidentally, the 'a' variable can be completely removed from the program to just use the 'i' variable like this:
for i in {1..5}; do
printf "\x$(printf %x $(( 64 + i )) )";
done
I had to change the constant to 64 since we are now counting starting at 1.
The {1..5} expression is a good short cut for 1 2 3 4 5, but you won't be able to put a variable into it. So, if you need to add a count variable back in, consider using the seq program instead like this:
count=$1
for i in $(seq 1 $count); do
printf "\x$(printf %x $(( 64 + i )) )";
done
Note that $() is different than the math operator $(()). $() runs a subcommand returning the results.
method 1: simple brace expansion
#!/bin/bash
# generate a lookup table
vars=( - $(echo {A..Z}) )
# use the elements
for i in {1..5}; do
echo ${vars[$i]}
done
{A..Z} generates 26 strings: A, B, ..., Z
which get stored in an array variable by vars=(...)
we prepend a - that we'll ignore
we can then do 1-based indexing into the array
limited to 26 variables (or whatever range we choose)
method 2: multiple brace expansion to generate arbitrary long variables
#!/bin/bash
if [[ ! $1 =~ ^[0-9]+$ ]]; then
echo "Usage: $0 count"
exit
fi
cmd='{A..Z}'
for (( i=$1; i>26; i=i/26 )); do
cmd="${A..Z}$cmd"
done
vars=( $(eval echo $cmd) )
for (( i=0; i<$1; i++ )); do
echo ${vars[$i]}
done
i/26 does integer division (throws away the remainder)
I'm lazy and generate "more than enough" variables rather than attempting to calculate how many is "exactly enough"
{a..b}{a..b}{a..b} becomes aaa aab aba abb baa bab bba bbb
using eval lets us do the brace expansion without knowing in advance how many sets are needed
Sample output:
$ mkvar.sh 10000 |fmt -64 | tail -5
ORY ORZ OSA OSB OSC OSD OSE OSF OSG OSH OSI OSJ OSK OSL OSM
OSN OSO OSP OSQ OSR OSS OST OSU OSV OSW OSX OSY OSZ OTA OTB
OTC OTD OTE OTF OTG OTH OTI OTJ OTK OTL OTM OTN OTO OTP OTQ
OTR OTS OTT OTU OTV OTW OTX OTY OTZ OUA OUB OUC OUD OUE OUF
OUG OUH OUI OUJ OUK OUL OUM OUN OUO OUP
This question already has answers here:
How can I increment a number in a while-loop while preserving leading zeroes (BASH < V4)
(3 answers)
incrementing a number in bash with leading 0
(8 answers)
Closed 4 years ago.
I have the following code in a bash script, where "values" is a variable of newline separated numbers, some of which have leading 0's, and I am trying to iterate through each value in values and add each value to the variable "sum".
sum=0
while read line; do
sum=$(( sum + line ))
done <<< "$values"
this code segment gives me the error: "value too great for base (error token is "09")", which as I understand, is because the bash arithmetic expression interprets the value "line" to be an octal value because it has a leading zero.
How can I allow for bash to interpret the value of line to be its decimal value? (e.g. 09 -> 9) for the value "line" within this bash arithmetic expression?
You can override the "leading 0 means octal" by explicitly forcing base ten with 10#:
sum=$(( 10#$sum + 10#$line ))
Note that, while you can usually leave the $ off variable references in arithmetic contexts, in this case you need it. Also, if the variable has leading spaces (in front of the first "0"), it won't parse correctly.
To trim a single leading zero:
"${line#0}"
To trim any number of leading zeros:
"${line##+(0)}"
For example:
$ line=009900
$ echo "${line##+(0)}"
9900
You can just get rid of the leading zeros, with something like:
shopt extglob on
x="${x##+(0)}"
[[ -z "${x}" ]] && x=0
This will remove all leading zeros and then catch the case where it was all zeros (leading to an empty string), restoring it to a single zero.
The following function (and test code) will show this in action:
#!/bin/bash
stripLeadingZeros() {
shopt -s extglob
retVal="${1##+(0)}"
[[ -z "${retVal}" ]] && retVal=0
echo -n "${retVal}"
}
for testdata in 1 2 3 4 5 0 09 009 00000009 hello 00hello ; do
result="$(stripLeadingZeros ${testdata})"
echo "${testdata} -> ${result}"
done
The output of that is:
1 -> 1
2 -> 2
3 -> 3
4 -> 4
5 -> 5
0 -> 0
09 -> 9
009 -> 9
00000009 -> 9
hello -> hello
00hello -> hello