How to create a CLI program with "shell prompt"? - bash

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

Related

What does "if [ -t 1 ]" do in shell scripting?

I have code for setting the zsh as default shell:
if [ -t 1 ]; then
exec zsh
fi
What exactly does the command if [ -t 1 ] do here?
I have code for setting the zsh as default shell:
No, you haven't. That's not what your code does, though it produces a similar effect.
if [ -t 1 ]; then
exec zsh
fi
What exactly does the command if [ -t 1 ] do here?
The command [ -t 1 ] is executed. If it exits with status 0 (indicating success, which for the [ command means the condition evaluates to true) then the commands in the body of the if statement are executed.
It may be a bit surprising that [ is a command, rather than part of shell syntax, but that's the case. Your if could be rewritten equivalently to use the test command instead:
if test -t 1; then
# ...
The other key thing, then, is the -t 1 part. You can find out about that in the manual for the test or [ command, but to save you the trouble, it is a conditional expression that evaluates whether file descriptor 1 (the shell's standard output) is connected to a terminal. This is similar to, but not exactly the same thing as, evaluating whether the shell is an interactive one.
Overall, the code presented has the effect of replacing the current (presumably bash) shell with zsh if the standard output is connected to a terminal. This is indirect, and a bit tricky; it would probably be better to genuinely set your login shell to /bin/zsh (or wherever it is installed) via the chsh command.
if command; then other_command; fi runs command and then, if that command exits with a return code of 0 ("success"), runs other_command.
The command [...] is designed to take the place of the Boolean expressions that you find in traditional programming languages. It has a number of options for what goes between the brackets and exits with 0=success if those options evaluate to a true value.
The specific subcommand -t tests a file descriptor to see if it is attached to a terminal. The file descriptor 1 is where the script's output is going (aka "standard output" or "stdout" for short). So -t 1 is true and [ -t 1 ] returns success if and only if the output of the script is going to a terminal (instead of into a file or pipe or something).
In that case, the current shell is replaced (via exec) by a copy of zsh. Which will hopefully not run the same script, since zsh works the same way and will make the same decision and go into an infinite loop execing itself.

Prevent other terminals from running a script while another terminal is using it

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

Passing variables to sftp batch

I'm writing a script that needs to pass a variable to the sftp batch. I have been able to get some commands working based on other documentation I've searched out, but can't quite get to what I need.
The end-goal is to work similar to a file test operator on a remote server:
( if [-f $a ] then:; else exit 0;)
Ultimately, I want the file to continue running the script if the file exists (:), or exit 0 if it does NOT exist (not exit 1). The remote machine is a Windows server, not Linux.
Here's what I have:
NOTE - the variable I'm trying to pass, $source_dir, changes based on the input parameter of the script that calls this function. This and the ls wildcard is the tricky part. I have been able to make it work when looking for a specific file, but not just "any" file.
${source_dir}= /this/directory/changes
RemoteCheck () {
/bin/echo "cd $source_dir" > someBatch.txt
/bin/echo "ls *" >> someBatch.txt
/usr/bin/sftp -b testBatch.txt -oPort=${sftp_port} ${sftp_ip}
exit_code=$?;
if [ $exit_code -eq 0 ]; then
:
else
exit 0
fi
There may be a better way to do this, but I have searched multiple forums and have not yet found a way to manipulate this.
Any help is appreciated, you gurus have always been very helpful!
You cannot test for existence of any file using just exit code of the OpenSSH sftp.
You can redirect the sftp output to a file and parse it to see if there are any files.
You can use shell echo command to delimit the listing from the rest of the output like:
!echo listing-start
ls
!echo listing-end

How-to check if Linux shell script is executed by a cronjob?

Is it possible to identify, if a Linux shell script is executed by a user or a cronjob?
If yes, how can i identify/check, if the shell script is executed by a cronjob?
I want to implement a feature in my script, that returns some other messages as if it is executed by a user. Like this for example:
if [[ "$type" == "cron" ]]; then
echo "This was executed by a cronjob. It's an automated task.";
else
USERNAME="$(whoami)"
echo "This was executed by a user. Hi ${USERNAME}, how are you?";
fi
One option is to test whether the script is attached to a tty.
#!/bin/sh
if [ -t 0 ]; then
echo "I'm on a TTY, this is interactive."
else
logger "My output may get emailed, or may not. Let's log things instead."
fi
Note that jobs fired by at(1) are also run without a tty, though not specifically by cron.
Note also that this is POSIX, not Linux- (or bash-) specific.

Installer on bash: how to show GUI messages?

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]}"

Resources