1

I'm trying to obtain the processID of pcmanfm like this:

pgrep -f "pcmanfm"

When pcmanfm is not running, the command above returns nothing (as I expect).

However, when I run the command from python, it returns a process ID even when pcmanfm is not running:

processID = os.system('pgrep -f "pcmanfm"')

Furthermore, if you run the command above multiple times at a python3 prompt, it returns a different processID each time. All the while, pcmanfm has been closed prior to these commands.

>>> processID = os.system('pgrep -f "pcmanfm"')
17412
>>> processID = os.system('pgrep -f "pcmanfm"')
17414
>>> processID = os.system('pgrep -f "pcmanfm"')
17416

This is really messing up my ability to launch pcmanfm if it isn't currently running. My script thinks it is running when it isn't.

Why is this happening?

I'm actually encountering this issue in an Autokey script that I've attempted to write based on this video I watched. Here's my current script:

processID = system.exec_command('pgrep -f "pcmanfm" | head -1',True)
dialog.info_dialog("info",processID)

if (processID):
    cmd = "wmctrl -lp | grep " + processID + " | awk '{print $1}'"
    windowID = system.exec_command(cmd,True)
    # dialog.info_dialog("info",windowID)
    cmd = "wmctrl -iR " + windowID
    #dialog.info_dialog("info",cmd)
    system.exec_command(cmd,False)
else:
    #os.system("pcmanfm /home/user/Downloads")
    cmd = "/usr/bin/pcmanfm /home/user/Downloads"
    system.exec_command(cmd,False)

The problem is, I keep getting processIDs even when pcmanfm isn't running. The script properly focuses pcmanfm if it is running, but it won't launch it if it isn't.

Update: I finally got this script to work by taking out -f and replacing it with -nx (from @they 's advice). Also, I added some exception handling to ignore autokey exceptions caused by empty output that's expected. Additionally, I converted it to a (more flexible) function so that it will service a wider variety of commands/applications:

import re
def focusOrLaunch(launchCommand):
    appName = re.findall('[^\s/]+(?=\s|$)',launchCommand)[0]
    processID = None
    try:
        processID = system.exec_command('pgrep -nx "' + appName + '"',True)
    except Exception as e:
        #dialog.info_dialog("ERROR",str(e))
        pass

    #dialog.info_dialog("info",processID)
    if (processID):
        cmd = "wmctrl -lp | grep " + processID + " | awk '{print $1}'"
        windowID = system.exec_command(cmd,True)
        # dialog.info_dialog("info",windowID)
        cmd = "wmctrl -iR " + windowID
        #dialog.info_dialog("info",cmd)
        system.exec_command(cmd,False)
    else:
        system.exec_command(launchCommand,False)
    
cmd = "/usr/bin/pcmanfm ~/Downloads"
focusOrLaunch(cmd)
0

2 Answers 2

4

Proposed solution:

Remove the -f option from your pgrep command.


Explanation:

You probably get the process ID of the shell that is executed to run your command. A new shell process with a new PID will be created for every system.exec_command.

Run e.g. sh -c 'pgrep -af nonexistent' and check the output. You will probably get something like

11300 sh -c pgrep -af nonexistent

With an existing command I also get a line for the shell

sh -c 'pgrep -af sshd'
695 /usr/sbin/sshd -D
11207 sshd: pi [priv]
11224 sshd: pi@pts/0
11331 sshd: [accepted]
11343 sh -c pgrep -af sshd

Depending on the PID values, your head command might extract the PID of a process you are looking for or the PID of the shell process.

With option -f you explicitly tell pgrep to search the whole command line instead of the process name only. This way it will find the string in the shell's command line argument.

Without -f you won't get the shell process.

$ sh -c 'pgrep -a sshd'
695 /usr/sbin/sshd -D
11207 sshd: pi [priv]
11224 sshd: pi@pts/0
11364 sshd: [accepted]
5
  • 1
    ... which means dropping the -f option would solve the issue.
    – they
    Jan 18 at 16:28
  • Hmm, without the -f, autokey errors out on this line alone processID = system.exec_command('pgrep "pcmanfm" | head -1',True) Jan 18 at 16:47
  • So, it turns out that system.exec_command throws an exception when the command's output is an empty line. @they Thank you for your comment. @Bodo Thanks for the elaboration. I've updated my question to provide the script that ultimately worked in Autokey. Jan 18 at 17:47
  • 2
    @LonnieBest You should not use pgrep with head in a pipeline. Instead, use pgrep with -o or -n to get the single PID corresponding to the oldest or most recently started process that matches the expression. Also consider using -x so that you don't accidentally match process names that contains your expression as a sub-string.
    – they
    Jan 18 at 17:57
  • @they Wise suggestions. Thank you. I updated my question to reflect your advice in the final script. Jan 18 at 19:12
2

Here's my take on focusing a program's window if it's running or launching the program if it's not. I used Firefox in the example, but I'm hoping it would work the same way for your pcmanfm if you made the appropriate edits to remove Firefox from the example code and replace it with the equivalent PCManFM information.

Note that I used the subprocess.Popen function because it allows you to execute other code and/or interact with the process with the subprocess.communicate function while the process is running. If that isn't needed, you can use the subprocess.run function instead.

output = system.exec_command("wmctrl -l", getOutput=True)
if "Firefox" in output:
    window.activate("Firefox",switchDesktop=True)
else:
    subprocess.Popen(["firefox"])
4
  • The issue with pcmanfm is that it doesn't indicate its application name on the title bar of its window. Instead, it only shows the folder name of where it currently is in the file system. Therefore, pcmanfm cannot be found in the output of wmctrl -l. This is why I have endured the trouble of finding the processID of each open window. With it, I can discover the command that launched each window and search for pcmanfm in those commands. Your solution will work for most applications, but for applications like pcmanfm (which doesn't showcase its name on the title bar), you need more. Jan 22 at 12:30
  • What do you get in the third column of the output of the wmctrl -lx command while PCManFM is running? If I do that command while Firefox is running, I get Navigator.Firefox in that column. If you get some useful (i.e.: unique) output in that column, this can be done by window class instead of by using the title bar. Jan 23 at 11:38
  • As I mentioned, it only shows the folder name of where I'm at in pcmanfm, which changes upon navigation to other folders. Window class may be a reliable alternative, but the final code shown at the bottom of my question, is working perfectly for every application I've tried so far (including pcmanfm). Jan 24 at 0:13
  • 1
    The problem is that, in Wayland, windows are managed by the programs that want them and not by the X server and compositor. As a result, the only actual "awareness" of them is by the programs that initiate them. You might be able to trick the system, though, at least as far as wmctrl is concerned. What happens if you do pcmanfm --class=foo and then run the wmctrl -lx command? Does it see the foo class? If so, there's some existing AutoKey syntax that works with classes. Jan 25 at 3:53

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.