Storing information as a single record - bash

I am trying to save all the data entered into the table as an individual record in the contacts.txt file like so
Record: 12
Bob
Roberts
Bobs Stuff
Bobby
Bos Road
Bobsville
BB0 B22
01234123456
At the moment my code is saving each field as an individual record like so
Record: 5
Bob
==========================
Record: 6
Roberts
and so on. How do I get round this?
This is my code:
#!/bin/bash
BOOK="contacts.txt"
# set field names i.e. shell variables
forename=""
surname=""
company=""
position=""
street=""
town=""
postcode=""
phone=""
# open fd
exec 3>&1
# Store data to $VALUES variable
VALUES=$(dialog --ok-label "Submit" \
--backtitle "Contacts" \
--title "Add New Contact" \
--form "Create a new contact" \
15 50 0 \
"Forename:" 1 1 "$forename" 1 10 10 0 \
"Surname:" 2 1 "$surname" 2 10 15 0 \
"Company:" 3 1 "$company" 3 10 45 0 \
"Position:" 4 1 "$position" 4 10 40 0 \
"Street:" 5 1 "$street" 5 10 50 0 \
"Town:" 6 1 "$town" 6 10 20 0 \
"Postcode:" 7 1 "$postcode" 7 10 8 0 \
"Phone:" 8 1 "$phone" 8 10 11 0 \
2>&1 1>&3)
# close fd
exec 3>&-
# Echo the answers and ask for confirmation
echo "Should I enter the values:"
echo -e " $VALUES";
echo -n "y/n: "
read answer
# Convert the answer to lower case
fixedanswer=`echo $answer | tr "A-Z" "a-z"`;
if [ "$fixedanswer" = "y" ]
then
# Write the values to the address book
echo "$VALUES" >>$BOOK
echo "Added the entry OK"
sleep 5
else
# Give the user a message
echo -e " $VALUES \n NOT written to $BOOK"
sleep 5
fi
exit 0

In the line
echo "$VALUES" >>$BOOK
remove the quotes around $VALUES.
Adding quotes will list each value as its own line of text. Removing the quotes considers it one line, printing each value with a space between them.

Related

How do I create a bash script, using loops, to create a Multiplication Table with 5 column/10 row format

Here is what I have:
#!/bin/bash
#create a multiplication table 5 columns 10 rows
echo " Multiplication Table "
echo "-----+-------------------------"
for x in {0..5}
do
for y in {0..10}
do
echo -n "$(( $x * $y )) "
done
echo
echo "-----+--------------------------"
done
This is my Output:
Multiplication Table
-----+-------------------------
0 0 0 0 0 0 0 0 0 0 0
-----+--------------------------
0 1 2 3 4 5 6 7 8 9 10
-----+--------------------------
0 2 4 6 8 10 12 14 16 18 20
-----+--------------------------
0 3 6 9 12 15 18 21 24 27 30
-----+--------------------------
0 4 8 12 16 20 24 28 32 36 40
-----+--------------------------
0 5 10 15 20 25 30 35 40 45 50
-----+--------------------------
This is the Needed Output:
Multiplication Table
----+-------------------------------------
| 0 1 2 3 4
----+-------------------------------------
0 | 0 0 0 0 0
1 | 0 1 2 3 4
2 | 0 2 4 6 8
3 | 0 3 6 9 12
4 | 0 4 8 12 16
5 | 0 5 10 15 20
6 | 0 6 12 18 24
7 | 0 7 14 21 28
8 | 0 8 16 24 32
9 | 0 9 18 27 36
----+-------------------------------------
I've tried to write this many different ways, but I'm struggling with finding a way to format it correctly. The first is pretty close, but I need it to have the sequential numbers being multiplied on the top and left side. I'm not sure how to use, or if I can use, the seq command to achieve this or if there is a better way. I also need to have straight columns and rows with the defining lines setting the table layout, but my looking up the column command hasn't produced the right output.
Here was my final output and code:
#!/bin/bash
#create a multiplication table 5 columns 10 rows
#Create top of the table
echo " Multiplication Table"
echo "----+------------------------------"
#Print the nums at top of table and format dashes
echo -n " |"; printf '\t%d' {0..5}; echo
echo "----+------------------------------"
#for loops to create table nums
for y in {0..9}
do
#Print the side nums and |
echo -n "$y |"
#for loop to create x
for x in {0..5}
do
#Multiply vars, tab for spacing
echo -en "\t$((x*y))"
done
#Print
echo
done
#Print bottom dashes for format
echo "----+------------------------------"
I changed a bit of Armali's code just to make it more appealing to the eye, and the echo was moved to the bottom (out of the loop) so it didn't print as many lines. But again, thank you Armali, as I would've spent a lot more time figuring out exactly how to write that printf code to get the format correct.
I'm not sure how to use, or if I can use, the seq command to achieve this …
seq offers no advantage here over bash's sequence expression combined with printf.
This variant of your script produces (with the usual 8-column tabs) the needed output:
#!/bin/bash
#create a multiplication table 5 columns 10 rows
echo " Multiplication Table"
echo "----+-------------------------------------"
echo -n " |"; printf '\t%d' {0..4}; echo
echo "----+-------------------------------------"
for y in {0..9}
do echo -n "$y |"
for x in {0..4}
do echo -en "\t$((x*y))"
done
echo
echo "----+-------------------------------------"
done

How to generate N columns with printf

I'm currently using:
printf "%14s %14s %14s %14s %14s %14s\n" $(cat NFE.txt)>prueba.txt
This reads a list in NFE.txt and generates 6 columns. I need to generate N columns where N is a variable.
Is there a simple way of saying something like:
printf "N*(%14s)\n" $(cat NFE.txt)>prueba.txt
Which generates the desire output?
# T1 is a white string with N blanks
T1=$(printf "%${N}s")
# Replace every blank in T with string %14s and assign to T2
T2="${T// /%14s }"
# Pay attention to that T2 contains a trailing blank.
# ${T2% } stands for T2 without a trailing blank
printf "${T2% }\n" $(cat NFE.txt)>prueba.txt
You can do this although i don't know how robust it will be
$(printf 'printf '; printf '%%14s%0.s' {1..6}; printf '\\n') $(<file)
^
This is your variable number of strings
It prints out the command with the correct number of string and executes it in a subshell.
Input
10 20 30 40 50 1 0
1 3 45 6 78 9 4 3
123 4
5 4 8 4 2 4
Output
10 20 30 40 50 1
0 1 3 45 6 78
9 4 3 123 4 5
4 8 4 2 4
You could write this in pure bash, but then you could just use an existing language. For example:
printf "$(python -c 'print("%14s "*6)')\n" $(<NFE.txt)
In pure bash, you could write, for example:
repeat() { (($1)) && printf "%s%s" "$2" "$(times $(($1-1)) "$2")"; }
and then use that in the printf:
printf "$(repeat 6 "%14s ")\n" $(<NFE.txt)

bash dialog read variables from form

Let's say I got dialog defined like this:
dialog --backtitle "Dialog Form Example" --title "Dialog - Form" \
--form "\nDialog Sample Label and Values" 25 60 16 \
"Form Label 1:" 1 1 "Value 1" 1 25 25 30 \
"Form Label 2:" 2 1 "Value 2" 2 25 25 30 \
"Form Label 3:" 3 1 "Value 3" 3 25 25 30 \
"Form Label 4:" 4 1 "Value 4" 4 25 25 30
That would show 4 inputs... but how do I read the output of that in bash script?
$? seems to output 0
When you want to assign the different form fields to different var's, you need to parse the output.
ans=$(dialog --backtitle "Dialog Form Example" --title "Dialog - Form" \
--form "\nDialog Sample Label and Values" 25 60 16 \
"Form Label 1:" 1 1 "Value 1" 1 25 25 30 \
"Form Label 2:" 2 1 "Value 2" 2 25 25 30 \
"Form Label 3:" 3 1 "Value 3" 3 25 25 30 \
"Form Label 4:" 4 1 "Value 4" 4 25 25 30 2>&1 >/dev/tty)
echo -e "\n\n\n\nAnswer=[${ans}]"
i=0
while read -r line; do
((i++))
declare var$i="${line}"
done <<< "${ans}"
echo "var2=${var2}"
You need to redirect the standard output of dialog to a variable:
RESULTS=$(dialog --backtitle "Dialog Form Example" --title "Dialog - Form" \
--form "\nDialog Sample Label and Values" 25 60 16 \
"Form Label 1:" 1 1 "Value 1" 1 25 25 30 \
"Form Label 2:" 2 1 "Value 2" 2 25 25 30 \
"Form Label 3:" 3 1 "Value 3" 3 25 25 30 \
"Form Label 4:" 4 1 "Value 4" 4 25 25 30)
In such a way, all the values will be there.
https://bash.cyberciti.biz/guide/The_form_dialog_for_input

Bash Shell Scripting - Dialog form variables

So, I just took up Shell Scripting and I'm developing an address book.
For the user to insert a contact I made this form:
form=$(dialog \
--title "INSERIR" \
--form "" \
0 0 0 \
"Nome:" 1 1 "$nome" 1 10 20 0 \
"Morada:" 2 1 "$morada" 2 10 20 0 \
"Telefone:" 3 1 "$telefone" 3 10 20 0 \
"E-Mail:" 4 1 "$mail" 4 10 20 0 \
2>&1 1>&3)
And I want to insert those values through a MySQL query. I saw somewhere that I had to use, for instance:
form[$1]
In order to access the variable $nome. However, it was a comment from 2008.
What is the easiest way to access those variables?
Thank you!
IFS=$'\n' read -r -d '' nome morada telefone mail < <( dialog ... )
Unlike dialog ... | { read; ... } (which scopes the variables which are read to a subshell), this approach puts dialog in the subshell, and your variables in the main shell -- much more convenient.
So, after a bit of tinkering I got what I was looking for.
Here is the new form:
exec 3>&1
dialog \
--separate-widget $'\n' \
--title "INSERIR" \
--form "" \
0 0 0 \
"Nome:" 1 1 "$nome" 1 10 30 0 \
"Morada:" 2 1 "$morada" 2 10 30 0 \
"Telefone:" 3 1 "$telefone" 3 10 30 0 \
"E-Mail:" 4 1 "$mail" 4 10 30 0 \
2>&1 1>&3 | {
read -r nome
read -r morada
read -r telefone
read -r mail
#The rest of the script goes here
}
exec 3>&-
So, you can really just put the output into an array and deal with that. Avoids all the subshell / subprocess garbage. (Just trust on the flippy redirect, yeah, it's ugly but you're basically just subbing out stdin and swapping it back.) Not sure why that's been so elusive after 5 years, but hey. I guess it's cool to be obscure.
response=$(dialog \
--title "INSERIR" \
--form "" \
0 0 0 \
"Nome:" 1 1 "$nome" 1 10 20 0 \
"Morada:" 2 1 "$morada" 2 10 20 0 \
"Telefone:" 3 1 "$telefone" 3 10 20 0 \
"E-Mail:" 4 1 "$mail" 4 10 20 0 \
3>&1 1>&2 2>&3 3>&-)
#convert the space separated string to an array.. the madness!!
responsearray=($response)
echo ${responsearray[0]} #nome
echo $(responsearray[1]} #morada
echo ${responsearray[2]} #telefone
echo ${responsearray[3]} #mail
...and bob's your uncle.
After several days looking for a way get those variables, here what I used, with your form:
nome=""
morada=""
telefone=""
mail=""
user_record=$(\
dialog \
--separate-widget $'\n' \
--title "INSERIR" \
--form "" \
0 0 0 \
"Nome:" 1 1 "$nome" 1 10 30 0 \
"Morada:" 2 1 "$morada" 2 10 30 0 \
"Telefone:" 3 1 "$telefone" 3 10 30 0 \
"E-Mail:" 4 1 "$mail" 4 10 30 0 \
3>&1 1>&2 2>&3 3>&- \
)
nome=$(echo "$user_record" | sed -n 1p)
morada=$(echo "$user_record" | sed -n 2p)
telefone=$(echo "$user_record" | sed -n 3p)
mail=$(echo "$user_record" | sed -n 4p)
echo $nome
echo $morada
echo $telefone
echo $mail
This way you can use those variables later on your script.
Hope it helps others.
The question regarding the easiest way to access the result depends partly on whether the items might contain blanks. If the items can contain arbitrary data, then line-oriented output (the default) seems the only way to go. If they are more constrained, e.g., not containing some readily-used punctuation character which can be used as a delimiter, then that makes it simpler.
The manual page mentions an option (and alias) which can be used to do this:
--separator string
--output-separator string
Specify a string that will separate the output on dialog's output from checklists, rather than a newline (for --separate-output) or a space. This applies to other widgets such as forms
and editboxes which normally use a newline.
For example, if the data does not include a : (colon), then you could use the option
--output-separator :
and get colon-separated values on a single line.
If there are no commas or quotes in the string, you could conceivably use
--output-separator \",\"
and embed the result directly in an SQL statement. However, commas occur more frequently than the other punctuation mentioned, so processing the form's output with sed is the most likely way one might proceed.

the logic behind bash power set function

The function (output the power set of a given input)
p() { [ $# -eq 0 ] && echo || (shift; p "$#") |
while read r ; do echo -e "$1 $r\n$r"; done }
Test Input
p $(echo -e "1 2 3")
Test Output
1 2 3
2 3
1 3
3
1 2
2
1
I have difficulty grasping the recursion in the following code. I tried to understand it by placing some variables inside of the code to denote the level of recursion and the order of execution, but I am still puzzled.
Here are the things I can tell so far:
The subshell's output will not be shown on the final output, as it gets redirected to the read command via pipe
The echo command appends new line for all of its output
The order of execution I see is:
p (1 2 3) -> 1 followed by all combination of output below\n
all combination of output below
p (2 3) -> 2 3\n3\n
p (3) -> 3
p () ->
So I think I should have p(2) instead of p(3) on execution #3, but how does that happen? Since shift only goes in one direction.
If I were to use "p(1 2 3 4)" as the input, it is the part that shows "1 2 3" in the output that confuses me.
The use of -e in the echo command seems to me pure obfuscation, since it could have been written:
p() { [ $# -eq 0 ] && echo || (shift; p "$#") |
while read r ; do
echo $1 $r
echo $r
done
}
In other words, "for every set in the power set of all but the first argument (shift; p "$#"), output both that set with and without the first argument."
The bash function works by setting up a chain of subshells, each one reading from the next one, something like this, where each box is a subshell and below it, I've shown its output as it reads each line of input: (I used "" to make "nothing" visible. => means "call"; <- means "read".)
+---------+ +-------+ +-------+ +-------+
| p 1 2 3 | ==> | p 2 3 | ==> | p 3 | ==> | p |
+---------+ +-------+ +-------+ +-------+
1 2 3 "" <--+-- 2 3 "" <---+-- 3 "" <-----+-- ""
2 3 "" <-/ / /
1 3 "" <--+-- 3 "" <-/ /
3 "" <-/ /
1 2 "" <--+-- 2 "" <---+-- "" <-/
2 "" <-/ /
1 "" <--+-- "" <-/
"" <-/

Resources