Hi everyone and thanks in advance.
I have a question for you.
In a shell script that I made to manage the startup/shutdown of a Jboss application cluster I use a function to answer various question during the process which tells the script wath to do
function ThreeOptions
{
ACT=0
read -n 1 -s answ
case ${answ:0:1} in
n|N) echo "Operation skipped"
ABRT=2
;;
e|E|x|X) echo "Operation aborted"
ABRT=3
CloseProcedure
;;
y|Y|*) echo "...ok..."
ACT=1
;;
esac
}
The function is written that way so that single pressure of the letter will select the option, and "Enter/Return" (well every other key indeed) will use the default.
How can I prevent the read command from reading unintentionally double/multiple keyboard inputs (aka sloppy hands).
EX.
echo -en " Shall I close all Jboss processes ? [Y/n/e]: "
ThreeOptions
...
echo -en " Shall I do some other thing?"
ThreeOptions
...
If I press "yy" I notice that I'm answering the ACTUAL question and the NEXT too.
PS: First question, so I hope it's understandable and that I didn't write too much :)
If you are using bash you can try the shell pattern matching in the bash case structure.
For example use +([y])|+([Y])|*) instead of y|Y|*)
Edit: remember to call shopt -s extglob in the function before using the case.
Related
I have a requirement where ScriptA.sh has commands to ask for User's inputs and perform a set of actions. I want to automate this by creating another script which will read the questions asked from output of ScriptA.sh and provide the necessary values in runtime.
ScriptA.sh as follows :-
echo "Enter the CR Number"
read varnamecr
echo "CR Number is" $varnamecr
echo "Loading the config set. Choose Option From Below set
1.JAN
2.FEB
3.MAR"
read optionchoosen
echo "Option Choosen is :" $optionchoosen
echo "Will run the script/load configuration is this Ok ?[y/N]"
read userinput
echo "Proceed further, User has pressed ->"$userinput"<--Key"
How to write the second script to achieve this. Tried spawn and few other commands in the second script, but no luck. Please help me with this.
Since you're not specifying any shell in your tag, this is a possible, albeit crude, solution in ksh. It's using the coprocess capability of that shell (pretty sure it's not supported in bash although please don't quote me on that one)
#!/bin/ksh
./ScriptA.sh |&
while read -p Dummy; do
print $Dummy
case $Dummy in
"Enter the CR Number")print -p "CR123456"
;;
"3.MAR")print -p "3"
;;
"Will run the script"*)print -p "y"
;;
esac
done
The output gives :
Enter the CR Number
CR Number is CR123456
Loading the config set. Choose Option From Below set
1.JAN
2.FEB
3.MAR
Option Choosen is : 3
Will run the script/load configuration is this Ok ?[y/N]
Proceed further, User has pressed ->y<--Key
Will input remain same everytime? If so you can create wrapper of this script to provide required input.
cat wrapper
./ScriptA.sh <<!
123
2
y
!
I would like prevent other terminals from running a certain script whenever another terminal is running it however in bash but I'm not quite sure on how I would be able to go about in doing it. Any help or tip could be greatly appreciated!
In example:
When that script is being run on another terminal, all other terminals would be unable to run that certain script as well. And display a message "UNDER MAINTENANCE".
You can use the concept of a "lockfile." For example:
if [ -f ~/.mylock ]; then
echo "UNDER MAINTENANCE"
exit 1
fi
touch ~/.mylock
# ... the rest of your code
rm ~/.mylock
To get fancier/safer, you can "trap" the EXIT signal to remove it automatically at the end:
trap 'rm ~/.mylock' EXIT
Use flock and put this on top of your script:
if ! flock -xn /path/to/lockfile ; then
echo "script is already running."
echo "Aborting."
exit 1
fi
Note: path/to/lockfile could be the path to your script. Doing so would avoid to create an extra file.
To avoid race conditions, you could use flock(1) along with a
lock file. There is one flock(1) implementation
which claims to work on Linux, BSD, and OS X. I haven't seen one
explicitly for Unix.
There is some interesting discussion here.
UPDATE:
I found a really clever way from Randal L. Schwartz here. I really like this one. It relies on having flock(1) and bash, and it uses the script itself as its own lockfile. Check this out:
/usr/local/bin/onlyOne is a script to obtain the lock
#!/bin/bash
exec 200< $0
if ! flock -n 200; then
echo "there can be only one"
exit 1
fi
Then myscript uses onlyOne to obtain the lock (or not):
#!/bin/bash
source /usr/local/bin/onlyOne
# The real work goes here.
echo "${BASHPID} working"
sleep 120
I tried to answer another SO question with a simple menu using the builtin select statement. The code displays names from /etc/passwd, and let you select a name by giving a number:
PS3="Enter a number: "
select name in $(cut -d: -f1 /etc/passwd) ; do
if [ -n "${name}" ]; then
break
fi
echo "Sorry, please enter a number as shown."
done
echo "Entry from passwd is: ${name}"
The works fine except for the very first time. When you give a correct answer the first time it will ask you to try again.
I tried to get a more detailed explanation of the first time, but I couldn't get a reproducable cook-book. When you copy-paste this code on your server, and give a correct answer you will probably have the same problem. When you repeat the command (from history or a new paste), the code shows now problem. I tried to get the problem again by logging out and logging in (sometimes it works) or rebooting.
I tried different ways to reproduce the problem in other situations (using different variable names, unsetting variables, using a slow list of values with select name in $(echo One; sleep 1; echo Two; sleep 2; echo Three; sleep 1); and opening a new shell.
I searched for other examples with select, but I can't find clues in other posts like https://stackoverflow.com/a/16750755/3220113 and https://askubuntu.com/a/1716.
I tried to fix my code with a sync and that seems to be a work-around:
PS3="Enter a number: "
select name in $(cut -d: -f1 /etc/passwd) ; do
# is sync needed here?
sync
if [ -n "${name}" ]; then
break
fi
echo "Sorry, please enter a number as shown."
done
echo "Entry from passwd is: ${name}"
I couldn't reproduce the error when I include the sync command. Is sync really a working patch and why do I need this here?
I do not need other ways to write a menu. I already found the graphical dialog Dialog from bash script and was looking for a simple replacement of my own over-complicated https://unix.stackexchange.com/a/115371/57293.
This problem only occurs when you type the commands interactively, not in a script. The reason is that the line you type after the select line is being used as the response to the prompt. Since if isn't in the menu, it reports an error. Then it doesn't execute the if command, because it was read as the response to the prompt.
It's not a problem in a script because the commands in the script are not used as standard input.
I want to create simple .sh script which will include binary package with my program and will copy it to destination folders. During Installation I want to show gui messages to imform user that all is ok. Seems like zenity from this question is that I need How to show a GUI message box from a bash script in linux?
But how to supply it with my single .sh script? (User should run installer from anywhere without any additional actions). Is there something universal for most common distributions of linux? Maybe "xmessage", but it looks very poor. Something else?
Anything like xmessage or zenity or gxmessage implies external dependencies that you cannot guarantee will be available (unless you can; you haven't said so in your question). To answer one of your questions, NO, there is nothing universal for Linux. Certainly not anything that depends on X, since so many Linux installations are headless.
For "something else", as a general principle, being self-contained is a good idea. That means using something that doesn't even depend on the X Window System. Shell based dialogs are readily available, whether you're in FreeBSD or Linux.
To be truly self-contained as well as portable (even between different distros of Linux, or different server configurations), I'd suggest writing your own dialog manager as a function within your shell script. Something along the lines of this:
#!/usr/bin/env bash
# A supporting function to see test a value against the contents of an array
is_in() {
value=$1; shift
for i in "$#"; do [[ $i == $value ]] && return 0; done
return 1
}
# Simple dialog implementation, no VT100 required,
dialog() {
# $options is an array of options
local i line max=0
# determine dialog width
for line in "${options[#]}"; do [[ ${#line} -gt $max ]] && max=${#line}; done
# draw a line
eval printf '%.s-' {1..$((max+8))}; echo
# print each option
for i in ${!options[#]}; do
printf "| %2d: %-${max}s |\n" "$i" "${options[i]}"
done
eval printf '%.s-' {1..$((max+8))}; echo
response=""
# only accept valid responses
while ! is_in "$response" "${!options[#]}"; do
read -p " Choose: " response
done
return "$response"
}
# Create our list, run the dialog, capture the result,
options=([1]="Hello world" [2]="This is a test")
dialog
result=$?
# And display the result.
echo "RESPONSE: $result / ${options[$result]}"
I am after a bash script which I can use to trigger a delta import of XML files via CRON. After a bit of digging and modification I have this:
#!/bin/bash
# Bash to initiate Solr Delta Import Handler
# Setup Variables
urlCmd='http://localhost:8080/solr/dataimport?command=delta-import&clean=false'
statusCmd='http://localhost:8080/solr/dataimport?command=status'
outputDir=.
# Operations
wget -O $outputDir/check_status_update_index.txt ${statusCmd}
2>/dev/null
status=`fgrep idle $outputDir/check_status_update_index.txt`
if [[ ${status} == *idle* ]]
then
wget -O $outputDir/status_update_index.txt ${urlCmd}
2>/dev/null
fi
Can I get any feedback on this? Is there a better way of doing it? Any optimisations or improvements would be most welcome.
This certainly looks usable. Just to confirm, you intend to run this ever X minutes from your crontab? That seems reasonsable.
The only major quibble (IMHO) is discarding STDERR information with 2>/dev/null. Of course it depends on what are your expectations for this system. If this is for a paying customer or employer, do you want to have to explain to the boss, "gosh, I didn't know I was getting error message 'Cant connect to host X' for the last 3 months because we redirect STDERR to /dev/null"! If this is for your own project, and your monitoring the work via other channels, then not so terrible, but why not capture STDERR to file, and if check that there are no errors. as a general idea ....
myStdErrLog=/tmp/myProject/myProg.stderr.$(/bin/date +%Y%m%d.%H%M)
wget -O $outputDir/check_status_update_index.txt ${statusCmd} 2> ${myStdErrLog}
if [[ ! -s ${myStdErrLog} ]] ; then
mail -s "error on myProg" me#myself.org < ${myStdErrLog}
fi
rm ${myStdErrLog}
Depending on what curl includes in its STDERR output, you may need filter what is in the StdErrLog to see if there are "real" error messages that you need to have sent to you.
A medium quibble is your use backticks for command substitution, if you're using dbl-sqr-brackets for evaluations, then why not embrace complete ksh93/bash semantics. The only reason to use backticks is if you think you need to be ultra-backwards compatible and that you'll be running this script under the bourne shell (or possibly one of the stripped down shells like dash).Backticks have been deprecated in ksh since at least 1993. Try
status=$(fgrep idle $outputDir/check_status_update_index.txt)
The $( ... ) form of command substitution makes it very easy to nest multiple cmd-subtitutions, i.e. echo $(echo one $(echo two ) ). (Bad example, as the need to nest cmd-sub is pretty rare, I can't think of a better example right now).
Depending on your situation, but in a large production environement, where new software is installed to version numbered directories, you might want to construct your paths from variables, i.e.
hostName=localhost
portNum=8080
SOLRPATH=/solr
SOLRCMD='delta-import&clean=false"
urlCmd='http://${hostName}:${portNum}${SOLRPATH}/dataimport?command=${SOLRCMD}"
The final, minor quibble ;-). Are you sure ${status} == *idle* does what you want?
Try using something like
case "${status}" in
*idle* ) .... ;;
* ) echo "unknown status = ${status} or similar" 1>&2 ;;
esac
Yes, your if ... fi certainly works, but if you want to start doing more refined processing of infomation that you put in your ${status} variable, then case ... esac is the way to go.
EDIT
I agree with #alinsoar that 2>/dev/null on a line by itself will be a no-op. I assumed that it was a formatting issue, but looking in edit mode at your code I see that it appears to be on its own line. If you really want to discard STDERR messages, then you need cmd ... 2>/dev/null all on one line OR as alinsoar advocates, the shell will accept redirections at the front of the line, but again, all on one line ;-!.
IHTH