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
Related
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
I have a data file. It will looks like:
input1:
1 20022 44444 44444
2 31012 22233 44444
3 31012 22233 00444
4 20022 44444 00444
5 20022 44444 00444
6 20022 44444 00444
7 31012 44444 00444
8 31012 44444 00444
9 31012 87634 44444
10 20022 87634 44444
I want to convert each character within each column as a sub-column and I want to put either 1 or zero in rows in the way that they represent if the the sub column was observed in that specific row or not:
output1:
c1.20022 c1.31012 c2.44444 c2.22233 c2.87634 c3.44444 c3.00444
1 1 0 1 0 0 1 0
2 0 1 0 1 0 1 0
3 0 1 0 1 0 0 1
4 1 0 1 0 0 0 1
5 1 0 1 0 0 0 1
6 1 0 1 0 0 0 1
7 0 1 1 0 0 0 1
8 0 1 1 0 0 0 1
9 0 1 0 0 1 1 0
10 1 0 0 0 1 1 0
My real data has moreover than 100000 columns and rows.
I should also mention that I want to run this program in Linux.
secound part: I want to delete those characters which repeated less than a hundred times within each column and I do not want any sub-column for those. for exaple in my example input.file I want to delete those characters which are repeated less than 3 times:
input2:
1 20022 44444 44444
2 31012 NA 44444
3 31012 NA 00444
4 20022 44444 00444
5 20022 44444 00444
6 20022 44444 00444
7 31012 44444 00444
8 31012 44444 00444
9 31012 NA 44444
10 20022 NA 44444
And output:
output2:
c1.20022 c1.31012 c2.44444 c3.44444 c3.00444
1 1 0 1 1 0
2 0 1 0 1 0
3 0 1 0 0 1
4 1 0 1 0 1
5 1 0 1 0 1
6 1 0 1 0 1
7 0 1 1 0 1
8 0 1 1 0 1
9 0 1 0 1 0
10 1 0 0 1 0
What I should change in shell script written below in the answer , in order to directly reach from my first input(input1) into the last output(output2) ?
a bit update:
if in my input each 2 line be representation of one individual(line 1 and 2 belongs to individual 1):
1 20022 44444 44444
1 31012 44444 44444
2 31012 00000 00444
2 20022 44444 00444
3 20022 44444 00444
3 20022 44444 00444
4 31012 44444 00444
4 31012 44444 00444
5 31012 11112 44444
5 20022 11112 44444
and I want in my output.txt each individual be repeated only one time while converting each character within each column as a sub-column and I want to put either 2 or 1 or zero in rows in the way that they represent how many times each character repeats within a sub-column for each individual. Meanwhile I want to delete those characters which are repeated less than 3 times within each column(here 00000 and 11112 from column2):
output1.txt:
c1.20022 c1.31012 c2.44444 c3.44444 c3.004444
1 1 1 2 2 0
2 1 1 1 0 2
3 1 0 1 0 2
4 0 2 2 0 2
5 1 1 0 2 0
here in out put I put spaces between digit numbers in order to make it understandable. but in fact those spaces not be needed(ex: first line: 1 11220)
As a non-fortran solution I wrote a (g)awk script which does what you want, and your file should be given to it twice. In the first run it builds an array of the labels appearing in each column, which is the only memory-heavy step in the process. In the post-processing phase each column is processed one-by-one, line after line independently, so I guess its utility depends on the distribution of header values.
Important note: the script utilizes real 2d arrays of the syntax labels[i][$i] rather than standard awk's array[i,j] syntax in order to be able to loop over the second index. This will work in gawk, but other awk flavours might not support it.
foo.awk:
#!/usr/bin/gawk
#set up label array from first run
NR==FNR{
for(i=2; i<=NF; i++){
labels[i][$i]=1;
}
}
#do actual printing in second run
NR!=FNR{
if(FNR==1){ #then print header
printf " ";
for(i=2; i<=NF; i++){ #i corresponds to columns in input
for(label in labels[i]){
printf " c%d.%s ",i-1,label}; #note i-1
}
print ""; #newline
};
printf "%10d", FNR; #column 1: line number
for(i=2; i<=NF; i++){
for(label in labels[i]){ #loop over every possible label in column i
if($i==label){
printf " 1 "; #1 if same
}
else {
printf " 0 "; #0 if different
}
};
}
print ""; #newline
}
And a front-end, bar.sh:
#!/bin/bash
infile=$1
gawk -f foo.awk $infile $infile
which you run by ./bar.sh infile after setting it to executable, where "infile" should be replaced by the actual name of your input file. Obviously you can skip the shell script and just call gawk -f foo8.awk infile infile, but I for one am too lazy to do this more than once.
Also, note that you probably want to remove most of the whitespace in the printf commands. Those are in there for a pretty output, but you probably won't be looking at the output by hand, but rather with some automated post-processing method. But all this whitespace would blow up the already huge file you end up with. So I suggest keeping a single space at the beginning of each printf in order to separate your columns from one another, and removing the rest.
Output:
c1.20022 c1.31012 c2.44444 c2.87634 c2.22233 c3.00444 c3.44444
1 1 0 1 0 0 0 1
2 0 1 0 0 1 0 1
3 0 1 0 0 1 1 0
4 1 0 1 0 0 1 0
5 1 0 1 0 0 1 0
6 1 0 1 0 0 1 0
7 0 1 1 0 0 1 0
8 0 1 1 0 0 1 0
9 0 1 0 1 0 0 1
10 1 0 0 1 0 0 1
Update
Regarding your updated question:
I want to delete those characters which repeated less than a hundred times within each column and I do not want any sub-column for those. for exaple in my example input.file I want to delete those characters which are repeated less than 3 times
It's your lucky day, as the above script only needs trivial changes to make that happen. For this we change the labels[i][label] variables from indicator to counter, i.e. we keep incrementing their value when we find the same label. Then during the second run we simply skip those labels which appear at most 2 times.
Updated foo.awk:
#!/usr/bin/gawk
#set up label array from first run
NR==FNR{
for(i=2; i<=NF; i++){
labels[i][$i]++; #counter instead of indicator
}
}
#do actual printing in second run
NR!=FNR{
if(FNR==1){ #then print header
printf " ";
for(i=2; i<=NF; i++){ #i corresponds to columns in input
for(label in labels[i]){
if(labels[i][label]<3) continue; #skip labels which appear at most 2 times
printf " c%d.%s ",i-1,label}; #note i-1
}
print ""; #newline
};
printf "%10d", FNR; #column 1: line number
for(i=2; i<=NF; i++){
for(label in labels[i]){ #loop over every possible label in column i
if(labels[i][label]<3) continue; #skip labels which appear at most 2 times
if($i==label){
printf " 1 "; #1 if same
}
else {
printf " 0 "; #0 if different
}
};
}
print ""; #newline
}
Output:
c1.20022 c1.31012 c2.44444 c3.00444 c3.44444
1 1 0 1 0 1
2 0 1 0 0 1
3 0 1 0 1 0
4 1 0 1 1 0
5 1 0 1 1 0
6 1 0 1 1 0
7 0 1 1 1 0
8 0 1 1 1 0
9 0 1 0 0 1
10 1 0 0 0 1
Update 2
Regarding your twice updated question,
a bit update: if in my input each 2 line be representation of one individual(line 1 and 2 belongs to individual 1):
...
Now you have data spanning two lines each, and you want to treat them together. Note that as your problem becomes more complicated, the solution does as well. In order to spare complications, I assumed that you have exactly 2 lines for each individual, which seems to be the case. I also had to assume that the first line in your input file starts with a 1. This also seems to be the case, but the above solutions didn't make use of this. As a matter of fact, it is assumed that the individuals span a range from 1 to the total number of individuals, without gaps. It could be done in a more general way, but I didn't want to overcomplicate it for no reason.
new bar.sh:
#!/bin/bash
infile=$1
cat $infile $infile |paste - - |gawk -f foo.awk
this will put each pair of input lines next to each other such that now each individual is again only on one line, then feed this modified file twice to foo.awk.
new foo.awk:
#!/usr/bin/gawk
#keep count of number of files (from first colum of first row)
{if($1==1) nfiles++;}
#set up label array from first run
nfiles==1{
for(i=2; i<=NF/2; i++){ #go over first half columns
labels[i][$i]++; #odd lines
labels[i][$(i+NF/2)]++; #even lines
}
}
#do actual printing in second run
nfiles==2{
if($1==1){ #then print header
printf " ";
for(i=2; i<=NF/2; i++){ #i corresponds to columns in input
for(label in labels[i]){
if(labels[i][label]<3) continue; #skip labels which appear at most 2 times
printf " c%d.%s ",i-1,label}; #note i-1
}
print ""; #newline
};
printf "%10d ", $1; #column 1: line number
for(i=2; i<=NF/2; i++){
for(label in labels[i]){ #loop over every possible label in column i
if(labels[i][label]<3) continue; #skip labels which appear at most 2 times
multi=0 #multiplicity of label "label" in line i
if($i==label) multi++;
if($(i+NF/2)==label) multi++;
printf " %3d ", multi;
};
}
print ""; #newline
}
Input:
1 20022 44444 44444
1 31012 44444 44444
2 31012 00000 00444
2 20022 44444 00444
3 20022 44444 00444
3 20022 44444 00444
4 31012 44444 00444
4 31012 44444 00444
5 31012 11112 44444
5 20022 11112 44444
Output:
c1.20022 c1.31012 c2.44444 c3.00444 c3.44444
1 1 1 2 0 2
2 1 1 1 2 0
3 2 0 2 2 0
4 0 2 2 2 0
5 1 1 0 0 2
Note that you can just remove most of the extraneous whitespace by changing
printf " %3d ", multi;
to
printf "%d", multi;
And also note that my example output is different from yours, but from your specification it seems to me that my version is the correct one (for instance, for individual 3 there should be a "2" in the first column).
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.
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.
How can I add 6 more single space separated columns to a file.
The input file that looks like this:
-11.160574
...
-11.549076
-12.020907
...
-12.126601
...
-11.93235
...
-8.297653
Where ... represents 50 more lines of numbers.
The output I want is this:
-11.160574 1 1 1 1 1 14
...
-11.549076 51 51 1 1 1 14
-12.020907 1 1 2 2 1 14
...
-12.126601 51 51 2 2 1 14
...
-11.93235 1 1 51 51 1 14
...
-8.297653 51 51 51 51 1 14
The 2nd and 3rd columns are loops for 1 to 51.
The 4th and 5th columns are also loops for 1 to 51, but at the upper level from above.
The last two ones constants columns of 1 and 14.
Use a loop to read the file line-by-line and maintain counters to keep track of the field numbers as shown below:
#!/bin/bash
field1=1
field2=1
while read line
do
echo "$line $field1 $field1 $field2 $field2 1 14"
(( field1++ ))
if (( $field1 == 52 )); then
field1=1
(( field2++ ))
fi
done < file
Here you go, an awk script:
{
mod = 51
a = (NR - 1) % mod + 1
b = int((NR - 1) / mod) + 1
c = 1
d = 14
print $0,a,a,b,b,c,d
}
Run it with something like awk -f the-script.awk in-file.txt. Or make it executable and add #!/usr/bin/awk -f at the top, and you can run it directly without typing awk -f.