0%

pdftk删除页码脚本

最近把《Linux命令行与shell脚本编程大全》翻了一遍,想演练一下,于是写出了下面这个脚本,用于调用pdftk删除给定页码生成新文档。编写调试过程中,对bash函数传参、位置参数调用等概念有了更深入的理解,特别是shift,用于处理数量未知的位置参数格外好用。

#!/bin/bash
# 对pdftk的简单封装,用于删除指定的页码,以空格分隔,支持形如"5-7"的页码范围。页码输入不必按顺序,类似"12 6-8 1 3"输入是可以正常工作的。

#isPdftkinstalled作用为检查pdftk是否可用,若可用则执行pdftk --version
function isPdftkinstalled {
    echo
    if [ -z $(whereis pdftk | gawk '{print $2}') ]
    then
        echo "pdftk未安装或未加入PATH,请检查。"
        echo "提示:pdftk已加入Deepin官方源,您可以通过 sudo apt install pdftk 简单安装。"
    else
        echo $(pdftk --version)
    fi
    echo
}

#getCouples作用为对输入的页码进行处理,支持输入单页或页码范围,将其转换为数对(获取范围前后页码,以冒号分隔)
function getCouples {
    local couples=''
    local left_end=''
    local right_end=''
    while [ -n "$1" ]
    do      
        arg1=$(echo "$1" | gawk -F"[- ]" '{print $1}')
        arg2=$(echo "$1" | gawk -F"[- ]" '{print $2}')
        left_end=$[ $arg1 - 1 ]
        #对应页码范围情况
        if [ -n "$arg2" ]
        then
            right_end=$[ $arg2 + 1 ]
        else
        #对应单页情况
            right_end=$[ $arg1 + 1 ]
        fi
        couples=$(echo $couples $left_end:$right_end)
        shift
    done
    #数对排序处理,注意sort是针对行的排序,故需要将空格转换成换行
    couples=$(echo $couples | tr " " "\n" | sort -t ':' -k 1 -n)
    couples=$(refineCouples $couples)
    echo $couples
}

#refineCouples作用为合并相邻数对,供getCouples调用
function refineCouples {
    local args=''
    local isNabour=''
    local Num1=$(echo $1 | cut -d ":" -f1)
    local Num2=$(echo $1 | cut -d ":" -f2)
    local Num3=''
    local Num4=''
    shift
    while [ -n "$1" ]
    do
        Num3=$(echo $1 | cut -d ":" -f1)
        Num4=$(echo $1 | cut -d ":" -f2)
        isNabour=$[ $Num2 - $Num3 ]
        if [ $isNabour -eq 1 ]
        then
            Num2=$Num4
        else
            args=$(echo $args $Num1:$Num2)
            Num1=$Num3
            Num2=$Num4
        fi
        shift
    done
    args=$(echo $args $Num1:$Num2)
    echo $args
}

#generateRanges作用为将数对转换为pdftk可用的页码范围
function generateRanges {
    local first_Bit=$(echo $1 | cut -d ":" -f1)
    local Num1=''
    local Num2=''
    local args=''
    while [ -n "$1" ]
    do
        Num1=$(echo $1 | cut -d ":" -f2)
        if [ -n "$2" ]
        then
            Num2=$(echo $2 | cut -d ":" -f1)
            if [ $Num1 -gt $Num2 ]
            then
                Num2=$Num1
            fi
        else
            Num2="end"
        fi
        shift
        args=$(echo $args $Num1-$Num2)
    done

    if [ $first_Bit -gt 0 ]
    then
        args=$(echo 1-$first_Bit $args)
    fi
    echo $args
}

#此函数作用为合并相邻页码范围,在使用refineCouples后已无使用必要,可删除
function refineRanges {
    local args=''
    local isNabour=''
    local Num1=$(echo $1 | cut -d "-" -f1)
    local Num2=$(echo $1 | cut -d "-" -f2)
    local Num3=''
    local Num4=''
    shift
    while [ -n "$1" ]
    do
        Num3=$(echo $1 | cut -d "-" -f1)
        Num4=$(echo $1 | cut -d "-" -f2)
        isNabour=$[ $Num3 - $Num2 ]
        if [ $isNabour -eq 1 ]
        then
            Num2=$Num4
        else
            args=$(echo $args $Num1-$Num2)
            Num1=$Num3
            Num2=$Num4
        fi
        shift
        echo $Num1
    done
    args=$(echo $args $Num1-$Num2)
    echo $args   
}

pdfFile=$1
shift
if [ $# -lt 2 ]
then
    echo "Usage:pdftool [filename] [discard pages]"
    exit
fi

pagesConserved=$(generateRanges $(getCouples $*))
pdftk_command=$(echo pdftk $pdfFile cat $pagesConserved output new_$(basename ${pdfFile}))
#echo $pdftk_command
$pdftk_command

根据大佬指点,使用bash自身的字符串展开替代cut命令:

#!/bin/bash
# 对pdftk的简单封装,用于删除指定的页码,以空格分隔,支持形如"5-7"的页码范围。页码输入不必按顺序,类似"12 6-8 1 3"输入是可以正常工作的。

#isPdftkinstalled作用为检查pdftk是否可用,若可用则执行pdftk --version
function isPdftkinstalled {
    echo
    if [ -z $(whereis pdftk | gawk '{print $2}') ]
    then
        echo "pdftk未安装或未加入PATH,请检查。"
        echo "提示:pdftk已加入Deepin官方源,您可以通过 sudo apt install pdftk 简单安装。"
    else
        echo $(pdftk --version)
    fi
    echo
}

#getCouples作用为对输入的页码进行处理,支持输入单页或页码范围,将其转换为数对(获取范围前后页码,以冒号分隔)
function getCouples {
    local couples=''
    local left_end=''
    local right_end=''
    while [ -n "$1" ]
    do      
        arg1=$(echo "$1" | gawk -F"[- ]" '{print $1}')
        arg2=$(echo "$1" | gawk -F"[- ]" '{print $2}')
        left_end=$[ $arg1 - 1 ]
        #对应页码范围情况
        if [ -n "$arg2" ]
        then
            right_end=$[ $arg2 + 1 ]
        else
        #对应单页情况
            right_end=$[ $arg1 + 1 ]
        fi
        couples=$(echo $couples $left_end:$right_end)
        shift
    done
    #数对排序处理,注意sort是针对行的排序,故需要将空格转换成换行
    couples=$(echo $couples | tr " " "\n" | sort -t ':' -k 1 -n)
    couples=$(refineCouples $couples)
    echo $couples
}

#refineCouples作用为合并相邻数对,供getCouples调用
function refineCouples {
    local args=''
    local isNabour=''
    #local Num1=$(echo $1 | cut -d ":" -f1)
    #local Num2=$(echo $1 | cut -d ":" -f2)
    local Num1=${1%%:*}
    local Num2=${1##*:}
    local Num3=''
    local Num4=''
    shift
    while [ -n "$1" ]
    do
        Num3=${1%%:*}
        Num4=${1##*:}
        isNabour=$[ $Num2 - $Num3 ]
        if [ $isNabour -eq 1 ]
        then
            Num2=$Num4
        else
            args=$(echo $args $Num1:$Num2)
            Num1=$Num3
            Num2=$Num4
        fi
        shift
    done
    args=$(echo $args $Num1:$Num2)
    echo $args
}

#generateRanges作用为将数对转换为pdftk可用的页码范围
function generateRanges {
    local first_Bit=${1%%:*}
    local Num1=''
    local Num2=''
    local args=''
    while [ -n "$1" ]
    do
        Num1=${1##*:}
        if [ -n "$2" ]
        then
            Num2=${2%%:*}
            if [ $Num1 -gt $Num2 ]
            then
                Num2=$Num1
            fi
        else
            Num2="end"
        fi
        shift
        args=$(echo $args $Num1-$Num2)
    done

    if [ $first_Bit -gt 0 ]
    then
        args=$(echo 1-$first_Bit $args)
    fi
    echo $args
}

pdfFile=$1
shift
if [ $# -lt 2 ]
then
    echo "Usage:pdftool [filename] [discard pages]"
    exit
fi

pagesConserved=$(generateRanges $(getCouples $*))
pdftk_command=$(echo pdftk $pdfFile cat $pagesConserved output new_$(basename ${pdfFile}))
#echo $pdftk_command
pdftk_command

可见使用bash本身的字符串替换更加高效灵活,通过#或%的数量可以制定首次匹配或最长匹配,用来截取文件名或后缀很有用。

bash递归函数测试

#!/bin/bash
# using recursion

function factorial {
    if [ $1 -lt 1 ]
    then
        echo 0
    elif [ $1 -eq 1 ]
    then
        echo 1
    else
        local temp1=$1
        local temp2=$(factorial $[ $1 - 1 ])
        local result=$[ $temp1 * $temp2 ]
        echo $result
    fi
}

function sumn {
    if [ $1 -lt 1 ]
    then
        echo 0
    elif [ $1 -eq 1 ]
    then
        echo 1
    else
        local temp1=$1
        local temp2=$(sumn $[ $1 - 1 ])
        local result=$[ $temp1 + $temp2 ]
        echo $result
    fi
}

function fbnq {
    if [ $1 -lt 1 ]
    then
        echo 0
    elif [ $1 -lt 2 ]
    then
        echo 1
    else
        local temp1=$(fbnq $[ $1 - 1 ])
        local temp2=$(fbnq $[ $1 - 2 ])
        local result=$[ $temp1 + $temp2 ]
        echo $result
    fi
}

read -p "Enter value: " value
result=$(factorial $value)
totalsum=$(sumn $value)
fibon=$(fbnq $value)
echo "The factorial of $value is: $result"
echo "The total sum of $value is: $totalsum"
echo "FibonacciRecursive of $value is: $fibon"

递归实现只有2步:构造递归表达式以及设定初值。

bash中简单函数数组传递

#!/bin/bash
# array variable to function test
function testit {
local newarray
newarray="$@"
echo "The new array value is: ${newarray[*]}"
}
myarray=(1 2 3 4 5)
echo "The original array is ${myarray[*]}"
testit ${myarray[*]}

书中使用命令展开进行传值

newarray=($(echo "$@"))

经过试验,这种方式也可以:

newarray=("$@")

从输出来看一致,不过内在是否存在区别?

我自己来解答:

  1. newarray="$@" : newarray不是数组;
  2. newarray=("$@") : newarray是数组(套了圆括号)
  3. newarray=("$*") : newarray不是数组

对于第3点可以回顾一下$@$*的区别。$@中每个元素是独立的,$*只有一个元素。另外无论newarray是否为数组,${newarray[*]}都可以完整输出内容,因为当newarray不是数组的情况下,全部内容都在${newarray[0]}中,从标准输出来看没区别。

对于for而言数组与否没区别,它只会根据当前IFS分割元素。

再次回顾书中的方式:

newarray=($(echo "$@"))

这种方式看似愚笨,实则非常鲁棒。通过命令展开将不确定的输入类型输出成普通字符串,再套括号变成数组,值得品味。

bash中简单的bc计算

#!/bin/bash

var1=3.14
var2=5.2

var3=$(bc << EOF
scale = 4
a1 = $var1 + $var2
a2 = $var1 * $var2
a1 + a2
EOF
)

echo The final result is $var3

结果毫无悬念,24.668。注意这里用到了here document语法,行内重定向的一大用途便是方便在行间传递参数调用程序,通常搭配命令替换使用。另外注意,bc中引用外部变量(bash变量)时需要加$,bc中声明的变量是不需要的。

bash中简单的for循环

#!/bin/bash for_test.sh
sum=0
i=$(echo {1..5})
echo "The content of list:$i"
for var in $i
do
    echo "The current value is:$var"
    sum=$[$sum+$var]
    echo "The sum is: $sum"
done
echo "The final sum is: $sum"

感觉bash更适合编程入门,足够简单,少量知识就可以写出实用脚本实现对系统的自动化管理。许多“现代”语言虽然语法看起来“简单”,但是隐藏了许多细节,初学者到头来只会机械地调用模块,对于实际发生了什么毫无头绪,反而容易迷失方向。

它被大家叫做“咪咪”,今天也一早就守在门口~

拆机图

盒子与遥控器合影

硬件配置

处理器:Amlogic S905M-B,4核1.5GHz
存储:4GB EMMC
内存:1GB DDR3
有线网口:100M
无线网卡(2.4G):RTL8189ETV
TF卡槽:1个
USB接口(2.0):1个microUSB,1个A口
HDMI接口:1个
AV接口:1个(RET键在接口底部)
散热:优秀
电源:12V 1A,5.5mmX2.5mm
蓝牙:无
随机遥控器:优秀(具备学习功能)

线刷方式

准备工具

  • microUSB数据线
  • 运行Windows系统的电脑,推荐使用Win7和USB2.0。
  • 棉签
  • TF卡
  • 下载安装晶晨Burning_Tool(部分固件需要license文件否则显示刷机剩余次数为0)
  • 刷机镜像

    刷机镜像推荐

  • 潜龙固件 20171210_Q5_G2-40F_S905-S905M_android4.42_root_qlzy.rar ,自带root和bootloader,刷机方便,可用于救砖。
  • 乐天固件 Q5_晶晨S905M-安卓4.4.2-支持8189和7601无线-当贝华为logo和动画-多遥控支持线刷包-20201126版.rar,可惜不带root,没法方便自行替换遥控文件。
  • ATV固件 MXQ_PRO_4K_p201_android_TV_by cOOLio_V2.5_MEE.7z。这个是真正的ATV固件,自带root开关,可惜R3300-M 4G的EMMC存储实在捉襟见肘所以不太实用。注意刷入这个固件后盒子会解锁4K@60fps(先前最大4K@30fps),CPU性能测试提升19%,美中不足没法遥控器开机了。

    备份EMMC分区

    刷机前,强烈建议使用Armbian对EMMC分区进行备份,以免后悔。利用testdisk等工具从原厂EMMC镜像中也可以提取出遥控文件。盒子在刷机前无法启动高版本Armbian,推荐使用Armbian_5.44_S9xxx_Ubuntu_bionic_3.14.29_server_20180729.imgArmbian_5.99_Aml-g12_Ubuntu_bionic_default_5.3.0_rtl8189ftv.tar.gz。开机前,用棉签(去掉一头棉布)按住RST键进入Armbian系统,执行ddbr命令备份mmcblk0。

注:Armbian镜像写入TF卡后需要进行配置,早期,中期,后期配置方式不同。Armbian_5.44_S9xxx_Ubuntu_bionic_3.14.29_server_20180729.img将gxbb_p200_1G_100M_RealtekWiFi.dtb复制到TF卡根目录,重命名为dtb.img即可,Armbian_5.99_Aml-g12_Ubuntu_bionic_default_5.3.0_rtl8189ftv.tar.gz修改uEnv.txt文件引用meson-gxbb-p201.dtb,后期Armbian20.10修改/extlinux/extlinux.conf并将u-boot-s905重命名为u-boot.ext。详情参见 https://forum.armbian.com/topic/12162-single-armbian-image-for-rk-aml-aw-aarch64-armv8/?ct=1613786525 ,其他用户自行编译(如恩山论坛)的可能存在差异。

首次刷机

  • 电脑上使用Burning_Tool导入镜像,默认配置不变,点击开始刷机;
  • 在棉签按住RST键的情况下,接通电源后迅速用microUSB数据线连接盒子与电脑,如无意外电脑会先自动安装驱动然后开始刷机进程,刷机进程显示100%后拔电源重启即可,视情况可能需要自行替换遥控文件:

/etc/remote.conf,remote1.conf,remote2.conf

我在原厂遥控文件基础上仅修改添加了menu键,余处未做改动。

刷机后,盒子可以引导Armbian20.10了。

二次刷机

在刷写第三方固件后不能按RST键进入刷机模式的情况下,网上流传的方式基本都是拆机短接强刷,其实没必要,有简单的方法。Armbian启动后,随便往mmcblk0头部写入一些数据,比如:
dd if=/dev/zero of=/dev/mmcblk0/ bs=4M count=30
这样下次开机只有电源灯亮。电脑上点击开始刷机,盒子拔掉电源,使用microUSB数据线连接电脑,会自动安装驱动并开始刷机进程。

Armbian镜像推荐

刚才已经列出了Armbian_5.44_S9xxx_Ubuntu_bionic_3.14.29_server_20180729.imgArmbian_5.99_Aml-g12_Ubuntu_bionic_default_5.3.0_rtl8189ftv.tar.gz。这2个都能驱动WIFI(8189es),但是内核版本比较老。我目前使用的是balbes150发布的Armbian_20.10_Arm-64_focal_current_5.9.0.img.xz,没有集成8189es驱动WIFI无法使用,不过当做headless server无所谓了。CSDN上倒是有RTL8189ES_linux_v5.8.9_35085.20191003.zip,要26块钱下载: https://download.csdn.net/download/rxgzh070207/11978856?utm_source=bbsseo

Coreelec测试

CoreELEC-Amlogic.arm-9.2.6-Generic.img.gz搭配gxbb_p200_1G_100M_RealtekWiFi.dtb可以使用,也能写入EMMC,我还特意制作了meson-ir遥控文件:

R3300M

点击展开查看 ``` # table R3300M, type: NEC # ###################################################### # # 遥控码 Keycode 实体按键 实现功能 # # ###################################################### # # 基本功能区(测试正常) 0xb3dc KEY_POWER #【待机】 开关机 0xb39c KEY_MUTE #【静音】 静音 0xb388 KEY_HOME #【首页】 HOME键 0xb382 KEY_MENU #【本地】 菜单键 0xb3ce KEY_OK #【确定】 确定 0xb3c5 KEY_BACK #【返回】 返回 0xb395 KEY_PLAYPAUSE #【播放暂停】 暂停/播放 0xb3ca KEY_UP #【上】 上 0xb3d2 KEY_DOWN #【下】 下 0xb399 KEY_LEFT #【左】 左 0xb3c1 KEY_RIGHT #【右】 右 0xb380 KEY_VOLUMEUP #【音量+】 音量增大 0xb381 KEY_VOLUMEDOWN #【音量-】 音量减小 0xb3dd KEY_PAGEUP #【上页】 向上翻页 0xb38c KEY_PAGEDOWN #【下页】 向下翻页 0xb385 KEY_CHANNELUP #【频道+】 向上调台 0xb386 KEY_CHANNELDOWN #【频道-】 向下调台 # 数字按键区(测试正常) 0xb387 KEY_0 #【0】 0 0xb392 KEY_1 #【1】 1 0xb393 KEY_2 #【2】 2 0xb3cc KEY_3 #【3】 3 0xb38e KEY_4 #【4】 4 0xb38f KEY_5 #【5】 5 0xb3c8 KEY_6 #【6】 6 0xb38a KEY_7 #【7】 7 0xb38b KEY_8 #【8】 8 0xb3c4 KEY_9 #【9】 9 # 快捷功能区(测试正常) 0xb3cd KEY_Red #【直播】 Kodi TV PVR(电视) 0xb391 KEY_Green #【回看】 Kodi Videos(视频) 0xb383 KEY_Yellow #【点播】 Kodi Music (音乐) 0xb3c3 KEY_BLUE #【信息】 Kodi Pictures(图片) # 其他按键定义 0xb398 KEY_CONTEXT_MENU #【声道】 显示详细信息 0xb38d KEY_SUBTITLE #【设置】 字幕 0xb3d6 KEY_STOP #【交换】 停止播放 0xb3da KEY_REWIND #【*】 快退 0xb3d0 KEY_FASTFORWARD #【#】 快进 0x51806e KEY_INFO #【TV/IPTV】 显示信息 ```

rc_maps.cfg

点击展开查看 ``` # Keymaps table # # This table creates an association between a keycode file and a kernel # driver. It can be used to automatically override a keycode definition. # # # Format: # driver - name of the driver provided via uevent - use * for any driver # table - RC keymap table, provided via uevent - use * for any table # file - file name. If directory is not specified, it is first looked up # in /storage/.config/rc_keymaps, then /usr/lib/udev/rc_keymaps # # For example: # # driver table file # # gpio-rc-recv rc-streamzap streamzap # gpio-rc-recv * justboom # * rc-rc6-mce rc6_mce_new # * * hauppauge_new meson-ir * R3300M ```

Openwrt测试

测试了恩山Flippy为N1最后发布的49+镜像,配合meson-gbxx-p201.dtb启动没问题,但是写入emmc后无法启动,原因不明。

单位的流浪猫,去年夏天带2只小猫过来的,在一楼值班室安然过冬,大家平时会投喂。今年大概会生出一窝橘猫……

/usr/share/www中发现debug.html页面,访问http://192.168.62.1/debug.html :

可用命令

webportal_user_status
pppstatus
dhcpcstatus
dhcpstalist
dhcpdetect
wlanstatus
wlanscan
wlankickoff
sysinfo
diskinfo
sysdebug
syscap
hwinfo
delayreboot
sta_stats
syslice
upnp_nat
if_stat
arp
route
reboot
save
mode
restore_factory
net_speed_test
wandetect
dpi
user_stat
if_rate_info
firmware_upgrade
wifi_schedule
data_stats
pppoe_pass_hack
clear_reset_flag

sysinfo

<?xml version="1.0" encoding="UTF-8"?><return>    <ITEM index="1" cmd="sysinfo" uptime="5:17:13" memory="22835200/62578688" cpu_usage="2" average_load="0.12/0.13/0.14" firmware_version="AP902P07V1.6.0Build798_TU" sn="BN212GH600045AA" sys_name="" sys_description="uRouter series wireless AP. Support 802.11b/g/n. 300Mbps, 2T2R." sys_model="uRouter mini SJBY" ap_vendor="BHU" uplink_interface_mac="84:82:f4:38:bd:3b" hardware_version="Z05" ip="0.0.0.0" netmask="0.0.0.0" gateway="0.0.0.0" firmware_name="kochab" longitude="" latitude="" cpu_type="MIPS 24KEc V5.5" mem_type="DDR2" mem_size="61112" flash_size="16384" sys_net_id="" primary_dns="114.114.114.114" second_dns="0.0.0.0" location="" systime="13:17:13" saved_config="" build_info="2017-11-03-09:13 Revision: 798" config_model_ver="V3" config_mode="basic" work_mode="router-ap" dev_mac="84:82:f4:38:bd:38" bmc_status="" status="done"/></return>

ifrate

<?xml version="1.0" encoding="UTF-8"?><return>    <ITEM index="1" cmd="ifrate" status="done">        <ifrate_sub>            <SUB interface="wlan3" tx_pkts="490" rx_pkts="367" tx_bytes="38872" rx_bytes="56368" tx_rate="0" rx_rate="0"/>            <SUB interface="wlan2" tx_pkts="0" rx_pkts="0" tx_bytes="0" rx_bytes="0" tx_rate="0" rx_rate="0"/>            <SUB interface="wlan1" tx_pkts="0" rx_pkts="0" tx_bytes="0" rx_bytes="0" tx_rate="0" rx_rate="0"/>            <SUB interface="wlan0" tx_pkts="485" rx_pkts="161" tx_bytes="75992" rx_bytes="50581" tx_rate="0" rx_rate="0"/>            <SUB interface="eth1" tx_pkts="2023" rx_pkts="0" tx_bytes="686136" rx_bytes="0" tx_rate="0" rx_rate="0"/>            <SUB interface="eth0" tx_pkts="44410" rx_pkts="49873" tx_bytes="34125041" rx_bytes="18727781" tx_rate="0" rx_rate="0"/>        </ifrate_sub>    </ITEM></return>