Making Bash modular - linux

I have a bunch of Bash scripts and they each make use of the following:
BIN_CHATTR="/usr/bin/chattr"
BIN_CHKCONFIG="/sbin/chkconfig";
BIN_WGET="/usr/bin/wget";
BIN_TOUCH="/bin/touch";
BIN_TAR="/bin/tar";
BIN_CHMOD="/bin/chmod";
BIN_CHOWN="/bin/chown";
BIN_ECHO="/bin/echo";
BIN_GUNZIP="/usr/bin/gunzip";
BIN_PATCH="/usr/bin/patch";
BIN_FIND="/usr/bin/find";
BIN_RM="/bin/rm";
BIN_USERDEL="/usr/sbin/userdel";
BIN_GROUPDEL="/usr/sbin/groupdel";
BIN_MOUNT="/bin/mount";
Is there a way I could just wget a Bash script with global settings like that and then include them in the script I want to run?

Yes, you can put all those variables in a file like "settings.sh" and then do this in your scripts:
source settings.sh

You can keep your variables in a shell script and then source that file:
source /path/to/variables.sh
You should actually use . which in bash is the same thing as source but will offer better portability:
. /path/to/variables.sh

Yes you can. Just add your variables and functions to a file, make it executable and "execute" it at the top of any script that needs to access them. Here's an example:
$ pwd
/Users/joe/shell
$ cat lib.sh
#!/bin/bash
BIN_WGET="/usr/bin/wget"
BIN_MOUNT="/bin/mount"
function test() {
echo "This is a test"
}
$ cat script.sh
#!/bin/bash
. /Users/joe/shell/lib.sh
echo "wget=$BIN_WGET"
test
$ ./script.sh
wget=/usr/bin/wget
This is a test

are you looking for the source command?
mylib.sh:
#!/bin/bash
JAIL_ROOT=/www/httpd
is_root(){
[ $(id -u) -eq 0 ] && return $TRUE || return $FALSE
}
test.sh
#!/bin/bash
# Load the mylib.sh using source comamnd
source mylib.sh
echo "JAIL_ROOT is set to $JAIL_ROOT"
# Invoke the is_root() and show message to user
is_root && echo "You are logged in as root." || echo "You are not logged in as root."
btw - use rsync -a to mirror scripts with +x flag.

Related

Bash: Creating a shell variable in a bash script that I can access from command line

I have very little experience working with bash. With that being said I need to create a bash script that takes your current directory path and saves it to a shell variable. I then need to be able to type "echo $shellvariable" and have that output the directory that I saved to that variable in the bash script. This is what I have so far.
#!/bin/bash
mypath=$(pwd)
cd $1
echo $mypath
exec bash
now when I go to command line and type "echo $mypath" it outputs nothing.
You can just run source <file_with_your_vars>, this will load your variables in yours script or command line session.
> cat source_vars.sh
my_var="value_of_my_var"
> echo $my_var
> source source_vars.sh
> echo $my_var
value_of_my_var
You have to export the variable for it to exist in the newly-execed shell:
#!/bin/bash
export mypath=$(pwd)
cd $1
echo $mypath
exec bash
Hello
'env -i' gives control what vars a shell/programm get...
#!/bin/bash
mypath=$(pwd)
cd $1
echo $mypath
env -i mypath=${mypath} exec bash
...i.e. with minimal environment.

Create a script that adds lines of code to .bashrc then reloads the terminal

I am trying to create a shell script that once run, adds a line of code to the end of .bashrc then reloads the terminal. This is the code i have written in my install.sh
function addbashaliases() {
echo 'if [ -f ~/aliases/.bash_aliases ]; then
. ~/aliases/.bash_aliases
fi' >> ~/.bashrc;
source ~/.bashrc
}
Nothing is happening. How should I write the code so that it runs and adds the text to the .bashrc file?
For the sake of clarity I prefer to append the information on the .bashrc file using the cat command instead of the echo. However, this should work also using your echo command.
This said you should make sure that:
The script calls the addbashaliases function
The ~/aliases/.bash_aliases file exists (I would expect to have something more similar to ~/.aliases/.bash_aliases)
You can check that the script has correctly run by checking the content of the ~/.bashrc file and printing some environment variable set on the .bash_aliases file after the source command.
#!/bin/bash
function addbashaliases() {
# Add source bash_aliases on .bashrc
cat >> ~/.bashrc << EOT
if [ -f ~/aliases/.bash_aliases ]; then
. ~/aliases/.bash_aliases
fi
EOT
# Reload current environment
source ~/.bashrc
}
# Execute the function
addbashaliases
I am just correcting your script. As per your logic it should be like below.
function addbashaliases() {
if [ -f ~/aliases/.bash_aliases ]; then
output=$(. ~/aliases/.bash_aliases)
echo $output >> ~/.bashrc
fi
source ~/.bashrc
}

Sourcing files in shell script vs sourcing on command line

I have the problem that my shell script is not acting exactly the same as my manual typing into a console. I am attempting to find and source some setup files in a shell script as follows:
#!/bin/bash
TURTLE_SHELL=bash
# source setup.sh from same directory as this file
_TURTLE_SETUP_DIR=$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)
. "$_TURTLE_SETUP_DIR/turtle_setup.sh"
This bash file calls a .sh file:
#!/bin/env sh
_TURTLE_ROS_SETUP_DIR=$_TURTLE_SETUP_DIR/../devel
if [ -z "$TURTLE_SHELL" ]; then
TURTLE_SHELL=sh
fi
if [ -d "$PX4_FIRMWARE_DIR/integrationtests" ]; then
if [ -f "$PX4_FIRMWARE_DIR/integrationtests/setup_gazebo_ros.bash" ]; then
. "$PX4_FIRMWARE_DIR/integrationtests/setup_gazebo_ros.bash" "$PX4_FIRMWARE_DIR"
fi
fi
if [ "$TURTLE_SHELL" = "bash" ]; then
if [ -f "$_TURTLE_ROS_SETUP_DIR/setup.bash" ]; then
source $_TURTLE_ROS_SETUP_DIR/setup.bash
fi
else
if [ "$TURTLE_SHELL" = "sh" ]; then
if [ -f "$_TURTLE_ROS_SETUP_DIR/setup.sh" ]; then
source $_TURTLE_ROS_SETUP_DIR/setup.sh
fi
fi
fi
The line in question is:
. "$PX4_FIRMWARE_DIR/integrationtests/setup_gazebo_ros.bash" "$PX4_FIRMWARE_DIR"
I have made sure that this code is actually running and that my environment variables are correct. If I run this command on the command line everything works well. However, the same is not true when the file is sourced via shell script. Why is this? Is there something different about the environment of a shell script that is different from a command line. Also, how can I fix this problem?
Edit:
I am sourcing either the .bash or the .sh scale, depending upon which shell I am using.
Edit 2:
I am sourcing this script. Thus, everything is run in my default bash terminal, and it is all run within the same terminal and not a terminal spawned from a child process. Why is the script not sourcing setup_gazebo_ros.bash within the current shell?
It's the same reason why you source the env script and not run it. When you run the script it runs in a new shell and the variables are not transferred back to the parent shell.
To illustrate
$ cat << ! > foo.sh
> export foo='FOO'
> !
$ chmod +x foo.sh
$ ./foo.sh
$ echo $foo
$ source ./foo.sh
$ echo $foo
FOO

Linux script: Reinterpret environment variable

I am creating a Bash script which reads some other environment variables:
echo "Exporting configuration variables..."
while IFS="=" read -r k v; do
key=$k
value=$v
if [[ ${#key} > 0 && ${#value} > 0 ]]; then
export $key=$value
fi
done < $HOME/myfile
and have the variable:
$a=$b/c/d/e
and want to call $a as in:
cp myOtherFile $a
The result for the destination folder for the copy is "$b/c/d/e", and an error is shown:
"$b/c/d/e" : No such file or directory
because it is interpreted literally as a folder path.
Can this path be reinterpreted before being used in the cp command?
You need eval to do this :
$ var=foo
$ x=var
$ eval $x=another_value
$ echo $var
another_value
I recommend you this doc before using eval : http://mywiki.wooledge.org/BashFAQ/048
And a safer approach is to use declare instead of eval:
declare "$x=another_value"
Thanks to chepner 2 for the latest.
It sounds like you want $HOME/myfile to support Bash notations, such as parameter-expansion. I think the best way to do that is to modify $HOME/myfile to be, in essence, a Bash script:
export a=$b/c/d/e
and use the source builtin to run it as part of the current Bash script:
source $HOME/myfile
... commands ...
cp myOtherFile "$a"
... commands ...
try this
cp myOtherFile `echo $a`

Equivalent of %~dp0 (retrieving source file name) in sh

I'm converting some Windows batch files to Unix scripts using sh. I have problems because some behavior is dependent on the %~dp0 macro available in batch files.
Is there any sh equivalent to this? Any way to obtain the directory where the executing script lives?
The problem (for you) with $0 is that it is set to whatever command line was use to invoke the script, not the location of the script itself. This can make it difficult to get the full path of the directory containing the script which is what you get from %~dp0 in a Windows batch file.
For example, consider the following script, dollar.sh:
#!/bin/bash
echo $0
If you'd run it you'll get the following output:
# ./dollar.sh
./dollar.sh
# /tmp/dollar.sh
/tmp/dollar.sh
So to get the fully qualified directory name of a script I do the following:
cd `dirname $0`
SCRIPTDIR=`pwd`
cd -
This works as follows:
cd to the directory of the script, using either the relative or absolute path from the command line.
Gets the absolute path of this directory and stores it in SCRIPTDIR.
Goes back to the previous working directory using "cd -".
Yes, you can! It's in the arguments. :)
look at
${0}
combining that with
{$var%Pattern}
Remove from $var the shortest part of $Pattern that matches the back end of $var.
what you want is just
${0%/*}
I recommend the Advanced Bash Scripting Guide
(that is also where the above information is from).
Especiall the part on Converting DOS Batch Files to Shell Scripts
might be useful for you. :)
If I have misunderstood you, you may have to combine that with the output of "pwd". Since it only contains the path the script was called with!
Try the following script:
#!/bin/bash
called_path=${0%/*}
stripped=${called_path#[^/]*}
real_path=`pwd`$stripped
echo "called path: $called_path"
echo "stripped: $stripped"
echo "pwd: `pwd`"
echo "real path: $real_path
This needs some work though.
I recommend using Dave Webb's approach unless that is impossible.
In bash under linux you can get the full path to the command with:
readlink /proc/$$/fd/255
and to get the directory:
dir=$(dirname $(readlink /proc/$$/fd/255))
It's ugly, but I have yet to find another way.
I was trying to find the path for a script that was being sourced from another script. And that was my problem, when sourcing the text just gets copied into the calling script, so $0 always returns information about the calling script.
I found a workaround, that only works in bash, $BASH_SOURCE always has the info about the script in which it is referred to. Even if the script is sourced it is correctly resolved to the original (sourced) script.
The correct answer is this one:
How do I determine the location of my script? I want to read some config files from the same place.
It is important to realize that in the general case, this problem has no solution. Any approach you might have heard of, and any approach that will be detailed below, has flaws and will only work in specific cases. First and foremost, try to avoid the problem entirely by not depending on the location of your script!
Before we dive into solutions, let's clear up some misunderstandings. It is important to understand that:
Your script does not actually have a location! Wherever the bytes end up coming from, there is no "one canonical path" for it. Never.
$0 is NOT the answer to your problem. If you think it is, you can either stop reading and write more bugs, or you can accept this and read on.
...
Try this:
${0%/*}
This should work for bash shell:
dir=$(dirname $(readlink -m $BASH_SOURCE))
Test script:
#!/bin/bash
echo $(dirname $(readlink -m $BASH_SOURCE))
Run test:
$ ./somedir/test.sh
/tmp/somedir
$ source ./somedir/test.sh
/tmp/somedir
$ bash ./somedir/test.sh
/tmp/somedir
$ . ./somedir/test.sh
/tmp/somedir
This is a script can get the shell file real path when executed or sourced.
Tested in bash, zsh, ksh, dash.
BTW: you shall clean the verbose code by yourself.
#!/usr/bin/env bash
echo "---------------- GET SELF PATH ----------------"
echo "NOW \$(pwd) >>> $(pwd)"
ORIGINAL_PWD_GETSELFPATHVAR=$(pwd)
echo "NOW \$0 >>> $0"
echo "NOW \$_ >>> $_"
echo "NOW \${0##*/} >>> ${0##*/}"
if test -n "$BASH"; then
echo "RUNNING IN BASH..."
SH_FILE_RUN_PATH_GETSELFPATHVAR=${BASH_SOURCE[0]}
elif test -n "$ZSH_NAME"; then
echo "RUNNING IN ZSH..."
SH_FILE_RUN_PATH_GETSELFPATHVAR=${(%):-%x}
elif test -n "$KSH_VERSION"; then
echo "RUNNING IN KSH..."
SH_FILE_RUN_PATH_GETSELFPATHVAR=${.sh.file}
else
echo "RUNNING IN DASH OR OTHERS ELSE..."
SH_FILE_RUN_PATH_GETSELFPATHVAR=$(lsof -p $$ -Fn0 | tr -d '\0' | grep "${0##*/}" | tail -1 | sed 's/^[^\/]*//g')
fi
echo "EXECUTING FILE PATH: $SH_FILE_RUN_PATH_GETSELFPATHVAR"
cd "$(dirname "$SH_FILE_RUN_PATH_GETSELFPATHVAR")" || return 1
SH_FILE_RUN_BASENAME_GETSELFPATHVAR=$(basename "$SH_FILE_RUN_PATH_GETSELFPATHVAR")
# Iterate down a (possible) chain of symlinks as lsof of macOS doesn't have -f option.
while [ -L "$SH_FILE_RUN_BASENAME_GETSELFPATHVAR" ]; do
SH_FILE_REAL_PATH_GETSELFPATHVAR=$(readlink "$SH_FILE_RUN_BASENAME_GETSELFPATHVAR")
cd "$(dirname "$SH_FILE_REAL_PATH_GETSELFPATHVAR")" || return 1
SH_FILE_RUN_BASENAME_GETSELFPATHVAR=$(basename "$SH_FILE_REAL_PATH_GETSELFPATHVAR")
done
# Compute the canonicalized name by finding the physical path
# for the directory we're in and appending the target file.
SH_SELF_PATH_DIR_RESULT=$(pwd -P)
SH_FILE_REAL_PATH_GETSELFPATHVAR=$SH_SELF_PATH_DIR_RESULT/$SH_FILE_RUN_BASENAME_GETSELFPATHVAR
echo "EXECUTING REAL PATH: $SH_FILE_REAL_PATH_GETSELFPATHVAR"
echo "EXECUTING FILE DIR: $SH_SELF_PATH_DIR_RESULT"
cd "$ORIGINAL_PWD_GETSELFPATHVAR" || return 1
unset ORIGINAL_PWD_GETSELFPATHVAR
unset SH_FILE_RUN_PATH_GETSELFPATHVAR
unset SH_FILE_RUN_BASENAME_GETSELFPATHVAR
unset SH_FILE_REAL_PATH_GETSELFPATHVAR
echo "---------------- GET SELF PATH ----------------"
# USE $SH_SELF_PATH_DIR_RESULT BEBLOW
I have tried $0 before, namely:
dirname $0
and it just returns "." even when the script is being sourced by another script:
. ../somedir/somescript.sh

Resources