Reading in input from text file, Bash script - bash

I am new to writing scripts in Bash and I am working on an assignment to make an inventory system. I have written all the scripts and they all work from using the standard input terminal. I now am looking to take the inputs from a text file called a1Input.txt which has all inputs on a new line.
r
9823
r
5430
c
7777
sml_widget
Small Widget (1 oz.)
6.99
15
50
Small, white, widget w/o packaging
r
7777
d
9823
r
9823
r
3293
u
3293
29.99
33
75
r
3293
The code for my initial bash script is this
#!/bin/bash
# assign1.bash
shopt -s nocasematch
option=""
until [ "$option" = "F" ]; do
echo "C - create a new item"
echo "R - read an existing item"
echo "U - update an existing item"
echo "D - delete an existing item"
echo "T - total price of an item"
echo "Choose a option"
read option
case $option in
C)
./create.bash
;;R)
./read.bash
;;U)
./update.bash
;;D)
./delete.bash
;;T)
./total.bash
;;*)
echo "Invalid input"
esac
done

What i would do to inject inputs from file to an interactive script is just:
cat a1Input.txt | ./interactive_script.sh
For example, imagine this simple script (copy-paste on terminal to create):
cat << EOF > questions.sh
#!/bin/bash
echo "What's your name ?"
read name
echo "What is your favourite movie ?"
read movie
echo "Hi \$name, i also love '\$movie' movie !!"
EOF
And these inputs:
cat << EOF > inputs.txt
Edu
Interstellar
EOF
Then, just execute:
chmod a+x questions.sh
cat inputs.txt | ./questions.sh
If your script is more complicated, consider using "expect", although this is quite complex.
BRs

Related

bash for script and input parameter

Can anyone help me to modify my script. Because it does not work. Here are three scripts.
1) pb.sh, use delphicpp_release software to read the 1brs.ab.sh and will give the output as 1brs.ab.out
2) 1brs.ab.sh, use for input parameter where a.sh(another script for protein structure), chramm.siz, charmm.crg are file for atom size and charge etc. rest of the parameters for run the delphicpp_release software.
3) a.sh, use for read several protein structures, which will be in the same directory.
my script_1 = pb.sh:
./delphicpp_release 1brs.ab.sh >1brs.ab.out
echo PB-Energy-AB = $(grep -oP '(?<=Energy> Corrected:).*' 1brs.ab.out) >>PB-energy.dat
cat PB-energy.dat
script_2 = 1brs.ab.sh:
in(pdb,file="a.sh")
in(siz,file="charmm.siz")
in(crg,file="charmm.crg")
perfil=70
scale=2.0
indi=4
exdi=80.0
prbrad=1.4
salt=0.15
bndcon=2
maxc=0.0001
linit=800
energy(s)
script_3 = a.sh:
for i in $(seq 90000 20 90040); do
$i.pdb
done
As we don't know what software is, something like
for ((i=90000;i<=100000;i+=20)); do
./software << " DATA_END" > 1brs.$i.a.out
scale=2.0
in(pdb,file="../$i.ab.pdb")
in(siz,file="charmm.siz")
in(crg,file="charmm.crg")
indi=z
exdi=x
prbrad=y
DATA_END
echo Energy-A = $(grep -oP '(?<=Energy>:).*' 1brs.$i.a.out) >>PB-energy.dat
done
A more POSIX shell compliant version
i=90000
while ((i<=100000)); do
...
((i+=20));
done
EDIT: Without heredoc
{
echo 'scale=2.0'
echo 'in(pdb,file="../'"$i"'.ab.pdb")'
echo 'in(siz,file="charmm.siz")'
echo 'in(crg,file="charmm.crg")'
echo 'indi=z'
echo 'exdi=x'
echo 'prbrad=y'
} > $i.ab.sh
./software <$i.ab.sh >$i.ab.out
but as question was changed I'm not sure to understand it.

Xcode bad coloring with shell script?

It seems that Xcode really sucks at coloring the shell script. For example, if you copy the following snippet into your Xcode, most of the whole chunk is colored red.
### Creation of the GM template by averaging all (or following the template_list for) the GM_nl_0 and GM_xflipped_nl_0 images
cat <<stage_tpl3 > fslvbm2c
#!/bin/sh
if [ -f ../template_list ] ; then
template_list=\`cat ../template_list\`
template_list=\`\$FSLDIR/bin/remove_ext \$template_list\`
else
template_list=\`echo *_struc.* | sed 's/_struc\./\./g'\`
template_list=\`\$FSLDIR/bin/remove_ext \$template_list | sort -u\`
echo "WARNING - study-specific template will be created from ALL input data - may not be group-size matched!!!"
fi
for g in \$template_list ; do
mergelist="\$mergelist \${g}_struc_GM_to_T"
done
\$FSLDIR/bin/fslmerge -t template_4D_GM \$mergelist
\$FSLDIR/bin/fslmaths template_4D_GM -Tmean template_GM
\$FSLDIR/bin/fslswapdim template_GM -x y z template_GM_flipped
\$FSLDIR/bin/fslmaths template_GM -add template_GM_flipped -div 2 template_GM_init
stage_tpl3
chmod +x fslvbm2c
fslvbm2c_id=`fsl_sub -j $fslvbm2b_id -T 15 -N fslvbm2c ./fslvbm2c`
echo Creating first-pass template: ID=$fslvbm2c_id
### Estimation of the registration parameters of GM to grey matter standard template
/bin/rm -f fslvbm2d
T=template_GM_init
for g in `$FSLDIR/bin/imglob *_struc.*` ; do
echo "${FSLDIR}/bin/fsl_reg ${g}_GM $T ${g}_GM_to_T_init $REG -fnirt \"--config=GM_2_MNI152GM_2mm.cnf\"" >> fslvbm2d
done
chmod a+x fslvbm2d
fslvbm2d_id=`$FSLDIR/bin/fsl_sub -j $fslvbm2c_id -T $HOWLONG -N fslvbm2d -t ./fslvbm2d`
echo Running registration to first-pass template: ID=$fslvbm2d_id
### Creation of the GM template by averaging all (or following the template_list for) the GM_nl_0 and GM_xflipped_nl_0 images
cat <<stage_tpl4 > fslvbm2e
#!/bin/sh
if [ -f ../template_list ] ; then
template_list=\`cat ../template_list\`
template_list=\`\$FSLDIR/bin/remove_ext \$template_list\`
else
template_list=\`echo *_struc.* | sed 's/_struc\./\./g'\`
template_list=\`\$FSLDIR/bin/remove_ext \$template_list | sort -u\`
echo "WARNING - study-specific template will be created from ALL input data - may not be group-size matched!!!"
fi
for g in \$template_list ; do
mergelist="\$mergelist \${g}_struc_GM_to_T_init"
done
\$FSLDIR/bin/fslmerge -t template_4D_GM \$mergelist
\$FSLDIR/bin/fslmaths template_4D_GM -Tmean template_GM
\$FSLDIR/bin/fslswapdim template_GM -x y z template_GM_flipped
\$FSLDIR/bin/fslmaths template_GM -add template_GM_flipped -div 2 template_GM
stage_tpl4
chmod +x fslvbm2e
fslvbm2e_id=`fsl_sub -j $fslvbm2d_id -T 15 -N fslvbm2e ./fslvbm2e`
echo Creating second-pass template: ID=$fslvbm2e_id
It would look like this.
Is there a way whereby I can fix the Xcode coloring issue?
What's confusing Xcode's syntax highlighting here is specifically the combination of heredocs (<<EOF) and escaped backticks (\`).
There's no way to fix it as-is, but, so long as there is no substituted content in the heredocs, you can use a quoted heredoc to remove the requirement for escaped backticks in the first place:
cat <<'stage_tpl3' > fslvbm2c
...
template_list=`cat ../template_list`
...
stage_tpl3
When the terminating label for a heredoc is enclosed in quotes, substitutions within the heredoc are disabled. It works the exact same way, and Xcode is able to highlight it more gracefully. (As a bonus, it's also easier to read and write the script without all the backslashes in the way!)
As an aside, note that it's conventional to always use the label "EOF" for heredocs. Some editors special-case this for syntax highlighting. It's also easier to spot than something specific to the document.

Bash user prompt while reading file

I am trying to create a user prompt while reading a file a line by line in Bash. The idea is for me to plot various files one-by-one using Gnuplot. Here is what I have:
#!/bin/bash
echo "Enter filename that contains the filenames:"
read fname
xr="[1e8:1e20]"
yr="[1:1e13]"
while read line
do
echo -e "reset\nset log\nset xrange$xr\nset yrange$yr\nset xlabel \"Frequency [Hz]\"\nset ylabel \"F_{/Symbol n} [Jy Hz]\"\nset key top left\nplot \"$line.dat\" u 3:(\$3*\$4)*1e26 w l ti \"$line^o\" \n"> plt.gp
gnuplot plt.gp
done < $fname
I would like to enter a user input/"continue?" type thing before "gnuplot plt.gp" command, because at the moment it just plots everything rapidly and then exits. The standard read -p command does not work here. I read somewhere I may need to use file descriptor exec 5 command, but I don't understand. Thanks.
#!/bin/bash
read -p 'Enter filename that contains the filenames: ' fname
xr="[1e8:1e20]"
yr="[1:1e13]"
while read line
do
echo -e "reset\nset log\nset xrange$xr\nset yrange$yr\nset xlabel \"Frequency [Hz]\"\nset ylabel \"F_{/Symbol n} [Jy Hz]\"\nset key top left\nplot \"$line.dat\" u 3:(\$3*\$4)*1e26 w l ti \"$line^o\" \n"> plt.gp
gnuplot plt.gp
read -p 'Do you want to continue? [Y/n]: ' want_to_continue </dev/tty
case "${want_to_continue}" in
Y|y)
continue
;;
*)
echo "OK. Bye!"
break
;;
esac
done < ${fname}

BASH scripts : whiptail file select

I've found a great little program that will allow me to add user friendly GUI's to my Bash Scripts;
whiptail
However the whiptail man page isn't all that helpful and doesn't provide any examples. After doing some google searches I understand how to create a simple yes/no menu using whiptail:
#! /bin/bash
# http://archives.seul.org/seul/project/Feb-1998/msg00069.html
if (whiptail --title "PPP Configuration" --backtitle "Welcome to SEUL" --yesno "
Do you want to configure your PPP connection?" 10 40 )
then
echo -e "\nWell, you better get busy!\n"
elif (whiptail --title "PPP Configuration" --backtitle "Welcome to
SEUL" --yesno " Are you sure?" 7 40)
then
echo -e "\nGood, because I can't do that yet!\n"
else
echo -e "\nToo bad, I can't do that yet\n"
fi
But what I would really like to build a file select menu using whiptail to replace some old code I have in a few different backup/restore bash scripts I have:
#!/bin/bash
#This script allows you to select a file ending in the .tgz extension (in the current directory)
echo "Please Select the RESTORE FILE you would like to restore: "
select RESTOREFILE in *.tgz; do
break #Nothing
done
echo "The Restore File you selected was: ${RESTOREFILE}"
I assume this has to be done via the '--menu' option of whiptail, but I am not sure how to go about it?
Any pointers?
Or can you point me in the direction of some whiptail examples?
Build an array of file names and menu select tags:
i=0
s=65 # decimal ASCII "A"
for f in *.tgz
do
# convert to octal then ASCII character for selection tag
files[i]=$(echo -en "\0$(( $s / 64 * 100 + $s % 64 / 8 * 10 + $s % 8 ))")
files[i+1]="$f" # save file name
((i+=2))
((s++))
done
A method like this will work even if there are filenames with spaces. If the number of files is large, you may have to devise another tag strategy.
Using alpha characters for the tags lets you press a letter to jump to the item. Numeric tags don't seem to do that. If you don't need that behavior, then you can eliminate some complexity.
Display the menu:
whiptail --backtitle "Welcome to SEUL" --title "Restore Files" \
--menu "Please select the file to restore" 14 40 6 "${files[#]}"
If the exit code is 255, the dialog was canceled.
if [[ $? == 255 ]]
then
do cancel stuff
fi
To catch the selection in a variable, use this structure (substitute your whiptail command for "whiptail-command"):
result=$(whiptail-command 2>&1 >/dev/tty)
Or
result=$(whiptail-command 3>&2 2>&1 1>&3-)
The variable $result will contain a letter of the alphabet that corresponds to a file in the array. Unfortunately, Bash prior to version 4 doesn't support associative arrays. You can calculate the index into the array of the file from the letter like this (notice the "extra" single quote):
((index = 2 * ( $( printf "%d" "'$result" ) - 65 ) + 1 ))
Example:
Welcome to SEUL
┌──────────┤ Restore Files ├───────────┐
│ Please select the file to restore │
│ │
│ A one.tgz ↑ │
│ B two.tgz ▮ │
│ C three.tgz ▒ │
│ D another.tgz ▒ │
│ E more.tgz ▒ │
│ F sp ac es.tgz ↓ │
│ │
│ │
│ <Ok> <Cancel> │
│ │
└──────────────────────────────────────┘
Whiptail is a lightweight reimplementation of the most popular features of dialog, using the Newt library. I did a quick check, and many features in Whiptail seem to behave like their counterparts in dialog. So, a dialog tutorial should get you started. You can find one here but Google is your friend of course. On the other hand, the extended example probably contains a lot of inspiration for your problem.
This function is part of my function repository for whiptail
# ----------------------------------------------------------------------
# File selection dialog
#
# Arguments
# 1 Dialog title
# 2 Source path to list files and directories
# 3 File mask (by default *)
# 4 "yes" to allow go back in the file system.
#
# Returns
# 0 if a file was selected and loads the FILE_SELECTED variable
# with the selected file.
# 1 if the user cancels.
# ----------------------------------------------------------------------
function dr_file_select
{
local TITLE=${1:-$MSG_INFO_TITLE}
local LOCAL_PATH=${2:-$(pwd)}
local FILE_MASK=${3:-"*"}
local ALLOW_BACK=${4:-no}
local FILES=()
[ "$ALLOW_BACK" != "no" ] && FILES+=(".." "..")
# First add folders
for DIR in $(find $LOCAL_PATH -maxdepth 1 -mindepth 1 -type d -printf "%f " 2> /dev/null)
do
FILES+=($DIR "folder")
done
# Then add the files
for FILE in $(find $LOCAL_PATH -maxdepth 1 -type f -name "$FILE_MASK" -printf "%f %s " 2> /dev/null)
do
FILES+=($FILE)
done
while true
do
FILE_SELECTED=$(whiptail --clear --backtitle "$BACK_TITLE" --title "$TITLE" --menu "$LOCAL_PATH" 38 80 30 ${FILES[#]} 3>&1 1>&2 2>&3)
if [ -z "$FILE_SELECTED" ]; then
return 1
else
if [ "$FILE_SELECTED" = ".." ] && [ "$ALLOW_BACK" != "no" ]; then
return 0
elif [ -d "$LOCAL_PATH/$FILE_SELECTED" ] ; then
if dr_file_select "$TITLE" "$LOCAL_PATH/$FILE_SELECTED" "$FILE_MASK" "yes" ; then
if [ "$FILE_SELECTED" != ".." ]; then
return 0
fi
else
return 1
fi
elif [ -f "$LOCAL_PATH/$FILE_SELECTED" ] ; then
FILE_SELECTED="$LOCAL_PATH/$FILE_SELECTED"
return 0
fi
fi
done
}
The use is simple
if dr_file_select "Please, select a file" /home/user ; then
echo "File Selected: \"$FILE_SELECTED\"."
else
echo "Cancelled!"
fi
I've tried following, which worked:
whiptail --title "PPP Config" --backtitle "Welcome to SEUL" --menu YourTitle 20 80 10 `for x in $(ls -1 *.tgz); do echo $x "-"; done`
you might change this into a multiple-liner as well, i've added checking for empty list:
MYLIST=`for x in $(ls -1 *.tgz); do echo $x "-"; done`
WC=`echo $MYLIST | wc -l`
if [[WC -ne 0]]; then
whiptail --title "PPP Config" --backtitle "Welcome to SEUL" --menu YourTitle 20 80 10 $MYLIST
fi
you need to adjust the numbers in order to get a cleaninterface. And you may replace the "-" by anything else if you want to. But if you don't, you will see 2 entries per line.
By the way: The selected entry is printed onto stderr.
This could need some more improving, but for a basic idea I think it's enough.
This seems to be one of the top results when you search for whiptail, and none of the previous results worked for me. This is what I wound up using:
#! /bin/bash
shopt -s nullglob
dir=`pwd`
cd /path/to/files
arr=(*.tgz)
for ((i=0; i<${#arr[#]}; i++)); do j=$((2*$i+2)); a[j]="${arr[$i]}"; a[j+1]=""; done
a[0]=""
# Next line has extra spaces at right to try to center it:
a[1]="Please make a selection from the files below "
result=$(whiptail --ok-button "OK button text" --cancel-button "Cancel Button Text" --title "Title Text" --backtitle "Text at upper left corner of page" --menu "Menu Text (not used??)" 30 160 24 "${a[#]}" 2>&1 >/dev/tty)
if [[ $? = 0 ]]
then
# ge 5 in next line should be length of file extension including . character, plus 1
[ ${#result} -ge 5 ] && outfile="/path/to/files/$result" || echo "Selection not made"
fi
cd "$dir"
$result will be empty if no valid selection was made. I added a dummy selection at the top of the list that returns an empty string as a result, so that you won't accidentally select the wrong file by accidentally hitting Enter right after the menu comes up. If you don't want that, then in the "for" line remove the +2 in "do j=$((2*$i+2))" and also the following two lines that set a[0] and a[1] explicitly.
The confusing thing about whiptail is that when reading from an array in a situation like this it expects two data items per line, both of which are displayed, the first being the result you want returned if the line is expected (which in some situations might be a letter or a number) and the second being whatever descriptive text you may want. That's why for the first line I use a[0] to give an empty string as the result, and a[1] as the descriptive text, but from there on the first item in the pair contains the filename (which is what I actually want returned) and the second is an empty string, since I don't want to display any text other than the filename on those lines.
Also a previous post said whiptail returned an error code of 255 if the cancel button was pressed, but that was not the case for the version I have - it returns 1. So I just test for an error code of 0 and if it is I assume it may be a valid entry, then I test for a valid string length (more than just the number of characters in the file extension, including the . character) to be sure.

Putting the output of a command with interaction inside a variable while using grep in bash

This program I use has it's own variables to set when you run it, so I want to set those variables and then greping the output then storing it inside a variable. However, I don't know how to go about this the correct way. The idea I have doesn't work. The focus is on lines 7 through 14.
1 #!/usr/local/bin/bash
2 source /home/gempak/NAWIPS/Gemenviron.profile
3 FILENAME="$(date -u '+%Y%m%d')_sao.gem"
4 SFFILE="$GEMDATA/surface/$FILENAME"
5 echo -n "Enter the station ID: "
6 read -e STATION
7 OUTPUT=$(sflist << EOF
8 SFFILE = $SFFILE
9 AREA = #$STATION
10 DATTIM = all
11 SFPARM = TMPF;DWPF
12 run
13 exit
14 EOF)
15 echo $OUTPUT
But I get this:
./listweather: line 7: unexpected EOF while looking for matching `)'
./listweather: line 16: syntax error: unexpected end of file
Putting together everyone's answers, I came across a working solution myself. This code works for me:
#!/usr/local/bin/bash
source /home/gempak/NAWIPS/Gemenviron.profile
FILENAME="$(date -u '+%Y%m%d')_sao.gem"
SFFILE="$GEMDATA/surface/$FILENAME"
echo -n "Enter the station ID: "
read -e STATION
OUTPUT=$(sflist << EOF
SFFILE = $SFFILE
AREA = #$STATION
DATTIM = ALL
SFPARM = TMPF;DWPF
run
exit
EOF
)
echo $OUTPUT | grep $STATION
Thanks everyone!
I'd put your program to run in a separate .sh script file, and then run the script from your first file, passing the arguments you want to pass as command line arguments. That way you can test them separately.
You could also do it in a function, but I like the modularity of the second script. I don't udnerstand exactly what you are trying to do above, but something like:
runsflist.sh:
#!/bin/bash
FILENAME="$(date -u '+%Y%m%d')_sao.gem"
SFFILE="$GEMDATA/surface/$FILENAME"
AREA = #$STATION
DATTIM = all
SFPARM = TMPF;DWPF
grep $STATION | sflist
main.sh:
#!/bin/bash
echo -n "Enter the station ID: "
read -e STATION
OUTPUT=`runsflist.sh`
echo $OUTPUT
If sflist needs interaction, I'd try something like this:
SFFILE=$(
( echo SFFILE = "$SFFILE"
echo AREA = "#$STATION"
echo DATTIM = all
echo SFPARM = TMPF;DWPF
echo run
cat
) | sflist)
Unfortunately, you have to type exit as part of the interaction.

Resources