1

I have been trying to get a way to check whether a script is running or not, because I want to ensure that there aren't any other instances when I run something. So far, I have tried with this:

#! /bin/ksh93

getProcessNum(){
    processNum=$(ps -ef | grep -E "$1" \
        | grep -vE "emacs|vi |ve |grep|ps|tail|tee|runProcess|jlast|tlast|elast" \
        | awk '/'$USER'/ { if($1 == "'$USER'") print $0}' \
        | awk -v myPid=$$ '{if ($3 != myPid) print $0}' \
        | wc -l
    )
}

getProcessNum "myAvoidedScript.k"

if [[ $processNum -gt 1 ]]; then
    echo "There are $processNum instances running. Stopping..."
else
    echo "You can continue"
fi

And I got this:

ThisProcessID=4063450
myUser  4063450  5636606   0 19:06:48 pts/10  0:00 /bin/ksh93 myAvoidedScript.k
myUser  8978492  5964234   0 19:06:48 pts/10  0:00 /bin/ksh93 myAvoidedScript.k
myUser  3277270  9109712 113 11:07:58  pts/5 70:50 /bin/ksh93 myAvoidedScript.k

I thought this way would be enough to do so, but there are some processes that are counted even though they are part of the same script. I noticed when I tried to run this script and it returned a number bigger than 1 even though there was nothing running.

So I said: Maybe if I try to eliminate the duplicates that started at the very exact time, would be enough and I tried this:

#! /bin/ksh93

getProcessNum(){
    processNum=$(ps -ef | grep -E "$1" \
        | grep -vE "emacs|vi |ve |grep|ps|tail|tee|runProcess|jlast|tlast|elast" \
        | awk '/'$USER'/ { if($1 == "'$USER'") print $0}' \
        | awk -v myPid=$$ '{if ($3 != myPid) print $0}' \
        | sort -k 4 | awk '!x[$5]++' | wc -l
    )
}

getProcessNum "myAvoidedScript.k"
if [[ $processNum -gt 1 ]]; then
    echo "There are $processNum instances running. Stopping..."
else
    echo "You can continue"
fi

Sometimes I get the correct result, but some others I get this

The returned PIDs are not always the same as the current PID ($$):

ThisProcessID=4063450
myUser  3177476  5964232   0 19:06:49 pts/10  0:00 /bin/ksh93 myAvoidedScript.k
myUser  3277270  9109712 113 11:07:58  pts/5 70:50 /bin/ksh93 myAvoidedScript.k

When you run this script on the verge of a second, it still brings two instances of this very same script.

ThisProcessID=4063450
myUser  4063450  5636606   0 19:07:48 pts/10  0:00 /bin/ksh93 myAvoidedScript.k
myUser  5177476  5964232   0 19:07:49 pts/10  0:00 /bin/ksh93 myAvoidedScript.k
myUser  3277270  9109712 113 11:07:58  pts/5 70:50 /bin/ksh93 myAvoidedScript.k

Does anyone know how can I ensure that no matter when I run this script it only brings the right processes running on a reliable way?

PS. I know that I could use a flag file but that would mean changing a lot of scripts.

  • You said in a comment that the other instances of the script aren't attached to the current terminal, but are they running as $USER? ps -u $USER -f (or other output options) should show them if they are running as $USER. Please clarify whether other users could run myAvoidedScript.k (and cause an issue). – Jeff Schaller Nov 14 '17 at 16:57
3

I don't have an AIX system to test this on, but you may be seeing children of the running script due to the use of a pipeline whilst doing the ps. You can try to avoid this by running ps on its own and capturing its output into a temporary file, or more simply a variable. You could also simplify the grep, awk, and wc into one awk command. For example,

getProcessNum(){
    procs=$(ps -u "$USER" -f)
    processNum=$(printf '%s\n' "$procs" |
        awk -v myPid="$$" -v name="$1" '
            index($0,"ksh93 " name) != 0 && $2 != myPid {tot++}
            END {print tot}'
    )
}
  • Thanks @meuh I was thinking about putting the output and a variable as you said. I am using ps -ef because the script I'm searching for is not attached to the current terminal so -u will not show it, but let me try your code as a reference to see what I can do. Thanks again. – StrayChild01 Nov 14 '17 at 13:49
  • My original post got edited. If you click on the edited... link you can see the previous version with ps -ef and a different awk line to test for the USER. – meuh Nov 14 '17 at 13:52
2

Does anyone know how can I ensure that no matter when I run this script it only brings the right processes running on a reliable way?

You could ask ps to list the processes owned by the current user -u $USER, and to print out their pids, parent pids, and full command strings (command and arguments), then pass that to awk to do further checking. Awk is given the current script's pid (-v me=$1) and the process name to look for (-v procname=$2) and then is told to look for lines that match three requirements:

  1. where the pid (column 1) is not the current script's pid, and
  2. where the ppid (column 2) is not the current script's pid, and
  3. where the command string matches the given argument

If any lines match all of those two requirements, then awk will print the pid; those pids (if any) are then piped through wc -l to count their instances. You could simply increase a counter, but if you wanted to investigate the pids for other purposes, this leaves you a way to retrieve them.

getProcessNum() {
  ps -u "$USER" -o pid=,ppid=,args= |
  awk -v me="$1" -v procname="$2" \
    '$1 != me && $2 != me && index(substr($0,19,length(procname)), procname) { print $1 }' |
  wc -l
}

The main difference in this one from meuh's is that I've asked ps to print the parent pids, so that we can specifically ignore any children of the current script; another difference is that I've specifically searched the string starting at column 19 (skipping over the pid and ppid) and further limited to the length of the process name, before determining a match with index. I did this in order to prevent as many false positives as I could, such as the admittedly contrived vi ksh93 myAvoidedScript.k.

You'd have to pass the exact incantation of the script, as given by ps -o args, in order to correctly exclude those instances -- for example here, getProcessNum $$ "/bin/ksh93 myAvoidedScript.k". If the script could ever be called with parameters (/bin/ksh93 myAvoidedScrip.k foo), or options (ksh93 -x myAvoidedScript.k) or full paths (/bin/ksh93 /path/to/myAvoidedScript.k) or with different interpreter paths (./ksh93 myAvoidedScript.k) or directly (./myAvoidedScript.k), then this checking would fail.

Unless you can guarantee the precise script invocation, it's going to be difficult to guarantee that only one copy of that script is being executed at a time.

  • Thanks for your thorough answer. The only problem I have with ps -u $USER is that this script will be sometimes invoked by cron as well as by terminal some times. I checked ps -u and it only brings the processes attached to the current terminal, not all of them. – StrayChild01 Nov 14 '17 at 18:36
  • Since IIRC you have access to an AIX machine, does AIX not have an equivalent to Solaris pgrep command? IIRC its ps has advanced features some of which have been copied to procps-ng on Linux. Nothing that can select processes based on advanced criteria like HP/UX ps -C? – Stéphane Chazelas Nov 14 '17 at 18:52
  • @StrayChild01, does cron run it as a different user than $USER? Also, I'm confused that you cannot see your own user's processes on different terminals; when I run ps -u $USER, I see processes from multiple terminals. Simple enough to remove the -u "$USER" portion of the ps string, though. – Jeff Schaller Nov 14 '17 at 18:55
  • @StéphaneChazelas I do, and there's no pgrep; I double-checked, as I realized we were rewriting it. I even checked the Toolbox, but no sign of pgrep or procps. I searched the manpage for ps for "(s)elect(ion)" but came up empty. The closest it gets are some flags that accept lists of: users, ttys, pids, groups, process groups, or workload management classes. – Jeff Schaller Nov 14 '17 at 19:02
  • @JeffSchaller That's correct, when I run ps -u $USER this is what I see: 2140 3408036 pts/10 0:00 ksh – StrayChild01 Nov 14 '17 at 22:25

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.