6

When I start a shell, in my case bash and/or zsh, what lines do I need to add to .zshrc and/or .bashrc to check to see if a program is already running, and if the program is not already running, start it?

Example: I am starting a new zsh shell. If top is not already running, I would like to start top and have it take over the shell. If top is already running in another window, I would like the .zshrc to continue loading and give me an open terminal.

6-16 Update:

if ! pgrep -U $USER top >/dev/null; then
exec top
fi

The script above satisfies my request. The -q flag wouldn't work for me because -q is not an option on my system. So I had to pipe the PID to /dev/null. Thank you mveroone, Kusalananda, and Thomas Dickey.

I have some feature creep that is outside of the scope of the original question. Suppose I am on a single user system where I can become root without needing to enter a password. How would I autorun top as root if I needed to?

6-18 Ok, the feature creep issue of passing a command as root is resolved. I'm using this to launch several

Thank you for all of the thoughtful replies.

  • You write, « -q is not an option on my system » What is your system? – roaima Jun 17 '16 at 8:42
  • @roaima Fedora 23 This is the output I receive when I use the -q option: pastebin.com/HXdr4nJX – J363 Jun 17 '16 at 21:36
3
+50

Use pgrep like this:

if ! pgrep -u $USER top >/dev/null 2>&1; then
    exec top
fi

Explanation:

  • The pgrep utility exits with a zero exit code if the command can be found running. Therefore, negate the test.
  • Also restrict it to the current user with -u $USER, so that it doesn't for some reason pick up someone else's processes. $USER is a standard shell variable. If that for some reason isn't defined, try using $LOGNAME instead, and if that fails, use $( id -n -u ) (but at that point you have to ask yourself what's going on, because $LOGNAME is also a standard shell variable).
  • We don't need the output from pgrep, so redirect the standard output and error streams to /dev/null.

If top (or whatever) isn't found to be running, exec it to replace the shell, as per the OP's question.

This should be working in all sh-compatible shells.

EDIT: The pgrep implementation on BSD systems (OS X, OpenBSD, ...) has a -q option that makes it discard any output, but the pgrep on Linux apparently doesn't have this flag. On BSD systems, use this rather than the slightly ugly redirect to /dev/null.

  • Thank you for the thoughtful reply. I added the code you provided to my .zshrc config file and it successfully launches top. However, your proposed solution did not detect if top was already running because every new terminal I opened was taken over by top, even if top was running in another terminal. The issue is that I do not have a -q option in pgrep. By removing the -q flag, the solution works as you intended. However, the PID of top displays on all newly started terminals. Any thoughts about how to prevent the pid from printing when starting a new terminal? – J363 Jun 16 '16 at 22:21
  • 1
    So, your pgrep implementation is different from mine. I'm amending my answer to fit your situation. – Kusalananda Jun 17 '16 at 5:19
  • @Kusalananda maybe it's a bit late, but, are you sure about first point? "The pgrep utility exits with a zero exit code if the command can be found running. Therefore, negate the test." I think you did want to mean non-zero exit code – JFernan Feb 22 at 1:30
  • 1
    @JFernan Yes, I'm sure about that point. A zero exit status signifies "success", i.e. that the process was found. – Kusalananda Feb 22 at 6:12
2

If the program is always started from the shell and you have full control over it (ie. it is not hardcoded inside 3rd party program), then you can use some locking mechanism like lockfile or flock. For example by adding

flock -n /tmp/top -c top

to the .bashrc/.zshrc you will ensure that only first instance of the shell will run the top command (unless you kill it, then another one which will be started).

You may want to create a function in order to be able to start the command also by hand (not only when shell initialize):

mytop () { flock -n /tmp/$0 -c top; }

And just run mytop whenever you want.

1

Agreeing that pgrep is the way to go, there are some pitfalls. You need not use the -q option, since you can redirect the output of pgrep to eliminate that:

pgrep -U $USER top >/dev/null || exec top

The pitfalls lie in the fact that it is not a standard utility, so you have to keep in mind that it may not be portable. Here are a few manpage links, to help:

The USER environment variable is more well-established, but does not appear to be in POSIX's list for the shell (see 2.5.3 Shell Variables). Rather, it notes that USER is one of many variables known to be in use (see note in 8.1 Environment Variable Definition).

This runs top as yourself, of course. If you chose to make it run as root, then you could change the $USER to root and then run top using either sudo (preferred) or su. Neither is in POSIX (see list). The latter (su) is available everywhere on Unix-like systems, but sudo is generally preferred. So you would have

pgrep -U root top >/dev/null || exec sudo top

Unless you have configured sudo to not require a password, you will be prompted for a password when your shell starts top.

0

I would append a snippet like this in my .zshrc (or other) :

if pgrep -u $USER program_name
then
    exec program_name
fi

Caveat : I haven't tested this code. Please don't use it on root or on the only user you have on a server. Make sure you can recover if it fails.

  • 1
    ... grep -q program_name | grep -v grep – Putnik Jun 1 '16 at 19:29
  • 2
    Parsing the output of ps u should be a last resort. At least use the -o option to get less ambiguous output. But even with that there are a lot of false positives, such as grep (which may or may not appear depending on timing). If pgrep is available, use it instead of ps | grep. – Gilles Jun 1 '16 at 21:51
  • Thanks for the input. You're both right. I was also concerned about the use of "exec" which would replace the shell and kill connection if interruped. – mveroone Jun 2 '16 at 9:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.