Preventing to start bash script with ./ (dot slash) - linux

I wrote a lot of bash scripts that should work with the current bash session, because I often used fg, jobs, etc.
I always starts my scripts with . script.sh but one of my friends startet it with ./script.sh and got error that fg "couldn't be executed".
Is it possible to force a . script.sh or anything else what I can do to prevent errors? Such as cancel the whole script and print an error with echo or something else.
Edit:
I think bashtraps have problems when executing sourced, is there any way to use fg, jobs and bashtraps in one script?

Looks like you're trying to determine if a script is being run interactively or not. The bash manual says that you can determine this with the following test:
#! /bin/bash
case "$-" in
*i*) echo interactive ;;
*) echo non-interactive ;;
esac
sleep 2 &
fg
If you run this with ./foo.sh, you'll see "non-interactive" printed and an error for the fg built-in. If you source it with . foo.sh or source foo.sh you won't get that error (assuming you're running those from an interactive shell, obviously).
For your use-case, you can exit with an error message in the non-interactive mode.

If job control is all you need, you can make it work both ways with #!/bin/bash -i:
#!/bin/bash -i
sleep 1 &
fg
This script works the same whether you . myscript or ./myscript.
PS: You should really adopt your friend's way of executing scripts. It's more robust and most people write their scripts to work that way (e.g. assuming exit will just exit the script).

There are a couple of simple tricks to remind people to use source (or .) to run your script: First, remove execute permission from it (chmod -x script.sh), so running it with ./script.sh will give a permission error. Second, replace the normal shebang (first line) with something like this:
#!/bin/echo please run this with the command: source
This will make the script print something like "please run this with the command: source ./script.sh" (and not run the actual script) if someone does manage to execute it.
Note that neither of these helps if someone runs the script with bash script.sh.

Related

How to solve bash error "syntax error at line 3: 'CYBER_UNAME=$' unexpected"?

This error happens when I run a software containing bash script with beggining like this:
#! /bin/sh
CYBER_UNAME=$(uname)
CYBER_UNAME_M=$(uname -m)
I tried to execute these two commands in terminal and it works fine. This error only happens when I run the shell script. What should I do?
The result of 'uname' is SunOS. This shell script cannot be modified since it's protected on our server.
The line
#! /bin/sh
should read:
#!/bin/bash
So, that script will probably never really work.
If you cannot modify the script in situ, you might want to copy it to your local directory and correct it.
Otherwise,
tail +2 scriptname|/bin/bash
might work.

Shell script will not be terminated and i need to kill it manually

I'm new to write shell script, and I looked at some tutorial on the Internet
and now I try to write my first script, it's simple:
#! /bin/csh
echo "Hello World! \a \n"
exit 0
It looks simple and correct, it will show Hello World on my terminal, but it won't be terminated -- I need to ctrl+c to stop the shell.
But I try
#! /bin/sh
echo "Hello World! \a \n"
exit 0
This can be terminated normally, I know that first line is to specify what shell
you want to use to execute the script, and I used the set command to find out my default shell is /bin/csh
Other users on this server are all ok.
Does everyone know how can I fix the problem? I can't use some shell scripts written by my friend.
modify the shebang from #! /bin/csh to #! /bin/csh -f
it will works !!
but i'm still confused why that other users on the same server, they can use the existed script that the shebang is /bin/csh without -f
is there anything wrong in my ~/.cshrc ?
Sorry for post another answer again
I found that if I run the script by "source filename"
it's terminated normally
so, maybe there's some reason that my child shell can't close normally and maybe in the loop??
you can use screen command to run your script
$screen ./script.sh
you will see that the process you left is still running.
$screen -r

Bash: Only allow script to run by being called from another script

We have two bash scripts to start up an application. The first (Start-App.sh) one sets up the environment and the second (startup.sh) is from a 3rd party that we are trying not to heavily edit. If someone runs the second script before the first the application does not come up correctly.
Is there a way to ensure that the startup.sh can only be called from the Start-App.sh script?
They are both in the same directory and run via bash on Red Hat Linux.
Is there a way to ensure that the startup.sh can only be called from the Start-App.sh script?
Ensure? No. And even less so without editing startup.sh at all. But you can get fairly close.
Below are three suggestions − you can either use one of them, or any combination of them.
The simplest, and probably the best, way is to add a single line at the top of startup.sh:
[ -z $CALLED_FROM_START_APP ] && { echo "Not called from Start-App.sh"; exit 42; }
And then call it from Start-App.sh like so:
export CALLED_FROM_START_APP=yes
sh startup.sh
of course, you can set this environment variable from the shell yourself, so it won't actually ensure anything, but I hope your engineering staff is mature enough not to do this.
You can also remove the execute permissions from startup.sh:
$ chmod a-x startup.sh
This will not prevent people from using sh startup.sh, so there is a very small guarantee here; but it might prevent auto-completion oopsies, and it will mark the file as "not intended to be executed" − if I see a directory with only one executable .sh file, I'll try and run that one, and not one of the others.
Lastly, you could perhaps rename the startup.sh script; for example, you could rename it to do_not_run, or "hide" it by renaming it to .startup. This probably won't interfere with the operation of this script (although I can't check this).
TL;DR:
[ $(basename "$0") = "Start-App.sh" ] || exit
Explanation
As with all other solutions presented it's not 100% bulletproof but this covers most common instances I've come across for preventing accidentally running a script directly as opposed to calling it from another script.
Unlike other approaches presented, this approach:
doesn't rely on manually set file names for each included/sourced script (i.e. is resilient to file name changes)
behaves consistently across all major *nix distros that ship with bash
introduces no unnecessary environment variables
isn't tied to a single parent script
prevents running the script through calling bash explicitly (e.g. bash myscript.sh)
The basic idea is having something like this at the top of your script:
[ $(basename "$0") = $(basename "$BASH_SOURCE") ] && exit
$0 returns the name of the script at the beginning of the execution chain
$BASH_SOURCE will always point to the file the currently executing code resides in (or empty if no file e.g. piping text directly to bash)
basename returns only the main file name without any directory information (e.g. basename "/user/foo/example.sh" will return example.sh). This is important so you don't get false negatives from comparing example.sh and ./example.sh for example.
To adapt this to only allow running when sourced from one specific file as in your question and provide a helpful error message to the end user, you could use:
[ $(basename "$0") = "Start-App.sh" ] || echo "[ERROR] To start MyApplication please run ./Start-App.sh" && exit
As mentioned from the start of the answer, this is not intended as a serious security measure of any kind, but I'm guessing that's not what you're looking for anyway.
You can make startup.sh non-executable by typing chmod -x startup.sh. That way the user would not be able to run it simply by typing ./startup.sh.
Then from Start-App.sh, call your script by explicitly invoking the shell:
sh ./startup.sh arg1 arg2 ...
or
bash ./startup.sh arg1 arg2 ...
You can check which shell it's supposed to run in by inspecting the first line of startup.sh, it should look like:
#!/bin/bash
You can set environment variable in your first script and before running second script check if that environment variable is set properly.
Another alternative is checking the parent process and finding the calling script. This also needs adding some code to the second script.
For example, in the called script, you can check the exit status of this and terminate.
ps $PPID | tail -1 | awk '$NF!~/parent/{exit 1}'
As others have pointed out, the short answer is "no", although you can play with permissions all day but this is still not bulletproof. Since you said you don't mind editing (just not heavily editing) the second script, the best way to accomplish this would be something along the lines of:
1) in the parent/first script, export an environment variable with its PID. This becomes the parent PID. For example,
# bash store parent pid
export FIRST_SCRIPT_PID = $$
2) then very briefly, in the second script, check to see if the calling PID matches the known acceptable parent PID. For example,
# confirm calling pid
if [ $PPID != $FIRST_SCRIPT_PID ] ; then
exit 0
fi
Check out these links here and here for reference.
To recap: the most direct way to do this is adding at least a minimal line or two to the second script, which hopefully doesn't count as "heavily editing".
You can create a script, let's call it check-if-my-env-set containing
#! /bin/bash
source Start-App.sh
exec /bin/bash $#
and replace the shebang (see this) on startup.sh by that script
#! /abs/path/to/check-if-my-env-set
#! /bin/bash
...
then, every time you run startup.sh it will ensure the environment is set correctly.
To the best of my knowledge, there is no way to do this in a way that it would be impossible to get around it.
However, you could stop most attempts by using permissions.
Change the owner of the startup.sh file:
sudo chown app_specific_user startup.sh
Make startup.sh only executable by the owner:
chmod u+x startup.sh
Run startup.sh as the app_specific_user from Start-App.sh:
sudo -u app_specific_user ./startup.sh

bash is not executed 'at -f foo.sh' command, even with #!/bin/bash shebang

I'm using the 'at' command in order to create 3 directories, just a dumb bash script:
#!/bin/bash
for i in {1..3}
do
mkdir dir$i
done
Everything is ok if I execute that script directly on terminal, but when I use 'at' command as follows:
at -f g.sh 18:06
It only creates one directory named dir{1..3}, taking interval not as an interval but as a list with one element {1..3}. According to this I think my mistake is using bash script due to at executes commands using /bin/sh but I'm not sure.
Please tell me if I'm right and I would appreciate some alternative to my code since even it is useless I'm curious to know what's wrong with at and bash.
The #! line only affects what happens when you run a script as a program (e.g. using it as a command in the shell). When you use at, it's not being run as a program, it's simply used as the standard input to /bin/sh, so the shebang has no effect.
You could do:
echo './g.sh' | at 18:06

How to create file with instruction of commands?

I have a file name myFirstFile that contains certain commands.
But I am not able to excecute them.
If I want to execute this as a program, which code should be implemented?
If you want to execute your program, it should start with:
#!/bin/sh
It's the generic script file "header". It indicates that the script is a shell script (if it's bash script you should have #!/bin/bash, etc.). If you want to execute it, you should call chmod +x ./myFirstFile to give privileges to call it as program, and then you can start your script normally: ./myFirstFile.
Make this file executable* and give it *.sh extention like:
"myFirstFile.sh"
Than run it from terminal (or crontab - it can do things for you when you sleep :) ) like:
cd directory/you/have/that/file
sh ./myFirstFile.sh
*Im not shure that making it executable is the most secure thing you can do. All my sh scripts are and I never digged into this issue, so make sure its cool
Also make sure you have "#!/bin/bash" in first line - sometimes it helps (dont know why, Google it)
edit: for example my script for starting Minecraf server looks like this
start.sh
#!/bin/bash
BINDIR=$(dirname "$(readlink -fn "$0")")
cd "$BINDIR"
while true
do
java -Xmx3584M -jar craftbukkit.jar
echo -e 'If you want to completely stop the server process now, press ctrl-$
echo "Rebooting in:"
for i in {5..1}
do
echo "$i..."
sleep 1
done
echo 'Restarting now!'
done
You have to make the file executable:
chmod +x myFirstFile
Then you can execute the commands in it:
./myFirstFile

Resources