understanding PATH variable export at the beginning of the bash script - bash

I have fairly often seen that PATH variable is exported at the beginning of the script. For example in /etc/init.d/rc script in Debian Wheezy:
PATH=/sbin:/usr/sbin:/bin:/usr/bin
export PATH
While I understand that this ensures that executables used in the script are started from correct directories, I don't fully understand which shells are affected by this export statement. For example here I start the script named rc(PID 6582; command is "/bin/sh /etc/init.d/rc") in bash(PID 3987):
init(1)-+-acpid(1926)
|-sshd(2139)-+-sshd(2375)---bash(2448)---screen(3393)---screen(3394)-+-bash(3395)---vim(3974)
| | |-bash(3397)---pstree(6584)
| | `-bash(3987)---rc(6582)---sleep(6583)
Am I correct that this PATH export statement in rc script affects only the /bin/sh with PID 6582 because parent shells(bash with PID 3987 in my example) do not inherit variables from children? In addition, am I correct that all the commands executed in script rc are started under the /bin/sh with PID 6582 and thus use this PATH=/sbin:/usr/sbin:/bin:/usr/bin variable? If yes, then hasn't the simple PATH=/sbin:/usr/sbin:/bin:/usr/bin been enough?

The environment variables are inherited by all the processes run from the script. PATH in particular affects the behaviour of the C functions execlp() and execvp(), so all the processes launched by the init.d script that started sshd and their descendants are impacted, but only until the point where one of these descendants changes and exports it.
In particular, bash(2448) most probably changes it, as it is a login shell, to match the system's and the user's config, so all it descendans are impacted by this change.
Then when you run manually the /etc/init.d/rc script by hand, the change is inherited by the sleep command (but that one never tries to run anithing).
If yes, then hasn't the simple PATH=/sbin:/usr/sbin:/bin:/usr/bin been enough?
If you mean just setting the variable instead of also exporting it, it depends on what the rc script runs. If it launches anything that tries to run commands with any of those functions, then no, only after exporting PATH it affects the children.

PATH should already be exported by the parent shell when the script run, so indeed, there is no need.
I can imagine corner cases where the shell which runs your script might not be properly initialized, such as for a startup script running very early in the boot process, but for regular userspace scripts, things should be set up the way you want them already.

Related

How to get the absolute path of a script using its PPID - Bash

I am trying the absolute path of a given PPID from a shell script.
However, the Parent Process may be any type of script (bash/csh/zsh/tsh/Makefile).
The child process is always a bash script and is the only script I have access on to edit.
I have tried:
ps --no-headers -o command $PPID but it only gets the command that invoked the parent process. This isn't what I need because the parent may have caused some cd's inside the script and I won't be able to resolve the relative path in the command to it.
ls -l /proc/$PPID/fd/255 and this was the closest to what I want but this is specific to bash scripts and as I mentioned, I don't have access to know my parent process' script type.
/proc/$PPID/exe returns the binary exe, and I need the script's absolute path that is using this binary.
There isn't such thing as "the" absolute path. There may be several way to access the same file. And some times, none of them (or more than one of them, depends how you look at it) are "the" main name.
I am thinking of hard links here, for example.
Makefile is never the path of a script. It is just a configuration file. That is read by default by the executable. Which is probably /usr/bin/make. To find a path to the Makefile file, you need to read arguments of make. Or to find a Makefile among open files (assuming there is only one, and that it is named Makefile, and that is not certain). And the strategy to guess what is the Makefile of a make process is specific to make. You need another strategy to guess what is the script file executed by a bash process, Another to find what is the python file executed by a python command (which could be a shebang python "executable", a python ../somerealtive/path/somefile.py a python /some/absolute/paht a python -m somemodele, a python -c "import somemod ; somemod.run()" etc.)
Now, since you've mentioned /proc/$PPID/exe, you can get the file "pointed" by this, using
path=$(readlink -f /proc/$PPID/exe)
Again, that is linux solution, more than a bash one. You have no guarantee from bash that the system on which you run this command have a /proc filesystem
I must add that I suspect a XY problem here. What are you trying to do exactly?

How to time child processes in bash?

I have a program that is a compiled binary that calls a whole bunch (~300) of child bash scripts. I would just like a way to time each of those child processes to find out which ones take the longest. The problem comes from the fact that I cannot change the compiled binary (by adding for example time foo.sh >> bar_log.txt). I can change the child scripts, so in theory I could move them to a different location and replace them with scripts that just call the ones from the new location with the time command, but again -- there are 300+ with unique names and whatnot. I was wondering if there was a way to call the original binary and get a log of the times to execute the child processes.
Thanks in advance.
EDIT: I also have limited access to additional programs and cannot download/install new ones. For example, I do not have atop.
The environment variable ENV (for POSIX shells; for bash when not started under the name sh, BASH_ENV) is parsed even by noninteractive shells as the location of a script to run on startup.
Thus, you can create a file that initializes tracing:
if [ -n "$BASH_VERSION" ]; then
# SECONDS is time since the start of this individual script
PS4=':$BASH_SOURCE:$LINENO:$SECONDS+'
else
# note that calling $(date) slows your code substantially
# nothing to do about it without shell extensions, however.
PS4=':${0}:$(date)+'
fi
set -x
...and set both ENV and BASH_ENV to point to that file.

Launch interactive Bash shell in Ruby script, with initial command

I'm working on an interactive Ruby script, which build and packages resources. In the middle of the process, I'd like to drop into an interactive shell, but pre-cd'd into a specific working directory, as well as with an explanatory message (CTRL-D to continue). The interactive bash + given initial command is what's problematic.
Per the answer for doing something like this in Bash, given at https://stackoverflow.com/a/36152028, I've tried
system '/bin/bash', '--init-file', '<(echo "cd ~/src/devops; pwd")'
However, bash runs interactively but completely ignores the '<(echo "cd ~/src/devops; pwd")' section.
Interestingly system '/bin/bash', '--init-file complains if no argument is given, but literally anything runs bash, but with no initial command.
*Note that (--rcfile instead of --init-file) has the same effect.
Change the working directory of the Ruby script first, so that bash inherits the correct working directory.
curr_dir = Dir.pwd
Dir.chdir("#{Dir.home}/src/devops")
system "/bin/bash"
Dir.chdir(curr_dir) # Restore the original working directory if desired
Oh, this is probably far better (you can probably guess how little familiarity I have with Ruby):
system("/bin/bash", :chdir=>"#{Dir.home}/src/devops")

Setting Fish Environment Variable Only Once in Function

I am trying to use fish as my shell. When I login with LightDM, I want to start certain Xsession apps, but only when the shell in invoked at the outset by LightDM.
I have tried this in ~/.config/fish/config.fish:
###################################################################
# Start xsession applications, but only once.
if test -z "$XSESSION_STARTED"
set -xg XSESSION_STARTED 'f'
end
if test "$XSESSION_STARTED" = 'f'
xsession-apps
end
The function xsession-apps then starts all the apps in the background and sets the environment variable at the end like this:
set -xg XSESSION_STARTED "t"
But XSESSION_STARTED does not appear to get set to 't' and causes the xsession-apps function to get called every time, even when I start a new terminal within gnome-term.
What am I missing. Is there a better way to approach this?
even when I start a new terminal within gnome-term.
That is to be expected. Global variables are set within that particular fish. If you start another fish, it won't have it (unless you start it inside that fish, because the variable is exported).
There's a few ways to approach this:
Don't do it in config.fish at all - use the DE's autostart mechanism or at least ~/.xinitrc. This is the best and cleanest approach, and independent of your shell.
Use universal variables - these are stored persistently and shared for all fish sessions on the machine. The issue here is invalidating it - you need to unset the variable again once you logout/reboot, but if your machine crashed that wouldn't happen
Use a flag file on a tmpfs (i.e. in RAM) - this will be automatically invalidated if your machine stops, whatever the cause. You need to setup a tmpfs for it, though.
Here is the code I used in ~/.xsessionrc:
# Apps launched directly by X window managers don't have their environment set
if [ $SHELL = "/usr/bin/fish" ]
then
/usr/bin/fish -c xsession-apps
else
source ~/src/dotfiles/keychain.sh
source ~/src/dotfiles/shell/aliases
source ~/src/dotfiles/shell/env
source ~/src/dotfiles/xsession-apps
eval "$(~/.rbenv/bin/rbenv init -)"
fi
It initializes fish or bash/zsh, depending on what I'm in the mood for at the time. Lately, I'm liking fish. I then defined a fish function called xsession-apps to launch the things I wanted started up in my X session, such as dropbox, hplip, xmobar, etc. I have a similar setup as a bash script, also called xsession-apps that gets sourced if I'm not using fish.
By the way, I use xmonad as my windowing environment.

Creating a startup daemon for a shell script in FreeBSD

I am trying to create a file in rc.d/ that will start up a /bin/sh script that I have written. I am following some examples found here:
http://www.freebsd.org/doc/en/articles/rc-scripting/article.html#rc-flags
#!/bin/sh -x
# PROVIDE: copyfiles
. /etc/rc.subr
name=copyfiles
rcvar=copyfiles_enable
pidfile="/var/run/${name}.pid"
command="/var/etc/copy_dat_files.sh -f /var/etc/copydatafiles.conf"
command_args="&"
load_rc_config $name
run_rc_command "$1"
It seems like I am having a problem with the pidfile. Does my script need to be the one that creates the pid file, or does it automatically get created? I have tried both ways, and whether or not i make my script create a pid file, I get an error that the pid file is not readable.
If my script is supposed to make it, what is the proper way to make the pid file?
Thanks
Look at the existing daemons for example (such as /etc/rc.d/mountd). Then look at the subroutines in /etc/rc.subr -- there is code in there to check the PID-file, but nothing creates it.
In other words, you can declare in the daemon-starting script, what the PID-file is, but creating it is up to the daemon. Speaking of the daemons, you may wish to use the daemon(8) utility, if your daemon is, in fact, a shell script. The utility will take care of the PID-file creation for you. (If the daemon is written in C, you can/should use daemon(3) function.)
BTW, in my own opinion, daemons, when opening up the PID-files for creation, should also lock them (with flock(3) or fcntl(2) or lockf(3)). This way, if an instance crashes (or is killed) without removing the PID-file, the next instance will have no problem determining, the file is stale.
In general, a daemon is supposed to create and clean up its own PID file.
From a shell-script you can give the following command to create it;
echo $$ >/var/run/${name}.pid
Do not forget to remove the file before exiting the script. Write a cleanup() function that does that and let trap call that function when certain signals occur. Also call cleanup just before exiting the script.

Resources