-bash: cmd: command not found - bash

I have this in /etc/init.d/unicorn
#!/bin/bash
# /etc/init.d/unicorn
# ### BEGIN INIT INFO
# chkconfig: 2345 95 016
# processname: unicorn
# dscription: Stop/start unicorn
### END INIT INFO
export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
cmd() {
cd /vagrant
unicorn -p 3000 -D
}
# Start the service
start() {
su - vagrant -c cmd
}
### main logic ###
case "$1" in
start)
start
;;
*)
echo $"Usage: $0 {start}"
exit 1
esac
exit 0
I am trying to start unicorn in my local rails folder. I feel like this command should work:
su - vagrant -c cmd
and cannot figure out the reason.

I guess you are under the impression that the shell function cmd which is defined in that script should be available in the shell which has started that script earlier.
This is wrong, unless you sourced that script (which is unusual for scripts in /etc/init.d/). When you start a script (not source it), then you start a second process which executes the script. All definitions of shell functions (like that cmd) are valid only within that shell script and die with the process.
In case you really want the cmd to be available, you will have to source the script unicorn:
source /etc/init.d/unicorn
The su command, however, will still not be able to call that shell function because it only can call executables it can start using exec(), so they need to be a file. A shell function isn't.

To fix this, inline the function in the shell invoked by su:
start() {
su - vagrant -c 'cd /vagrant && unicorn -p 3000 -D'
}
and as for figuring out the reason in the first place, tooling is helpful:
$ shellcheck unicorn
In unicorn line 19:
su - vagrant -c cmd
^-- SC2033: Shell functions can't be passed to external commands.

Related

Not able exit from docker container with bash script containing exit command

I'm able to exit when I enter the exit command in container environment. But if I try to run a script file having the exit command, I'm not able to exit from the container.
1.working
ubuntu#iot-docker:/repo$ exit
exit
root#iot-docker:/repo# exit
exit
ubuntu#ubuntu-***-Twr:~/shirisha/plo-***-snt-sp_u103a3$
not working
script.sh
#!/bin/bash
exit
exit
exit is not a command to exit your container, it just exits the current shell interpreter.
When you run your script, a new shell interpreter is started according to the first line of your script (here /bin/bash). When it encounters the exit command, the interpreter stops and you get back to the command line (the previous shell).
You can make this expriment:
$ bash # Starts a new shell
$ exit # Exits the new shell; we come back to the old one
exit
$
See? Running bash in command line is similar to running your script, and exiting from it brings you back to your previous shell. You didn't exit your container.
Solution:
exec script.sh param1 ... paramN
exec will replace your current shell with the command being started (script.sh). When that command exits, you will exit your container because your old shell no longer exists.
When you script a script without "sourcing" the script, the script will be started in a new subprocess. The exit works, you will finish that subprocess.
It is important to remember, that a script starts a new environment.
Look at the script example.sh
#!/bin/bash
my_value=high
cd /tmp
Call this script with
cd $HOME
my_value="low"
./example.sh
pwd
echo "My value is now ${my_value}"
Now nothing has changed: all changes in the subprocess are gone.
You can call this script with source ./example.sh (or short . ./example.sh),
and things have changed.
When you don't want to source your script, a function (in .bashrc) might help:
example() {
my_value=high
cd /tmp
}
Now you can call the function:
cd $HOME
my_value="low"
example
pwd
echo "My value is now ${my_value}"

Using 'exec' in bash_profile stops login shell

I'm creating the following .bash_profile (from linuxfromscratch guide) for lfs user:
exec env -i HOME=$HOME TERM=$TERM PS1='\u:\w\$ ' /bin/bash
When executing su - lfs I get:
[1]+ Stopped su - lfs
Executing fg resumes lfs' user shell. Why is this happening?
That's because exec executes the code in the current process. Normally a command is executed in a child shell/environment. Try the following:
$ bash # open second shell
$ exec false # close second shell
$ echo $? # get exit code
$ exit # close terminal
The man page isn't really helpful here. I often use exec if I run a script through a Qt process and it should end after some period of time, regardless whether the command if finished or not.

Exit from SSH using a sh script

I have a RHEL box (bash) and I have SSH'd to an ESXi (sh) from it.
Now on ESXi I have created a simple script
#!/bin/sh
echo hello
exit
This only exits the script. I want to exit the script + exit the ESXi shell and return to my original RHEL bash.
Thanks much.
If you are only SSHing in for the purpose of running this command, then instead you could just have the ssh run the command for you:
[RHEL]$ ssh user#ESXi '/tmp/myscript.sh'
...and if you needed to interact with the script, or watch it's output, add the -t switch:
[RHEL]$ ssh -t user#ESXi '/tmp/mysctipt.sh'
Remove the shebang ie do
echo hello && exit
save it as script and then source the script like
. script

Script not working as Command line

i've created simple bash script that do the following
:
#!/usr/bin/env bash
cf ssh "$1"
When I run the command line from the CLI like cf ssh myapp its running as expected, but when I run the script like
. myscript.sh myapp
I got error: App not found
I dont understand what is the difference, I've provided the app name after I invoke the script , what could be missing here ?
update
when I run the script with the following its working, any idea why the "$1" is not working ...
#!/usr/bin/env bash
cf ssh myapp
When you do this:
. myscript.sh myapp
You don't run the script, but you source the file named in the first argument. Sourcing means reading the file, so it's as if the lines in the file were typed on the command line. In your case what happens is this:
myscript.sh is treates as the file to source and the myapp argument is ignored.
This line is treated as a comment and skipped.
#!/usr/bin/env bash
This line:
cf ssh "$1"
is read as it stands. "$1" takes the value of $1 in the calling shell. Possibly - most likely in your case - it's blank.
Now you should know why it works as expected when you source this version of your script:
#!/usr/bin/env bash
cf ssh myapp
There's no $1 to resolve, so everything goes smoothly.
To run the script and be able to pass arguments to it, you need to make the file executable and then execute it (as opposed to sourcing). You can execute the script for example this way:
./script.bash arg1 arg2

How to source a csh script from inside a bash script

My default shell is bash. I have set some environment variables in my .bashrc file.
I installed a program which use .cshrc file. It contains the path to several cshell scripts.
When I run the following commands in the shell windows it works perfectly :
exec csh
source .cshrc
exec bash
I have tried to put these commands in bash script, unfortunately it didn't work.
is there another way to write a script in order to get the same result as running commands from a shell windows.
I hope my question is now clear
Many thanks for any help
WARNING : don't put the following script in your .bashrc, it will reload bash and so reload .bashrc again and again (stopable with C-c anyway)
Use preferable this script in your kit/CDS stuff startup script. (cadence presumably)
WARNING 2 : if anything in your file2source fails, the whole 'trick' stops.
Call this script : cshWrapper.csh
#! /bin/csh
# to launch using
# exec cshWrapper.csh file2source.sh
source $1
exec $SHELL -i
and launch it using
exec ./cshWrapper.csh file2source.sh
it will : launch csh, source your file and came back to the same parrent bash shell
Example :
$> ps
PID TTY TIME CMD
7065 pts/0 00:00:02 bash
$>exec ./cshWrapper.csh toggle.csh
file sourced
1
$> echo $$
7065
where in my case i use the file toggle.csh
#! /bin/csh
# source ./toggle.csh
if ! $?TOGGLE then
setenv TOGGLE 0
endif
if ($?TOGGLE) then
echo 'file sourced'
if ($TOGGLE == 0) then
setenv TOGGLE 1
else
setenv TOGGLE 0
endif
endif
echo $TOGGLE
Hope it helps
New proposal, since I faced another problem with exec.
exec kills whatever remains in the script, except if you force a fork by using a pipe after it `exec script |cat'. In such case if you have environment variable in the script, they are not spread back to the script itself, which is not what we want. The only solution I found is to use 3 files (let's call them for the example : main.bash that call first.cshrc and second.sh).
#! /bin/bash
#_main.bash_
exec /bin/csh -c "source /path_to_file/cshrc; exec /bin/bash -i -c /path_to_file/second.sh"
# after exec nothing remains (like Attila the Hun)
# the rest of the script is in 'second.sh'
With that manner, i can launch in a single script call, an old cshrc design kit, and still process some bash command after, and finally launch the main program in bash (let say virtuoso)

Resources