Running bash function in command of su - bash

In my bash script, I execute some commands as another user. I want to call a bash function using su.
my_function()
{
do_something
}
su username -c "my_function"
The above script doesn't work. Of course, my_function is not defined inside su. One idea I have is to put the function into a separate file. Do you have a better idea that avoids making another file?

You can export the function to make it available to the subshell:
export -f my_function
su username -c "my_function"

You could enable 'sudo' in your system, and use that instead.

You must have the function in the same scope where you use it. So either place the function inside the quotes, or put the function to a separate script, which you then run with su -c.

Another way could be making cases and passing a parameter to the executed script.
Example could be:
First make a file called "script.sh".
Then insert this code in it:
#!/bin/sh
my_function() {
echo "this is my function."
}
my_second_function() {
echo "this is my second function."
}
case "$1" in
'do_my_function')
my_function
;;
'do_my_second_function')
my_second_function
;;
*) #default execute
my_function
esac
After adding the above code run these commands to see it in action:
root#shell:/# chmod +x script.sh #This will make the file executable
root#shell:/# ./script.sh #This will run the script without any parameters, triggering the default action.
this is my function.
root#shell:/# ./script.sh do_my_second_function #Executing the script with parameter
this function is my second one.
root#shell:/#
To make this work as you required you'll just need to run
su username -c '/path/to/script.sh do_my_second_function'
and everything should be working fine.
Hope this helps :)

Related

Chaining commands together in .bashrc

I'm trying to chain two commands together in a function or alias.
What I want to do is ssh into a proxy box, and then into another box from there. So something like:
ssh -J mylogin#host mylogin#host2
So far i've tried:
function doot {ssh -J mylogin#host && mylogin#"$1"}
and:
function doot {ssh -J mylogin#host; mylogin#"$1"}
and:
alias doot="ssh -J mylogin#host; mylogin#"$1""
It either doesn't recognize the function, or the alias just gives me an error. I feel that it's having an issue with the "$1" but i'm not sure how to chain these two commands together.
I want to just type in doot [nameofhost] and execute the command
ssh -J mylogin#host mylogin#host2
Neither of your attempted functions or alias do ssh -J mylogin#host mylogin#host2. Why?
The use of && and ';' separate commands. In your case that would make two separate commands out of ssh -J mylogin#host and mylogin#"$1". You need a single command specifying mylogin#host as the jump-host and mylogin#"$1" as the final destination. Simply do:
doot() {
ssh -J mylogin#host mylogin#"$1"
}
(note: quotes are not wrong but not entirely needed as hostnames don't contain whitespace. Also note "$1" within the function refers to the function argument, not the command line argument for your script. You would need to call as doot "$1". There is no ';' or && involved.)
There is a problem with alias as an alias does not contain arguments. From man bash:
There is no mechanism for using arguments in the replacement text.
If arguments are needed, a shell function should be used (see FUNCTIONS below).
Also, you want to validate that $1 has been given. You can do that with:
[ -z "$1" ] && { ## validate at least one argument given
printf "error: destination hostname required as argument.\n" >&2
return 1
}
(note: if you are calling the function that includes this test from the command line in the parent shell, your function should return instead of exit. In that case exit would exit the parent shell. If you use the function within a separate script you call from the parent, then you want exit to close the subshell)
You did not specify if the proxy required any additional parameters, sssuming it just a jump box, allowing ssh
Function foo {
ssh mylogin#host ssh mylogin#host2
}
If your current user is ‘mylogin’, you can abbreviate to
ssh host ssh host2

Bash Scripting; alias command inside a x.sh script

I'm trying to write a simple script using bash which must take two variables: a name and a command; then apply these two variables inside a "alias" command like so: alias $1=$2.
It`s for an online school activity which teaches introduction to bash scripting.
What I have come up with is:
function doalias {
alias $1=$2
}
doalias $1 $2
Next thing I do is write inside the terminal ./doalias.sh inst "sudo apt-get install".
Now the message I get from the bash terminal is that "=" is not recognized as a command. Why is this and how can I solve it?
You need to put the passed parameter in quotes when actioning the alias command and so:
function doalias {
alias "$1=$2"
}
doalias "$1" "$2"
To have the alias set in the current shell, you will need to source the script and so:
source doalias.sh inst "sudo apt-get install"

run a custom script when called with no args

. Working language is python. I want to attach a shell script when no argument is given or on own option.
Sorry if there is any confusion..
The main working flow is im working on a commmad called kubectl. So if the user enters this command without any arguments(my own option on this command) then i want to execute a shell script.
You can override kubectl by a function to accomplish that. Just append the function below to your .bashrc.
kubectl()
case $* in
( kadalu ) /path/to/script ;;
( * ) command kubectl "$#"
esac

How to avoid blackboxing function output in bash?

If I have a bash script that is purely made of functions, how do I get things like prompts to show up in the terminal? For example, consider the following:
prompt() {
read -p "This is an example prompt. [Y/n]"
}
main() {
prompt
}
main "$#"
How do I get that prompt message to show up in the terminal? When I just call prompt() from main(), it blackboxes the whole prompt() function. Do I have to return something from prompt()? What if I want to echo a bunch of messages after the read in prompt()? How do I get those to show up in the terminal?
I think I'm missing a basic programming concept here.
If you run a script with that code, main will not be executed. An alternative is to leave prompt() as a function, and move the functionality of main() outside of the function.
prompt() {
read -p "This is an example prompt. [Y/n]"
}
prompt
If you would like to keep main as a function, then you will have to source the file, and then call main.
$ source file.sh
$ main
where file.sh is the file with your code, and $ denotes the terminal prompt.
Answer to last question 1st. Yes, 'shell' is executed linearly (functions must appear before they are called and only get executed when called) and main is not automatically called.
The default variable for a read is $REPLY unless you supply your own variable(s), so:
echo $REPLY
or
read -p "Enter data?" MYVAR; echo $MYVAR
or
read -p "Enter data?" FIRSTVAR RESTOFSENTENCE; echo $FIRSTVAR:$RESTOFSENTENCE
if you want to do more stuff after your "main", you can just add those commands in the sequence you want them to occur either inside of main (provided that main is called) or after the call to main
if you want them to just be functions, you can save them to a file called myfuncs.sh and then from a shell source that and run a function(s):
. myfuncs.sh && main
How are you executing the script, and what's calling main()?
Bash scripts aren't like C programs and don't require a main() function. Bash will "remember" any functions it sees, but you actually need to call them directly from the script.
Try this:
# Start of script
prompt() {
read -p "This is an example prompt. [Y/n]"
}
prompt
# End of script
bash$ chmod +x script_name
bash$ ./script_name
you type prompt instead of prompt()
better is...
function prompt() {
read -p "$* [Y/n]"
}
then
prompt "This is an example prompt."
My understanding of your question is this:
You have a set of functions (of bash shell scripting) and you want them to return values to the calling points.
Example in C++:
char * prompt() {
std::cout<<"Some message:";
std::cin>>value;
return value;
}
This kind of return values are impossible in the bash(or other bourne descendant shells) scripting. Functions only have an exit value(similar to commands), for example, an implementation of the false command:
myfalse() { return 1; }
Thus return only sets a zero or nonzero exit status.
To use a value from a function, the function needs to put the value to standard output and that output captured via command substitution. For example:
toupper() {
val="$(echo $1| tr 'a-z' 'A-Z')"
echo $val
}
ucasestr="$(toupper "Hello, World")"
echo $ucasestr
HELLO, WORLD

Shell: How to call one shell script from another shell script?

I have two shell scripts, a.sh and b.sh.
How can I call b.sh from within the shell script a.sh?
There are a couple of different ways you can do this:
Make the other script executable with chmod a+x /path/to/file(Nathan Lilienthal's comment), add the #!/bin/bash line (called shebang) at the top, and the path where the file is to the $PATH environment variable. Then you can call it as a normal command;
Or call it with the source command (which is an alias for .), like this:
source /path/to/script
Or use the bash command to execute it, like:
/bin/bash /path/to/script
The first and third approaches execute the script as another process, so variables and functions in the other script will not be accessible.
The second approach executes the script in the first script's process, and pulls in variables and functions from the other script (so they are usable from the calling script).
In the second method, if you are using exit in second script, it will exit the first script as well. Which will not happen in first and third methods.
Check this out.
#!/bin/bash
echo "This script is about to run another script."
sh ./script.sh
echo "This script has just run another script."
There are a couple of ways you can do this. Terminal to execute the script:
#!/bin/bash
SCRIPT_PATH="/path/to/script.sh"
# Here you execute your script
"$SCRIPT_PATH"
# or
. "$SCRIPT_PATH"
# or
source "$SCRIPT_PATH"
# or
bash "$SCRIPT_PATH"
# or
eval '"$SCRIPT_PATH"'
# or
OUTPUT=$("$SCRIPT_PATH")
echo $OUTPUT
# or
OUTPUT=`"$SCRIPT_PATH"`
echo $OUTPUT
# or
("$SCRIPT_PATH")
# or
(exec "$SCRIPT_PATH")
All this is correct for the path with spaces!!!
The answer which I was looking for:
( exec "path/to/script" )
As mentioned, exec replaces the shell without creating a new process. However, we can put it in a subshell, which is done using the parantheses.
EDIT:
Actually ( "path/to/script" ) is enough.
If you have another file in same directory, you can either do:
bash another_script.sh
or
source another_script.sh
or
. another_script.sh
When you use bash instead of source, the script cannot alter environment of the parent script. The . command is POSIX standard while source command is a more readable bash synonym for . (I prefer source over .). If your script resides elsewhere just provide path to that script. Both relative as well as full path should work.
Depends on.
Briefly...
If you want load variables on current console and execute you may use source myshellfile.sh on your code. Example:
#!/bin/bash
set -x
echo "This is an example of run another INTO this session."
source my_lib_of_variables_and_functions.sh
echo "The function internal_function() is defined into my lib."
returned_value=internal_function()
echo $this_is_an_internal_variable
set +x
If you just want to execute a file and the only thing intersting for you is the result, you can do:
#!/bin/bash
set -x
./executing_only.sh
bash i_can_execute_this_way_too.sh
bash or_this_way.sh
set +x
You can use /bin/sh to call or execute another script (via your actual script):
# cat showdate.sh
#!/bin/bash
echo "Date is: `date`"
# cat mainscript.sh
#!/bin/bash
echo "You are login as: `whoami`"
echo "`/bin/sh ./showdate.sh`" # exact path for the script file
The output would be:
# ./mainscript.sh
You are login as: root
Date is: Thu Oct 17 02:56:36 EDT 2013
First you have to include the file you call:
#!/bin/bash
. includes/included_file.sh
then you call your function like this:
#!/bin/bash
my_called_function
Simple source will help you.
For Ex.
#!/bin/bash
echo "My shell_1"
source my_script1.sh
echo "Back in shell_1"
Just add in a line whatever you would have typed in a terminal to execute the script!
e.g.:
#!bin/bash
./myscript.sh &
if the script to be executed is not in same directory, just use the complete path of the script.
e.g.:`/home/user/script-directory/./myscript.sh &
This was what worked for me, this is the content of the main sh script that executes the other one.
#!/bin/bash
source /path/to/other.sh
The top answer suggests adding #!/bin/bash line to the first line of the sub-script being called. But even if you add the shebang, it is much faster* to run a script in a sub-shell and capture the output:
$(source SCRIPT_NAME)
This works when you want to keep running the same interpreter (e.g. from bash to another bash script) and ensures that the shebang line of the sub-script is not executed.
For example:
#!/bin/bash
SUB_SCRIPT=$(mktemp)
echo "#!/bin/bash" > $SUB_SCRIPT
echo 'echo $1' >> $SUB_SCRIPT
chmod +x $SUB_SCRIPT
if [[ $1 == "--source" ]]; then
for X in $(seq 100); do
MODE=$(source $SUB_SCRIPT "source on")
done
else
for X in $(seq 100); do
MODE=$($SUB_SCRIPT "source off")
done
fi
echo $MODE
rm $SUB_SCRIPT
Output:
~ ❯❯❯ time ./test.sh
source off
./test.sh 0.15s user 0.16s system 87% cpu 0.360 total
~ ❯❯❯ time ./test.sh --source
source on
./test.sh --source 0.05s user 0.06s system 95% cpu 0.114 total
* For example when virus or security tools are running on a device it might take an extra 100ms to exec a new process.
pathToShell="/home/praveen/"
chmod a+x $pathToShell"myShell.sh"
sh $pathToShell"myShell.sh"
#!/bin/bash
# Here you define the absolute path of your script
scriptPath="/home/user/pathScript/"
# Name of your script
scriptName="myscript.sh"
# Here you execute your script
$scriptPath/$scriptName
# Result of script execution
result=$?
chmod a+x /path/to/file-to-be-executed
That was the only thing I needed. Once the script to be executed is made executable like this, you (at least in my case) don't need any other extra operation like sh or ./ while you are calling the script.
Thanks to the comment of #Nathan Lilienthal
Assume the new file is "/home/satya/app/app_specific_env" and the file contents are as follows
#!bin/bash
export FAV_NUMBER="2211"
Append this file reference to ~/.bashrc file
source /home/satya/app/app_specific_env
When ever you restart the machine or relogin, try echo $FAV_NUMBER in the terminal. It will output the value.
Just in case if you want to see the effect right away, source ~/.bashrc in the command line.
There are some problems to import functions from other file.
First: You needn't to do this file executable. Better not to do so!
just add
. file
to import all functions. And all of them will be as if they are defined in your file.
Second: You may be define the function with the same name. It will be overwritten. It's bad. You may declare like that
declare -f new_function_name=old_function_name
and only after that do import.
So you may call old function by new name.
Third: You may import only full list of functions defined in file.
If some not needed you may unset them. But if you rewrite your functions after unset they will be lost. But if you set reference to it as described above you may restore after unset with the same name.
Finally In common procedure of import is dangerous and not so simple. Be careful! You may write script to do this more easier and safe.
If you use only part of functions(not all) better split them in different files. Unfortunately this technique not made well in bash. In python for example and some other script languages it's easy and safe. Possible to make partial import only needed functions with its own names. We all want that in next bush versions will be done the same functionality. But now We must write many additional cod so as to do what you want.
Use backticks.
$ ./script-that-consumes-argument.sh `sh script-that-produces-argument.sh`
Then fetch the output of the producer script as an argument on the consumer script.

Resources