给定XX的进程iD,我想要列出任何窗口ID,其中_NET_WM_PID = XX。如果可能的话,更好的是最老的仍然活跃的窗口ID。
我对linux很新,但我要做的是创建一个脚本,该脚本将采用命令行,并查看是否已经打开一个属于使用同一命令行调用的进程的窗口。如果是这样,只需将焦点设置到该窗口,否则执行命令行以获得新进程。我的目的是在我的ubuntu桌面中使用它,我将把这个脚本挂钩到我的easystroke鼠标手势命令中,这样,例如,每次我为gmail做手势我都没有得到一个全新的gmail会话,我只是被带到我现有的gmail chrome app窗口。也许有一个更简单的方法可以解决所有这些,但我还没有找到它的方式。
在帮助下,我已经弄清楚如何使用pgrep找到命令行的PID以及如何使用wmctrl将焦点设置为窗口句柄,但我仍然坚持从PID到窗口ID。
答案 0 :(得分:36)
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
结果:
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
正如您将看到的,即使您在屏幕上只看到一个窗口,单个进程也可能有一定数量的已知窗口。
也许你应该获得这些工具来源,以便制作你想要的东西。
答案 1 :(得分:24)
你也可以用 wmctrl 查找PID,事实上,我认为这是一种更好的方法。 xwininfo 会返回各种看似是Windows的实体,但您无法在桌面上找到它们。
如果你做 man wmctrl ,你会发现 wmctrl -l </ strong>列出了桌面上实际可见的所有窗口(最重要的是) window ids 和 titles 。 -p 添加 PID , -x 将添加窗口类。
正如手册所说(RTFM,对吗?:D),wmctrl也可以搜索其中的一些并激活与搜索匹配的窗口。但是,我不知道是什么决定了所有可能的匹配中的哪一个将被返回。另一方面,您可以使用提供的列表函数编写一个包装器,该包装器可以更好地进行搜索,并且可能基于一些其他属性(例如上次访问窗口的时间戳),您可以通过查询提供的<例如,em> win 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
无论如何,对于你所描述的你正在构建的东西 - 如果我是你,我会找出你想要的命令生成什么类型的窗口,然后根据它进行搜索,而不是基于PID。或者,您可以假设命令CMD可能生成具有包含CMD的类名的窗口。
找到你的线后,你应该使用窗口id
通过wmctrl激活窗口。
希望这有帮助。
旁注:我发现xdotool也可以根据类名和窗口标题进行搜索,但非常慢。在我的计算机上,这个bash脚本(调用相当多的外部实用程序)的速度是xdotool:P的编译替代品的10倍。
答案 2 :(得分:3)
Here是几个X11窗口管理解决方案(包括此问题之一)。
Xwininfo和xprop是获取所有窗口ID的好工具,但不是用于获取与PID关联的主窗口的ID的最简单工具(如果可以使用它们)。要获取主窗口的ID,请按以下方式使用wmctrl:
#!/usr/bin/env bash
# getwindidbypid
#
# 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
示例:
user ~ $ getwindidbypid 37248
0x05a00012
如果wmctrl找到多个主窗口,此解决方案将打印多个窗口ID。要仅返回第一个,只需将[@]
更改为[0]
中的echo "${WIND_IDS[@]}"
。
答案 3 :(得分:2)
您可以使用:
xdotool getwindowfocus getwindowname
(原样:你不需要用任何东西替换那些听起来不错的名字。)
答案 4 :(得分:2)
$WINDOWID
功能在xterm
环境下,您可以找到以下变量:
echo $WINDOWID
58720292
因此对于任何 other pid:
使用sed
很快:
targetpid=12345
sed -zne 's/WINDOWID=//p' /proc/$targetpid/environ
...
xdotool windowactivate $(sed -zne 's/WINDOWID=//p' /proc/$targetpid/environ)
或在纯bash 函数中:
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
}
然后
getWinFromPid 123456 myWinId
xdotool windowactivate $myWinId
gnome-terminal
:这很强大,因为我们不希望terminal
进程的pid,而需要使用终端的shell
的pid。例如:
wmctrl -lp
不显示通缉的小伙子们!
所以我们必须在流程的层次结构中导航
本人在活动会话中
SHWINID=$(xprop -root | sed -ne 's/^_NET_ACTIVE_WINDOW.*[)].*window id # //p')
在任何 active 终端控制台中键入此命令时,便会起作用。
然后使用xdotool
:
sleep 12; xdotool windowactivate $SHWINID
您现在可以切换到另一个窗口,它将在12秒后返回。
我写了这个小函数:
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')
}
然后
getWinFromPid <process id> [<variable name>]
如果未提交变量名,则将使用pid id作为索引号填充全局数组$winIDfromPid
:
getWinFromPid 1234 winId
echo $winId
0x0100012345
getWinFromPid 1234
echo ${winIDfromPid[1234]}
0x0100012345
declare -p winIDfromPid
declare -a winIDfromPid=([1234]="0x0100012345")
Nota:已通过xterm
,mate-terminal
,konsole
和gnome-terminal
进行过测试。
Nota2:如果已经安装了wmctrl
,则可以替换最后两行功能:
done < <(xwininfo -root -children -all |
sed -e '1,/children:$/d;s/^ *\(0x[0-9a-fA-F]\+\) .*/\1/p;d')
作者:
done < <(wmctrl -l|sed -ne 's/^ *\(0x[0-9a-fA-F]\+\) .*/\1/p');}
功能将变为大约2倍。
winID=0x123456
ps --ppid $(xprop -id $winID _NET_WM_PID|sed s/.*=//) ho sid |
xargs -I{} -n1 ps --sid {} fw
或没有 ID 的情况下,您将必须用鼠标单击:
ps --ppid $(xprop _NET_WM_PID|sed s/.*=//) ho sid|xargs -I{} -n1 ps --sid {} fw
进入功能
psWin() {
ps --ppid $(xprop ${1+-id} $1 _NET_WM_PID|sed s/.*=//) ho sid |
xargs -I{} -n1 ps --sid {} fw
}
然后:
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
最后,有一个小功能可以显示带有进程的窗口列表。
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
}
然后
shWinList
0xa600024 pts/42 user@hostloc: /tmp 14817[bash]
0x1c00024 pts/3 user@hostloc: ~ 29349[watch ps --tty pts/3 fw] 31989[bash]
0x4600024 pts/16 user@remote: ~ 14441[ssh user@remote]
0x6000024 pts/43 user@hostloc: ~ 5728[bash]
答案 5 :(得分:0)
注意:我提供的第一个答案使用cmctrl将pids与Windows关联,以执行着眼于现有进程的任务。有时wmctrl无法正常运行,并希望重新启动。事实证明,使用进程名称本身更加有用,简化和可靠。因此,此版本使用进程名称,而不考虑pid。
我使用openbox和键盘。我使用以下在Debian中可用的bash来根据进程名称返回,启动和在现有窗口之间旋转:
#!/usr/bin/bash
#
# focus
# [-c|--processCount COUNT] # maximum number of processes to automatically start at once,
# [-m|--mainTitle MAIN_TITLE] # extended regular expression to match a main window in search so that associated windows will not be counted as a separate instance of the same process. Example: The window in which thunderbird creates a new email is not the main thunderbird window which can be separately closed.
# [-p|--processName PROCESS_NAME] # extended regular expression to match the process name which is need COMMAND spawns a process of a different name. Example: flashpeak-slimjet launches a program called slimjet.
# [-w|--wantedTitle WANTED_TITLE] # further reduces matches according to WANTED_TITLE. This is needed when EVAL opens a specific file.
# [-v|--verbose] # use to see window information which must be matched, what has been matched etcn.
# COMMAND # used as the PROCESS_NAME if not explicitly stated.
# [COMMAND_ARGUMENTS] # EVAL (with COMMAND) which is assumed to create a newly focused window.
#
# Application switcher which moves to next window applicable to COMMAND or executes COMMAND.
# Rotate to next window belonging to PROCESS_NAME.
# Another instance via EVAL if at last matched window and COUNT allows.
#
# If no window declarations contain regular expressions PROCESS_NAME or COMMAND, then COMMAND
# If the active window does not match PROCESS_NAME or COMMAND, then activate the first match.
# If the active window matches PROCESS_NAME or COMMAND, then goto the next match.
# If at list end, and COUNT allows, then execute COMMAND again, otherwise wrap back to the first matching window.
#
# bug: COUNT may not distinguish between a 'main' window and its spawns which may inhibit expected COMMAND after wrapping only spawned windows. This bug is to be resolved via the introduction of -m which identifies a 'main' window.
gBase=$(basename "$0")
gDir=$(dirname "$0")
unset aWantsMainWindow
. "$gDir"/argumentDeclare. # declare ARGUMENT
while :; do
. "$gDir"/argumentNextOrBreak. # ready ARGUMENT
if OptionDidReplyIntegerOrIs '-c' '--processCount'; then
[[ -z "${REPLY+x}" ]] && REPLY="$1" && shift
COUNT=$REPLY
elif OptionDidReplyOrIs '-p' '--processName'; then
[[ -z "${REPLY+x}" ]] && REPLY="$1" && shift
PROCESS_NAME=$REPLY
elif OptionDidReplyOrIs '-m' '--mainTitle'; then
[[ -z "${REPLY+x}" ]] && REPLY="$1" && shift
MAIN_TITLE=$REPLY
elif OptionDidReplyOrIs '-w' '--wantedTitle'; then
[[ -z "${REPLY+x}" ]] && REPLY="$1" && shift
WANTED_TITLE=$REPLY
elif OptionIsFlag '-v' '--verbose'; then
aVerbose=-
else
>&2 "!!! $gBase: Unknown option ${ARGUMENT} !!!"
exit 1
fi
done
: ${COMMAND:="$1"} # used for description matching
EVAL="$@" # used for evaluation
# previous wmctrl/pid usage falls into problems which clear after reboot
# xwininfo entire tree which contains many undesired windows
# | grep grandchild depth (or deeper) assumed to be children of the window manager
# | grep exclude label lines
# | grep exclude no name -- leaving -- 0xid (main_name): ("name" "name") dimensions...
# | sed -- leaving -- 0xid "main_name": ("name" "name")
zInfos=$(xwininfo -tree -root \
| grep -E '^ ' \
| grep -E '^[[:blank:]]+0x[[:xdigit:]]+' \
| grep -v '(has no name): ()' \
| sed -E 's/^[[:blank:]]*(0x[[:xdigit:]]+ ".*": \(".*" ".*"\)).*/\1/')
[[ -n "$aVerbose" ]] \
&& printf "%d Available windows:\n%s${zInfos:+\n}" \
"$(wc -l < <(printf "%s${zInfos:+\n}" "$zInfos"))" \
"$zInfos"
zInfosMatch=$(grep -E "0x[[:xdigit:]]+ \".*\": \((\".*\" )?\"${PROCESS_NAME:-$COMMAND}\"( \".*\")?\)" <<< "$zInfos")
[[ -n "$WANTED_TITLE" ]] \
&& zInfosMatch=$(grep -E '0x[[:xdigit:]]+ \"$WANTED_TITLE\"): \(".*" ".*"\)' <<< "$zInfosMatch")
[[ -n "$aVerbose" ]] \
&& printf "%d Matching windows:\n%s${zInfosMatch:+\n}" \
"$(wc -l < <(printf "%s${zInfosMatch:+\n}" "$zInfosMatch"))" \
"$zInfosMatch"
zIdFocus=$(printf '0x%x\n' "$(xdotool getwindowfocus)")
[[ -n "$aVerbose" ]] \
&& printf "Currently focused id: %s\n" "$zIdFocus"
zIdsMatch=$(cut -d' ' -f1 <<< "$zInfosMatch")
if [[ -z "$aWantsMainWindow" ]]; then
# attempt to roll to next process window
zFirstIndexMatch=$(grep "$zIdFocus" -Fnxm1 <<< "$zIdsMatch" | cut -d: -f1)
if [[ -n "$zFirstIndexMatch" ]]; then
[[ "$zFirstIndexMatch" -le "$(wc -l <<< "$zIdsMatch")" ]] \
&& zNextIndexMatch=$(( zFirstIndexMatch + 1 )) \
|| unset zNextIndex
else
zIds=$(cut -d' ' -f1 <<< "$zInfos")
zNextIndex=$(( 1 + $(grep "$zIdFocus" -Fnxm1 <<< "$zIds" | cut -d: -f1) ))
while IFS= read -r zNextId || [[ -n "$zNextId" ]]; do
zNextIndexMatch=$(grep "$zNextId" -Fnxm1 <<< "$zIdsMatch" | cut -d: -f1)
[[ -n "$zNextIndexMatch" ]] && break
zNextIndex=$(( zNextIndex + 1 ))
done < <(tail -n+"$zNextIndex" <<< "$zIds")
fi
# find next matching window (if previous match and available)
[[ -n "$zNextIndexMatch" ]] \
&& zNextIdMatch=$(sed "$zNextIndexMatch!d" <<< "$zIdsMatch")
# raise first matching window (if no previous match and available)
[[ -z "$zNextIndexMatch$zFirstIndexMatch" ]] \
&& zNextIdMatch=$(head -n1 <<< "$zIdsMatch")
if [[ -n "$zNextIdMatch" ]]; then
[[ -n "$aVerbose" ]] \
&& printf 'Next matching id: %s\n' "$zNextIdMatch"
wmctrl -iR "$zNextIdMatch"
exit
fi
fi
# consider executing EVAL
[[ -z "$MAIN_TITLE" ]] \
&& zInfosMain="$zInfosMatch" \
|| zInfosMain=$(grep -E '0x[[:xdigit:]]+ \"$MAIN_TITLE\"): \(".*" ".*"\)' <<< "$zInfosMatch")
zInfosMainCount=$(wc -l < <(printf "%s${zInfosMain:+\n}" "$zInfosMain"))
[[ -n "$aVerbose" ]] \
&& printf "%d Main Windows: %s${zInfosMain:+\n}" \
"$zInfosMainCount" \
"$zInfosMain"
if [[ "${COUNT:-1}" -gt "$zInfosMainCount" ]]; then
[[ -n "$aVerbose" ]] \
&& printf 'Opening new main window via: %s\n' "$EVAL"
eval $EVAL & # ampersand to deal with processes that don't just end
disown -a # disown to deal with processes that don't let me end
exit
fi
# raise first matching window
zNextIdMatch=$(head -n1 <<< "$zIdsMatch")
if [[ -z "$zNextIdMatch" ]]; then
>&2 printf "!!! $aBase: Did not locate first matching window or attempt to execute: %s !!!\n" "$EVAL"
exit 1
else
[[ -n "$aVerbose" ]] \
&& printf 'Wrapping to first matching id: %s\n' "$zNextIdMatch"
wmctrl -iR "$zNextIdMatch"
exit
fi
注意:此代码需要软件包wmctrl。
示例用法:
关注qpaeq 焦点-c2 tilda 焦点-c2 -m'。*-Slimjet'-p slimjet flashpeak-slimjet focus -m“。*-Mozilla Thunderbird”雷鸟 focus -m'。*-Mozilla Firefox'-p Firefox-esr firefox-esr