太极拳能不能打?学会了少林七十二绝艺,就能打,否则……不能打。拳理与编程,是相通的。白俄女芭蕾舞者的肌肉运用之妙,也是与拳理相通的。
在 Bash 看来,或者在任意一种在 Linux 环境里称得上 Shell 的物种看来,只有有了 ffmpeg、grep、sed、awk 以及一个 X 窗口零碎之类的货色,就能够用不到 50 行代码写出一个不错的截屏工具。当然,假使还有 GIMP 或相似的货色,风味更盛。
工夫的名义
晓得上面这条命令,在我敲了回车键之后,会输入什么吗?
$ date +"%Y-%m-%d %T"
会输入
2021-02-19 23:32:18
若以这样的后果作为截屏所得图像的文件名,是不是很好?反正我是打算这样做,先将工夫里的 :
变成短横:
$ date +"%Y-%m-%d %T" | sed -e "s/ /-/g; s/:/-/g"
工夫在流逝:
2021-02-19-23-34-47
将上述代码整合起来,利用子 Shell,就有了截屏脚本 screenshot 的第一步……师出有名:
#!/bin/bash
NAME=$(date +"%Y-%m-%d %T" | sed -e "s/ /-/g; s/:/-/g")
IMAGE=/tmp/${NAME}.png
echo $IMAGE
为 screenshot 增加可执行权限:
$ chmod +x screenshot
而后将其放到零碎 PATH
变量所指定的目录内,执行这个脚本,就能够失去截屏后果的文件名:
$ screenshot
/tmp/2021-02-19-23-40-11.png
工夫在流逝……
世界有多大?
世界有多大呢……单就截屏而言,世界就是计算机屏幕分辨率那么大。
X 窗口零碎有个工具叫 xrandr,它会通知我世界有多大:
$ xrandr
Screen 0: minimum 8 x 8, current 1600 x 900, maximum 32767 x 32767
LVDS1 connected primary 1600x900+0+0 (normal left inverted right x axis y axis) 310mm x 170mm
1600x900 60.01*+ 59.82 40.00
1400x900 59.96 59.88
1368x768 60.00 59.88 59.85
1280x800 59.81 59.91
1280x720 59.86 60.00 59.74
1024x768 60.00
1024x576 60.00 59.90 59.82
960x540 60.00 59.63 59.82
800x600 60.32 56.25
864x486 60.00 59.92 59.57
800x450 60.00
640x480 59.94
720x405 59.51 60.00 58.99
640x360 59.84 59.32 60.00
VGA1 disconnected (normal left inverted right x axis y axis)
VIRTUAL1 disconnected (normal left inverted right x axis y axis)
然而,我感觉它的废话太多了。它所说的,只有第一句是我想晓得的:
Screen 0: minimum 8 x 8, current 1600 x 900, maximum 32767 x 32767
然而,这一句还是太多了,只有 1600 x 900
是我想晓得的……世界就这么大,那就去掉 xrandr 的那些废话,只有把它们扔到管道里,传给 grep,再传给 sed,
$ xrandr | grep -o "current [0-9]* x [0-9]*" | sed -e 's/current *//g'
1600 x 900
用变量记录这个世界的大小,
SCREEN=$(xrandr | grep -o "current [0-9]* x [0-9]*" | sed -e 's/current *//g')
SCREEN_W=$(echo $SCREEN | sed -e 's/ x [0-9]*//')
SCREEN_H=$(echo $SCREEN | sed -e 's/[0-9]* x //')
总会用得着。
窗口
在屏幕上敞开一个窗口的时候,有时会想起一句古老的诗,人生天地间,忽如远行客。我要写的截屏工具,兴许能留住任一窗口璀璨的霎时,只有我晓得它在哪里。
一个窗口在哪里,是由它的左上角坐标以及它的宽度和高度决定的。在我还没发现 X 窗口零碎的 xwininfo 这个工具之前,我只能思考应用诗歌来实现这样的截屏工具……
xwininfo 说,我晓得你想要晓得的,只有你用鼠标点一下你想点的……
$ xwininfo
xwininfo: Please select the window about which you
would like information by clicking the
mouse in that window.
我用鼠标左键轻易点了一个窗口,xwininfo 把它晓得的所有都通知了我,
xwininfo: Window id: 0x30168e6 "Terminal"
Absolute upper-left X: 479
Absolute upper-left Y: 235
Relative upper-left X: 11
Relative upper-left Y: 39
Width: 642
Height: 434
Depth: 32
Visual: 0x104
Visual Class: TrueColor
Border width: 0
Class: InputOutput
Colormap: 0x3000006 (not installed)
Bit Gravity State: NorthWestGravity
Window Gravity State: NorthWestGravity
Backing Store State: NotUseful
Save Under State: no
Map State: IsViewable
Override Redirect State: no
Corners: +479+235 -479+235 -479-231 +479-231
-geometry 80x24+468+196
我想晓得的是这些:
Absolute upper-left X: 479
Absolute upper-left Y: 235
Relative upper-left X: 11
Relative upper-left Y: 39
Width: 642
Height: 434
我要把这些数字都提炼进去,放到一个数组里:
declare -a WIN_PARAMS
WIN_PARAMS=($(xwininfo | sed -n -e '/^[[:space:]]*Absolute ..*[XY]/p;
/^[[:space:]]*Relative ..*[XY]/p;
/^[[:space:]]*Width:/p;
/^[[:space:]]*Height:/p'| awk'BEGIN{FS=":"}{print $2}'))
如果放心不下,想亲自视察 WIN_PARAMS
,那就遍历一下它:
for ((i = 0; i < ${#WIN_PARAMS[@]}; i++))
do
echo ${WIN_PARAMS[$i]}
done
或
for i in ${WIN_PARAMS[@]}
do
echo $i
done
相框
没什么好说的,都是小学数学:
MARGIN=12
WIN_X=$((${WIN_PARAMS[0]}-${WIN_PARAMS[2]}))
WIN_Y=$((${WIN_PARAMS[1]}-${WIN_PARAMS[3]}))
WIN_W=$((${WIN_PARAMS[4]}+${WIN_PARAMS[2]}+$MARGIN))
WIN_H=$((${WIN_PARAMS[5]}+${WIN_PARAMS[3]}+$MARGIN))
if (($WIN_X < 0)); then WIN_X=0; fi
if (($WIN_Y < 0)); then WIN_Y=0; fi
if (($WIN_X + $WIN_W > $SCREEN_W))
then
WIN_W=$(($SCREEN_W - $WIN_X))
fi
if (($WIN_Y + $WIN_H > $SCREEN_H))
then
WIN_H=$(($SCREEN_H - $WIN_Y))
fi
截屏
不懂 ffmpeg,也没关系啊……这个截屏工具的精华,不在这里。
ffmpeg -video_size ${WIN_W}x${WIN_H} \
-f x11grab -ss 00:00:00 \
-i :0.0+${WIN_X},${WIN_Y} \
-frames:v 1 $IMAGE 2>/dev/null
查看 / 编辑
GIMP 还是挺好用的,尽管在挣钱方面远不如 PhotoShop……
gimp $IMAGE &
附录
残缺的 screenshot 脚本……我抓个图吧!
将这个脚本绑定到 Linux 桌面环境里的某个快捷键,应该不难……