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]}"
Related
I want to call a program when any SSH user logs in that prints a welcome message. I did this by editing the /etc/ssh/sshrc file:
#!/bin/bash
ip=`echo $SSH_CONNECTION | cut -d " " -f 1`
echo $USER logged in from $ip
For simplicity, I replaced the program call with a simple echo command in the example
The problem is, I learned SCP is sensitive to any script that prints to stdout in .bashrc or, apparently, sshrc. My SCP commands failed silently. This was confirmed here: https://stackoverflow.com/a/12442753/2887850
Lots of solutions offered quick ways to check if the user is in an interactive terminal:
if [[ $- != *i* ]]; then return; fi link
Fails becase [ is not linked
case $- in *i* link
Fails because in is not recognized?
Use tty program (same as above)
tty gave me a bizarre error code when executed from sshrc
While all of those solutions could work in a normal BASH environment, none of them work in the sshrc file. I believe that is because PATH (and I suspect a few other things) aren't actually available when executing from sshrc, despite specifying BASH with a shebang. I'm not really sure why this is the case, but this link is what tipped me off to the fact that sshrc is running in a limited environment.
So the question becomes: is there a way to detect interactive terminal in the limited environment that sshrc executes in?
Use test to check $SSH_TTY (final solution in this link):
test -z $SSH_TTY || echo $USER logged in from $ip
I'm just learning bash scripting. It's the first time I have to redirect output to another program and I don't know how to do it.
I have to write a script which connects a GUI program, and zero, one or two programs - I need two players, both can be computer or human. GUI gets output from both programs (or humans, I mean from stdin).
Let's assume that there is one human and one comp_player. Human gives command using stdin, this command has to be redirected to running GUI program and running comp_player, both expecting input. Then, comp_player's output has to be redirected to GUI (if there were second computer player, it would also be necessary to redirect this output to second computer player's input). The turn ends.
I know how to create a file to read and write and redirect input or output from it. For example:
echo "anything" >&3
exec 3<>sometextfile
read line <&3
echo $line
But what I don't know is how to redirect, for example, line I just read to running program who expects input and capture its output, which I can redirect to GUI and another program.
I know it isn't as simple as code above and I that have to use something called named pipes, but I tried to read some tutorials and I failed to write working script.
Can you give me an example of fragment of a script which, say:
(gui program and computer player program are running)
-reads line from stdin
-"sends" the line to gui program's and comp_player's inputs
-"reads" output from comp_player and writes it to stdout and also "sends" it to gui input
Named pipes are a special kind of files used to connect the input and output of two completely separate programs. Think of it as a temporary buffer, or an array that's shared between two programs that don't know about each other. This makes them an awesome tool to share messages between the two programs and get them to communicate very effectively.
As a simple test to see how a named pipe works, open two terminals in the same directory, and type mkfifo mypipe in the first one to create the file. Now, to use it just write something to it, for example: echo "A very important message" > mypipe
Now the message is stored in the pipe file, you will see the terminal is blocked, as if the echo hadn't finish. Go to the second terminal and get the contents of the pipe using: cat mypipe
You will print out the "very important message" you stored in the piped from the first terminal. Notice the pipe is empty now, and you simply can't get the message again from it.
Now that you know how named pipes work, here's a very simple example of how three players would communicate. Notice that we can't use a single file for all of them, instead we will create separate pipes to communicate player1 and player2, player1 and gui, and player2 and gui. I'm guessing the gui program is written in another language, but I will leave that to you.
PLAYER 1 (HUMAN)
player2pipe="pipe1"
guipipe="pipe2"
#First make sure we have our files
if [ ! -p $player2pipe ]; then
mkfifo $player2pipe
fi
if [ ! -p $guipipe ]; then
mkfifo $guipipe
fi
while true; do #Or until the game ends
echo -n "Do something: "
read move
# Send our move to the other two players
echo $move > $player2pipe
echo $move > $guipipe
playermove=$(cat $player2pipe) # Read other player's move from the pipe file. The execution will pause until there's something to read
# Do something about that move here
done
PLAYER2 (COMPUTER)
player1pipe="pipe1"
guipipe="pipe3"
if [ ! -p $player1pipe ]; then
mkfifo $player1pipe
fi
if [ ! -p $guipipe ]; then
mkfifo $guipipe
fi
while true; do
playermove=$(cat $player1pipe)
# Do something about that move here
move="A very good move made by a computer" #Obviously you will have to generate a new move
echo $move > $player1pipe
echo $move > $guipipe
done
GUI
player1pipe="pipe2"
player2pipe="pipe3"
if [ ! -p $player1pipe ]; then
mkfifo $player1pipe
fi
if [ ! -p $player1pipe ]; then
mkfifo $player1pipe
fi
while true; do #Or until the game ends
# Read other players' move from the pipe files. Notice the order here, if player2 moved before player1 the execution would be locked until the pipe is emptied
player1move=$(cat $player1pipe)
player2move=$(cat $player2pipe)
#Print out their move or whatever you need to do with it.
echo $player1move
echo $player2move
# Do whatever else you need to do about those moves
done
Save the three files in the same directory and execute them from three different terminals to see how they work.
Hope I helped.
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
I'm very new in programming. The following task looks very simple but I don't know how to do it. Appreciate if anyone can give me some guidance.
I'm using Linux OS. I would like to create a CLI program and let the user run it in shell terminal. I plan to use Bash shell script to create this program, but C++ or Perl should be ok too. The CLI program will accept commands from the user, execute it, and then optionally show the result summary on the screen.
When the CLI program is running, I would like to make it always shows a "shell prompt" at the left-hand side of the shell terminal, just like the Bash shell prompt. The prompt indicates that the CLI program is waiting for the user to type in a command and press the return key.
[AAA#Bash user]$ # This is Bash shell
[AAA#Bash user]$./CliApp.sh
CliApp > # The CLI program is running, user can type in command here
CliApp > exit
[AAA#Bash user]$ # Now the user has returned to the Bash shell
I think I know how to print something on the screen and get inputs from the users, but I found that it looks very unprofessional. I believe that there is a better way to create this kind of program.
Can anyone give me some guidance how to create this kind of program? Appreciate if you can give me the links to any example program. Thanks.
Are you looking for something along the lines of the following?
#!/bin/bash
input=""
while [ "$input" != "exit" ]
do
echo -n "cliApp($mode) > " && read input
if [ "$input" = "config" ]
then
mode="config"
elif [ "$input" = "up" ]
then
mode=""
elif [ "$input" = "exit" ]
then
break
else
if [ "$mode" = "config" ]
then
if [ "$input" = "list" ]
then
echo "ABCD"
else
echo "Invalid"
fi
else
echo "Invalid"
fi
fi
done
exit
Writing a command interpreter through shell script sounds a bit redundant to me, since you must be using a command interpreter (bash, csh, sh, or something else) to be able to do that.
It is possible to customize your bash prompt if that's what you're looking for.
There are some C/C++ libraries you could used to assist you on building your own command processor/interpreter, that includes fancy features, like tab completion. You should take look at these:
The GNU Readline Library
Editline Library
would like to add new functionality to the bash shell. I need to have a queue for executions.
What is the easy way to add new functionality to the bash shell keeping all native functions?
I would like to process the command line, then let the bash to execute them. For users it should be transparent.
Thanks Arman
EDIT
I just discovered prll.sourceforge.net it does exactly what I need.
Its easier than it seems:
#!/bin/sh
yourfunctiona(){ ...; }
...
yourfunctionz(){ ...; }
. /path/to/file/with/more/functions
while read COMMANDS; do
eval "$COMMANDS"
done
you can use read -p if you need a prompt or -t if you want it to timeout ... or if you wanted you could even use your favorite dialog program in place of read and pipe the output to a tailbox
touch /tmp/mycmdline
Xdialog --tailbox /tmp/mycmdline 0 0 &
COMMANDS="echo "
while ([ "$COMMANDS" != "" ]); do
COMMANDS=`Xdialog --stdout --inputbox "Text here" 0 0`
eval "$COMMANDS"
done >>/tmp/mycmdline &
To execute commands in threads you can use the following in place of eval $COMMANDS
#this will need to be before the loope
NUMCORES=$(awk '/cpu cores/{sum += $4}END{print sum}' /proc/cpuinfo)
for i in {1..$NUMCORES};do
if [ $i -eq $NUMCORES ] && #see comments below
if [ -d /proc/$threadarray[$i] ]; then #this core already has a thread
#note: each process gets a directory named /proc/<its_pid> - hacky, but works
continue
else #this core is free
$COMMAND &
threadarray[$i]=$!
break
fi
done
Then there is the case where you fill up all threads.
You can either put the whole thing in a while loop and add continues and breaks,
or you can pick a core to wait for (probably the last) and wait for it
to wait for a single thread to complete use:
wait $threadarray[$i]
to wait for all threads to complete use:
wait
#I ended up using this to keep my load from getting to high for too long
another note: you may find that some commands don't like to be threaded, if so you can put the whole thing in a case statement
I'll try to do some cleanup on this soon to put all of the little blocks together (sorry, I'm cobbling this together from random notes that I used to implement this exact thing, but can't seem to find)