How to input two integer by space BASH? - bash

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

Related

Convert range to string

If I run the
echo {0..9}
command, then I get the following output:
0 1 2 3 4 5 6 7 8 9
Can I somehow put the string "0 1 2 3 4 5 6 7 8 9" into a variable inside bash script? I only found a way using echo:
x=`echo {0..9}`
But this method implies the execution of an external program. Is it possible to somehow manage only with bash?
Interested, rather than a way to convert a range to a string, but additionally concatenate with a string, for example:
datafiles=`echo data{0..9}.txt`
First of all,
x=`echo {0..9}`
doesn't call an external program (echo is a built-in) but creates a subshell. If it isn't desired you can use printf (a built-in as well) with -v option:
printf -v x ' %s' {0..9}
x=${x:1} # strip off the leading space
or
printf -v datafiles ' data%s.txt' {0..9}
datafiles=${datafiles:1}
or you may want storing them in an array:
datafiles=(data{0..9}.txt)
echo "${datafiles[#]}"
This last method will work correctly even if filenames contain whitespace characters:
datafiles=(data\ {0..9}\ .txt)
printf '%s\n' "${datafiles[#]}"

change charters in a string based on vcf table data

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.

Bash shell iterations over letters and numbers

Say I want to iterate over two lists of letters and numbers.
A B C D and seq 1 100.
How can I iterate over letters along with numbers but not as in nested for-loop? So it would be A1B2C3D4 A5B6C7D8 ...
What I've tried so far: nested for-loop and & done don't seem to be of any help, since they produce either A1 B1 C1 D1 A2 B2... or inconsistent results of parallel execution.
Also it feels like a very basic parallel loop, so no need for a detailed explanation or actual code: ANY ANSWER mentioning link to docs or the conventional name of such sequence would be immediately accepted.
The following script generates your expected output with a leading space:
Script
for i in {1..100}; do
IFS= read c
printf %s "$c$i"
done < <(yes $' A\nB\nC\n\D')
Output
A1B2C3D4 A5B6C7D8 A9B10C11D12 A13B14C15D16 A17B18C19D20 A21B22C23D24 A25B26C27D28 A29B30C31D32 A33B34C35D36 A37B38C39D40 A41B42C43D44 A45B46C47D48 A49B50C51D52 A53B54C55D56 A57B58C59D60 A61B62C63D64 A65B66C67D68 A69B70C71D72 A73B74C75D76 A77B78C79D80 A81B82C83D84 A85B86C87D88 A89B90C91D92 A93B94C95D96 A97B98C99D100
Explanation
To read the sequence 1 2 3 ... 100 in its full length, we need to repeat the sequence A B C D over and over again. yes is a command that repeats its argument ad infinitum. yes x prints
x
x
x
...
To let yes print something different in every line, we use a trick. $' A\nB\nC\nD' is a string that contains linebreaks ($'' is a so called bash ansi-c quote). yes $' A\nB\nC\nD' will print
A
B
C
D
A
B
...
Instead of printing to the console, we want to consume the text later. To this end, we could write yes ... | someCommand or someCommand < <(yes ...) which has some advantages over a pipe. The latter is called process substitution. Note that for ...; done is also just one command. The redirected stdin can be read from anywhere inside the for loop.
#!/bin/bash
# ASCII code for A
A=65
# Loop from 1 to 100
for ii in $( seq 1 100 )
do
# Compute ASCII code with using modulo
code=$(( (ii-1) % 4 + A ))
# Print letter
printf "\x$(printf %x $code)"
# Print number
echo $ii
done

Zero padding numbers in a Bash loop

I'm trying to make a list with a simple bash looping
I want this:
000000
000001
000002
They give me this:
0
1
2
My shell code:
countBEG="000000"
countEND="999999"
while [ $countBEG != $countEND ]
do
echo "$countBEG"
countBEG=$[$countBEG +1]
done
Change your echo to use printf, where you can specify format for left padding.
printf "%06d\n" "$countBEG"
This sets 6 as fixed length of the output, using zeros to fill empty spaces.
You're looking for:
seq -w "$countBEG" "$countEND"
The -w option does the padding.
The following command will produce the desired output (no need for the loop) :
printf '%06d\n' {1..999999}
Explanation :
{1..999999} is expanded by bash to the sequence of 1 to 999999
the format string '%06d\n' tells printf to display the number it is given as argument padded to 6 digits and followed by a linefeed
printf repeats this output if it is given more arguments than is defined in its format specification

Bash: Sum fields of a line

I have a file with the following format:
a 1 2 3 4
b 7 8
c 120
I want it to be parsed into:
a 10
b 15
c 120
I know this can be easily done with awk, but I'm not familiar with the syntax and can't get it to work for me.
Thanks for any help
ok simple awk primer:
awk '{ for (i=2;i<=NF;i++) { total+=$i }; print $1,total; total=0 }' file
NF is an internal variable that is reset on each line and is equal to the number of fields on that line so
for (i=2;i<=NF;i++) starts a for loop starting at 2
total+=$i means the var total has the value of the i'th field added to it. and is performed for each iteration of the loop above.
print $1,total prints the 1st field followed by the contents of OFS variable (space by default) then the total for that line.
total=0 resets the totals var ready for the next iteration.
all of the above is done on each line of input.
For more info see grymoires intro here
Start from column two and add them:
awk '{tot=0; for(i=2;i<$NF;i++) tot+=$i; print $1, tot;}' file
A pure bash solution:
$ while read f1 f2
> do
> echo $f1 $((${f2// /+}))
> done < file
On running it, got:
a 10
b 15
c 120
The first field is read into variable f1 and the rest of the fields are i f2. In variable f2 , spaces are replaced in place with + and evaluated.
Here's a tricky way to use a subshell, positional parameters and IFS. Works with various amounts of whitespace between the fields.
while read label numbers; do
echo $label $(set -- $numbers; IFS=+; bc <<< "$*")
done < filename
This works because the shell expands "$*" into a single string of the positional parameters joined by the first char of $IFS (documentation)

Resources