是否有linux命令来确定与给定进程ID关联的窗口ID?

Dav*_*orn 38 linux x11

给定XX的进程iD,我想要列出任何窗口ID,其中_NET_WM_PID = XX.如果可能的话,更好的是最老的仍然活跃的窗口ID.

我对linux很新,但我要做的是创建一个脚本,该脚本将采用命令行,并查看是否已经打开一个属于使用相同命令行调用的进程的窗口.如果是这样,只需将焦点设置到该窗口,否则执行命令行以获得新进程.我的目的是在我的ubuntu桌面中使用它,我将把这个脚本挂钩到我的easystroke鼠标手势命令中,这样,例如,每次我为gmail做手势我都没有得到一个全新的gmail会话,我只是被带到我现有的gmail chrome app窗口.也许有一个更简单的方法来解决所有这些问题,但我还没有找到方法.

在帮助下,我已经弄清楚如何使用pgrep找到命令行的PID以及如何使用wmctrl将焦点设置到窗口句柄,但我仍然坚持从PID到窗口ID.

Pat*_*ick 38

xwininfo和xprop允许检索你想要的东西,但它有点棘手.

xwininfo允许检索所有已知窗口,xprop查询X关于_NET_WM_PID参数的单个窗口ID.

到目前为止,一个hacky方法是:

#!/bin/sh

findpid=$1

known_windows=$(xwininfo -root -children|sed -e 's/^ *//'|grep -E "^0x"|awk '{ print $1 }')

for id in ${known_windows}
do
    xp=$(xprop -id $id _NET_WM_PID)
    if test $? -eq 0; then
        pid=$(xprop -id $id _NET_WM_PID|cut -d'=' -f2|tr -d ' ')

        if test "x${pid}" = x${findpid}
        then
            echo "Windows Id: $id"
        fi
    fi
done
Run Code Online (Sandbox Code Playgroud)

结果:

mycroft:~ $ ./find_windows.sh 1919
Windows Id: 0x1800748
Windows Id: 0x181b221
Windows Id: 0x1803ad5
Windows Id: 0x181f681
Windows Id: 0x181f658
Windows Id: 0x180006d
Windows Id: 0x1800003
Windows Id: 0x1800001
Windows Id: 0x180001e
Run Code Online (Sandbox Code Playgroud)

正如您将看到的,即使您只在屏幕上看到一个窗口,单个进程也可能具有一定数量的已知窗口.

也许你应该得到这些工具来源,以达到你想要的.


PSk*_*cik 27

你也可以用wmctrl查找PID ,事实上,我认为这是一个更好的方法.xwininfo将返回所有类似于 Windows的实体,但您不会在桌面上找到它们.

如果你做man wmctrl ,你会发现wmctrl -l列出了桌面上实际可见的所有窗口(最重要的是)它们的窗口ID标题.-p添加PID,-x将添加窗口类.

正如手册所说(RTFM,对吗?:D),wmctrl也可以搜索其中一些并激活与搜索匹配的窗口.但是,我不知道是什么决定了所有可能的匹配中的哪一个将被返回.另一方面,您可以使用提供的列表函数编写一个包装器,它可以更好地进行搜索,并且可能基于一些其他属性(例如上次访问窗口的时间戳),您可以通过查询提供的获胜来获得例如,id到xprop.

下面的这些代码行返回最近的一个mate-terminal类窗口的实例:

XTIME="_NET_WM_USER_TIME" #a shorter name for xprop query that shoul return timestamps
export TMPDIR=/dev/shm    #save tmp files to memory to make it faster
LST=`mktemp`              #tmp file to store our listing 
wmctrl -lx |  awk -F' ' '{printf("%s\t%s    \t",$1,$3); for(i=5;i<=NF;i++) printf("%s",$i); printf("\n")  }'  > $LST #pretty-print our listing of windows into the tmp file
 #To each line of listing, prepend a timestamp acquired via an xprop call
 #Use awk to find a line whose 3rd column (winclass) matches the window class "mate-terminal.Mate-terminal" and among those that do, find the one whose timestamp is the largest
while read LINE; do ID=`echo "$LINE"|cut -f 1`; TIME=`xprop -id $ID $XTIME`;  TIME="${TIME/* = /}"; echo -e "$TIME\t$LINE" ; done <$LST ) | awk -v s="mate-terminal.Mate-terminal" '$3 == s {if($1>max){max=$1;line=$0};};END{print line}'
rm $LST  #delete tmp file
Run Code Online (Sandbox Code Playgroud)

无论如何,对于你所描述的你正在构建的东西 - 如果我是你,我会找出你想要的命令产生什么类型的窗口,然后我的搜索基于它,而不是基于PID.或者,您可以假设命令CMD可能生成具有包含CMD的类名的窗口.

找到你的线后,你应该使用窗口ID
通过wmctrl激活窗口.

希望这可以帮助.

旁注:我发现xdotool也可以根据类名和窗口标题进行搜索,但速度非常慢.在我的计算机上,这个bash脚本(调用相当多的外部实用程序)的速度是xdotool:P的编译替代品的10倍.

  • 但 xdotool 的一个好处是它不需要“高级”窗口管理器,因此绝对值得一提。 (2认同)

Ric*_*ard 7

您可以使用:

xdotool getwindowfocus getwindowname
Run Code Online (Sandbox Code Playgroud)

(按原样:您不需要用任何东西替换那些好听的名字。)


Adr*_*zak 7

这里有多个很棒的 X11 窗口管理解决方案。

试试 wmctrl。这是一个脚本:

#!/usr/bin/env bash
# 
# File:
#   getwindidbypid
# 
# Description:
#   Get the ID of a window by PID (if the process has a window).
# 
# Usage:
#   getwindidbypid <PID>
# 

while IFS= read line; do
  if [[ "${line}" =~ (0x)([0-9a-z]+)([ ][- ][0-9]+[ ])([0-9]*) ]]; then
    winId="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
    pid="${BASH_REMATCH[4]}"
    if [[ "${pid}" -eq "${1}" ]]; then
      WIND_IDS+=("${winId}")
    fi
  fi
done < <(wmctrl -lp)

if [ "${#WIND_IDS[@]}" -gt 0 ]; then
  echo "${WIND_IDS[@]}"
fi
Run Code Online (Sandbox Code Playgroud)

例子:

user ~ $  getwindidbypid 37248
0x05a00012
Run Code Online (Sandbox Code Playgroud)


F. *_*uri 7

$WINDOWID功能

在其他一些实现的xterm 环境下,您可以找到这个变量:

echo $WINDOWID
58720292
Run Code Online (Sandbox Code Playgroud)

因此对于任何其他pid:

很快通过使用sed

targetpid=12345
sed -zne 's/WINDOWID=//p' /proc/$targetpid/environ
Run Code Online (Sandbox Code Playgroud)

...

xdotool windowactivate $(sed -zne 's/WINDOWID=//p' /proc/$targetpid/environ)
Run Code Online (Sandbox Code Playgroud)

或者在纯 bash函数中:

xdotool windowactivate $(sed -zne 's/WINDOWID=//p' /proc/$targetpid/environ)
Run Code Online (Sandbox Code Playgroud)

然后

getWinFromPid () { 
    local pid=$1 array line
    [ -z "$pid" ] || [ ! -d /proc/$pid ] && return -1
    local -n result=${2:-winIDfromPid[$pid]}
    mapfile -d $'\0' -t array </proc/$pid/environ
    for line in "${array[@]}" ;do
        [ -z "${line%WINDOWID=*}" ] &&
            result=${line#*=} && return
    done
}
Run Code Online (Sandbox Code Playgroud)

对于其他术语,例如gnome-terminal

这很强大,因为我们不需要terminal进程的 pid,而是使用终端的 pid shell。对于样品:

getWinFromPid 123456 myWinId
xdotool windowactivate $myWinId
Run Code Online (Sandbox Code Playgroud)

不要显示想要的 pid!

所以我们必须在流程的层次结构中导航

1.从进程ID获取窗口ID

1a. 当前活动终端窗口

来自活动会话本人:

wmctrl -lp
Run Code Online (Sandbox Code Playgroud)

当您在任何活动终端控制台中键入此内容时,此操作就会起作用。

然后现在,与xdotool

SHWINID=$(xprop  -root | sed -ne 's/^_NET_ACTIVE_WINDOW.*[)].*window id # //p')
Run Code Online (Sandbox Code Playgroud)

您现在可以切换到另一个窗口,将在 12 秒后返回。

1b. 来自任何 shell 的窗口 ID 或子进程 ID

我写了这个小函数:

sleep 12; xdotool windowactivate $SHWINID
Run Code Online (Sandbox Code Playgroud)

然后

getWinFromPid () { 
    local pid=$1 ttypid crtpid wid xprop ttycheck
    [ -z "$pid" ] || [ ! -d /proc/$pid ] && return -1
    local -n result=${2:-winIDfromPid[$pid]}
    read ttycheck < <(ps ho tty $pid)
    ttypid=$ttycheck
    while [ "$ttypid" = "$ttycheck" ]; do
        crtpid=$pid
        read pid ttypid < <(ps ho ppid,tty $pid)
    done
    result=
    while [ -z "$result" ] && read wid; do
        xprop=$(xprop -id $wid)
        [ "$xprop" ] && [ -z "${xprop//*_NET_WM_DESKTOP*}" ] &&
            [ -z "${xprop//*_NET_WM_PID(CARDINAL) = $crtpid*}" ] && result=$wid
    done < <(xwininfo -root -children -all |
       sed -e '1,/children:$/d;s/^ *\(0x[0-9a-fA-F]\+\) .*/\1/p;d')
}
Run Code Online (Sandbox Code Playgroud)

如果没有提交变量名,这将$winIDfromPid使用 pid id 作为索引号填充全局数组:

getWinFromPid <process id> [<variable name>]
Run Code Online (Sandbox Code Playgroud)

注意:这是用xtermmate-terminalkonsole和进行测试的gnome-terminal

Note2:如果你已经wmctrl安装了,你可以替换最后两行函数:

getWinFromPid 1234 winId
echo $winId
0x0100012345

getWinFromPid 1234
echo ${winIDfromPid[1234]}
0x0100012345

declare -p winIDfromPid 
declare -a winIDfromPid=([1234]="0x0100012345")
Run Code Online (Sandbox Code Playgroud)

经过:

    done < <(xwininfo -root -children -all |
       sed -e '1,/children:$/d;s/^ *\(0x[0-9a-fA-F]\+\) .*/\1/p;d')
Run Code Online (Sandbox Code Playgroud)

函数将变得大约快两倍。

2.从窗口ID获取进程运行PID

    done < <(wmctrl -l|sed -ne 's/^ *\(0x[0-9a-fA-F]\+\) .*/\1/p');}
Run Code Online (Sandbox Code Playgroud)

或者没有ID,您将必须用鼠标单击:

ps --ppid $(xprop _NET_WM_PID|sed s/.*=//) ho sid|xargs -I{} -n1 ps --sid {} fw
Run Code Online (Sandbox Code Playgroud)

进入一个函数

winID=0x123456
ps --ppid $(xprop -id $winID _NET_WM_PID|sed s/.*=//) ho sid |
    xargs -I{} -n1 ps --sid {} fw
Run Code Online (Sandbox Code Playgroud)

然后:

ps --ppid $(xprop _NET_WM_PID|sed s/.*=//) ho sid|xargs -I{} -n1 ps --sid {} fw
Run Code Online (Sandbox Code Playgroud)

3. shell窗口列表,带有进程

最后,有一个小功能用于显示带有进程的窗口列表。

psWin() {
    ps --ppid $(xprop ${1+-id} $1 _NET_WM_PID|sed s/.*=//) ho sid |
        xargs -I{} -n1 ps --sid {} fw
}
Run Code Online (Sandbox Code Playgroud)

然后

psWin $SHWINID
  PID TTY      STAT   TIME COMMAND
30529 pts/2   Ss     0:00 bash
19979 pts/2   S+     0:00  \_ bash
19982 pts/2   S+     0:00      \_ xargs -I{} -n1 ps --sid {} fw
19986 pts/2   R+     0:00          \_ ps --sid 30529 fw
Run Code Online (Sandbox Code Playgroud)
shWinList () {
    local pids=() wids=() wtitl=() ttys=() pid ttypid wpid crtpid line title desk ttycheck
    for pid in $(ps axho pid,tty| sed -ne 's/ pts.*//p') ;do     # list pid / tty
        wpid=$pid ttypid=
        read ttycheck < <(ps ho tty $pid)
        ttypid=$ttycheck
        while [ "$ttypid" = "$ttycheck" ]; do
            crtpid=$wpid
            read wpid ttypid < <(ps ho ppid,tty $wpid)
        done
        [ -e /proc/$pid ] && pids[crtpid]+=$pid\  ttys[crtpid]=$ttycheck
    done
    while read wid; do   title= pid= desk=                       # list wid / tty
        while read line; do
            [ "$line" ] && { 
                [ -z "${line%%_NET_WM_PID*}" ] && pid=${line##*= }
                [ -z "${line%%WM_NAME*}" ] &&
                    title=${line#*\"} title=${title%\"*}
                [ -z "${line%%_NET_WM_DESKTOP(*}" ] && desk=${line##*= } ;}
        done < <(xprop -id $wid)
        [ "${pids[pid]}" ] && [ "$title" ] && [ "$desk" ] &&
            wtitl[16#${wid#0x}]=${title} wids[16#${wid#0x}]=${pids[pid]} \
                 ttys[16#${wid#0x}]=${ttys[pid]}
    done < <(xwininfo -root -children -all |
                 sed -ne 's/^ *\(0x[0-9a-fA-F]\+\) .*/\1/p')
    for xwin in ${!wids[@]} ;do  out=                            # merge & print
        printf "  0x%x %-9s %-40s " $xwin "${ttys[$xwin]}" "${wtitl[$xwin]}"
        for pid in ${wids[$xwin]} ;do
            mapfile -d '' cmdl < /proc/$pid/cmdline
            echo -n " $pid[${cmdl[*]}]"
        done ; echo
    done
}
Run Code Online (Sandbox Code Playgroud)