乐趣区

关于bash:用-Bash-脚本写一个截屏工具

太极拳能不能打?学会了少林七十二绝艺,就能打,否则……不能打。拳理与编程,是相通的。白俄女芭蕾舞者的肌肉运用之妙,也是与拳理相通的。

在 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 桌面环境里的某个快捷键,应该不难……

退出移动版