Shell脚本:(delayexec)Cygwin下调用schtasks创建Windows任务计划,实现延迟或定时执行某一命令
脚本用法:
/v/bin/delayexec
创建Windows任务计划,延迟或定时执行Cygwin下某命令.
注:本脚本创建的任务计划仅执行一次,不会重复调用,如果需要周期性反复执行的命令,请手动创建任务计划或使用`schtasks`命令来创建;
Usage:
delayexec delaytime-Seconds|time-clock(时:分:秒) command argument1 argument2 ...
Example1:
delayexec 3600 tplink
- 延迟3600秒(1小时)执行tplink
Example2:
delayexec 05:30 recordlive-bilibili 7200
- 今天或次日5点半(取决于现在时刻早于还是晚于指定的时间)执行recordlive-bilibili录制视频两小时
Example3:
delayexec ++07:30 killpm tplink
- 两天后的7点半退出tplink进程,+号指定延迟的天数,+可叠加使用,如+++代表当前日期后延3天
代码如下:
#!/bin/bash
#Windows计划任务cron脚本,通过任务计划调用Cygwin执行某命令
## Example:
## 1. delayexec 3600 tplink #延迟3600秒(1小时)运行tplink
## 2. delayexec 05:30 tplink #定时5点半运行tplink,自动识别日期,当前时刻早于指定时间,则当天执行,当前时刻晚于指定时间,则次日执行
## 3. delayexec ++07:30 tplink #两天后的七点半运行命令tplink,此模式用以显式指定日期,一个+号代表延期一天,可以重复叠加,+代表1天后,+++代表3天后
## 4. delayexec '12-31 15:30' killpm tplink #指定今年12月31号下午三点半退出tplink进程(缺省年份自动设定为今年)
## 5. delayexec '2025-12-24 15:30' killpm tplink #指定2025年12月24号下午三点半退出tplink进程(其中年月日分隔符也可省略)
## -----------------------
## 执行原始CMD命令示例(不使用Cygwin的PATH环境变量),注意需要转义$符号:
## delayexec 60 PATH="\$ORIGINAL_PATH" cmd /c start cmd /k ping www.baidu.com #显示DOS窗口ping命令
## delayexec 60 PATH="\$ORIGINAL_PATH" cmd /c start cmd /k netstat -an -p TCP #显示DOS窗口netstat命令
## delayexec 60 PATH="\$ORIGINAL_PATH" cmd /k netstat -an -p TCP #隐藏执行,不显示DOS窗口
## 注:不支持同时运行多个CMD命令,如有需要请独立编写bat脚本:如以下delay命令,第二个参数ipconfig不会在CMD窗口中显示(实际命令已经在父级Cygwin窗口执行了)
## delayexec 60 PATH="\$ORIGINAL_PATH" cmd /c start cmd /k ping www.baidu.com '&' ipconfig /all
SCRIPTPATH=$(realpath $0)
display_usage() {
echo -e "$SCRIPTPATH\n"
echo -e "\t创建Windows任务计划,延迟或定时执行Cygwin下某命令."
echo -e "\t注:本脚本创建的任务计划仅执行一次,不会重复调用,如果需要周期性反复执行的命令,请手动创建任务计划或使用\`schtasks\`命令来创建;"
echo -e "\nUsage:\n\tdelayexec delaytime-Seconds|time-clock(时:分:秒) command argument1 argument2 ..."
echo -e "Example1:\n\tdelayexec 3600 tplink"
echo -e "\t- 延迟3600秒(1小时)执行tplink"
echo -e "Example2:\n\tdelayexec 05:30 recordlive-bilibili 7200"
echo -e "\t- 今天或次日5点半(取决于现在时刻早于还是晚于指定的时间)执行recordlive-bilibili录制视频两小时"
echo -e "Example3:\n\tdelayexec ++07:30 killpm tplink"
echo -e "\t- 两天后的7点半退出tplink进程,+号指定延迟的天数,+可叠加使用,如+++代表当前日期后延3天"
echo -e "--------\n"
echo -e "高级用法:\n\t可直接指定完整的日期和时间(年份可省略,省略年份时,自动补齐为当前年份):"
echo -e "\t- 格式一:“[年-]月-日 时:分[:秒]”\t eg:【2022-09-18 15:33:12】 或 【09-18 15:33】"
echo -e "\t- 格式二:“[年]月日 时:分[:秒]”\t\t eg:【20220918 15:33:12】 或 【0918 15:33】"
echo -e "\x20Example4:\n\tdelayexec '2022-12-24 15:30' killpm tplink"
echo -e "\t- 指定2022年12月24日下午三点半运行命令退出tplink进程;"
echo -e "\x20Example5:\n\tdelayexec '20221224 15:30' killpm tplink"
echo -e "\t- 同上,缩减了日期中间的短横线分隔符,2022年12月24日下午三点半退出tplink进程;"
echo -e "\x20Example6:\n\tdelayexec '12-31 15:30' killpm tplink"
echo -e "\t- 指定今年12月31号下午三点半退出tplink进程(缺省年份自动设定为今年);"
echo -e "\x20Example7:\n\tdelayexec '1231 15:30' killpm tplink"
echo -e "\t- 同上,缩减了日期分隔符,今年12月31号下午三点半退出tplink进程;"
echo -e "\n\t- 注:在年月日没有分隔符的情况下,推荐月日最好为4位数,尽管程序对三位数月日进行了部分适配(不带年份时),但月份数位数为单时请手动前面补零,否则 \`date\` 命令无法正确处理。"
echo -e "--------\n"
echo -e "执行原始CMD命令示例(不使用Cygwin的PATH环境变量),注意需要转义\$符号:"
echo -e "\nExample8:\n\tdelayexec 60 PATH=\"\\\$ORIGINAL_PATH\" cmd /c start cmd /k ping www.baidu.com #显示DOS窗口ping命令"
echo -e "\tdelayexec 60 PATH=\"\\\$ORIGINAL_PATH\" cmd /c netstat -an -p TCP #隐藏执行,不显示DOS窗口"
}
# if less than two arguments supplied, display usage
if [ $# -lt 1 ]
then
display_usage
exit 1
fi
# check whether user had supplied -h or --help . If yes display usage
if [[ ( $* == "--help") || $* == "-h" ]]
then
display_usage
exit 0
fi
getDelayExecTime() {
# 参数$1传递需要延迟执行任务计划的时间,单位为秒
#local updateTime=$(date -d "1970-01-01 UTC ${1} seconds +3 minutes" +'%Y-%m-%dT%H:%M:%S')
local updateTime=$(date -d "+${1} Seconds" +'%Y-%m-%dT%H:%M:%S')
echo "$updateTime"
}
getClockExecTime() {
# 参数$1传递执行任务计划的准确时刻,示例:14:25
case $1 in
0)
local executeTime=$(date -d "+1day $2" +'%Y-%m-%dT%H:%M:%S')
;;
*)
local executeTime=$(date -d "$2" +'%Y-%m-%dT%H:%M:%S')
;;
esac
echo "$executeTime"
}
#说明:以下为什么要用两个模板文件,为什么要先导出XML再更改,主要考虑是为了更好的兼容性,比如更换电脑后,用户Sid改变导致任务计划不能按预期效果执行,此故障到底会不会发生未经严格测试,仅此以防万一
schTasksXML="/v/reg-tpl/update-delayexec-schtasks.xml"
schTasksXMLInitFile="/v/reg-tpl/update-delayexec-schtasks-init.xml"
#默认延迟时间:3600秒
delayTimeSec=3600
#执行命令的时间:
executeTime=-1
#脚本 $1 供外部传递延迟时间,参数类型正整数,单位为秒
if [ ! -z "$1" ]
then
expr "$1" + 0 &>/dev/null
if [ $? -eq 0 ]
then
delayTimeSec=$1
shift
elif [[ "$1" =~ ^(\++)?[0-9]{1,2}:[0-9]{2}(:[0-9]{2})?$ || "$1" =~ ^([0-9]{4}-)?([0-9]{1,2}-[0-9]{2} )?[0-9]{1,2}:[0-9]{2}(:[0-9]{2})?$ || "$1" =~ ^([0-9]{4})?([0-9]{1,2}[0-9]{2} )?[0-9]{1,2}:[0-9]{2}(:[0-9]{2})?$ ]]
then
delayDays=0
#处理时间参数传递+号的情况,+号可重复叠加使用,如:++00:30 表示两天后的夜里12点半。
while read -n 1 char ; do
if [ "$char" != "+" ]
then
break
fi
let delayDays+=1
done <<<"$1"
if [ $delayDays -eq 0 ]
then
fixDateTime="$1"
fixDate=$(echo "$fixDateTime"|awk -F ' ' 'NF<2{exit}{print $1}')
#echo "fixDate:$fixDate"
if [[ "$fixDateTime" =~ "-" ]] && [ ${#fixDate} -gt 0 -a ${#fixDate} -lt 9 ]
then
fixDateTime="$(date +'%Y-')${fixDateTime}"
elif [[ "$fixDate" =~ ^(0[1-9]|[1][0-2]{1})[0-3][0-9]$ ]] #匹配四位数月日,eg:0921,1128,1205...
then
fixDateTime="$(date +'%Y')${fixDateTime}"
elif [[ "$fixDate" =~ ^[1-9][0-3][0-9]$ ]] #匹配月份位数为单数的情况,eg:810,922...
then
fixDateTime="$(date +'%Y0')${fixDateTime}"
elif [ ${#fixDate} -ne 0 -a ${#fixDate} -le 4 ]
then
echo "日期时间参数:“${fixDateTime}” 解析失败,因日期 “${fixDate}” 不在有效范围,脚本退出..."
exit 1
fi
[ $(date +'%s') -ge $(date -d "$fixDateTime" +'%s' 2>/dev/null) ] &>/dev/null
increDay=$?
[ $increDay -eq 2 ] && {
echo "参数无效:“${fixDateTime}” 指定的日期时间无效,脚本退出..."
exit 1
}
[ ! -z "$fixDate" ] && increDay=1
executeTime=$(getClockExecTime $increDay "$fixDateTime")
#[ $(date +'%s') -ge $(date -d "$1" +'%s') ]
#executeTime=$(getClockExecTime $? "$1")
delayTimeSec=-1
else
timeExpression="+ ${delayDays} days ${1//+/}"
executeTime=$(getClockExecTime 1 "$timeExpression")
delayTimeSec=-1
fi
shift
fi
fi
#预备执行的命令:
prepareExecCommand="$*"
[ -z "$prepareExecCommand" ] && {
echo "待执行命令为空,脚本退出..."
exit 0
}
#XML使用的计划任务名称:
schtasksTitle="${*//\//_}"
schtasksTitle="${schtasksTitle//&/_a_}"
#schtasksTitle="$1"
[ "$executeTime" = "-1" ] && executeTime=$(getDelayExecTime $delayTimeSec)
[ $delayTimeSec -ne -1 ] && echo "延迟 $delayTimeSec 秒后执行命令:${prepareExecCommand}"
echo "执行时间:$(date -d "${executeTime}" +'%Y-%m-%d %H:%M:%S')"
#exit 0 #Test:中断
#注意计划任务的任务名/TN字符的长度限制:ERROR: Value for '/TN' option cannot be more than 237 character(s).
schtasksURI='\\Cygwin自用\\定时执行命令:'"${schtasksTitle}"
#导出最新的定时执行命令的任务计划模板:
gsudo SCHTASKS /Query /TN "${schtasksURI//\\\\/\\}" /XML ONE >$(cygpath -aw "$schTasksXML") 2>/dev/null
#如果XML文件大小为零,即计划任务管理器没有该任务,则从预先准备的最原始的XML文件读取任务计划信息:
if [ "$(stat -c '%s' $schTasksXML)" == "0" ]
then
#echo "指定的任务计划不存在!"
#exit 0
schTasksXML=$schTasksXMLInitFile
fi
#替换任务唤醒时间为指定时间,并去掉禁用任务的标识(如果任务被禁用的话):
awk -i inplace '/<URI>/{getline;print "<URI>'"${schtasksURI}"'</URI>"}/StartBoundary/{getline;print "\t<StartBoundary>'${executeTime}'</StartBoundary>"}/<Enabled>false/{getline;}/<Arguments>/{getline;print "\t<Arguments>'"${prepareExecCommand}"'</Arguments>"}{print}' $schTasksXML
#替换user sid为当前电脑当前用户的user sid,否则无法导入
userSidInfo=$(PATH="$ORIGINAL_PATH" cmd /c whoami /user)
pcName=$(echo "$userSidInfo"|tail -n 1|awk -F '[ \\\\]' '{printf $1}'|tr '[:lower:]' '[:upper:]')
userSid=$(echo "$userSidInfo"|tail -n 1|awk '{printf $2}')
awk -i inplace '/<UserId>/{getline;print "<UserId>'"${userSid}"'</UserId>"};/<Author>/{getline;print "<Author>"'"${pcName}"'"</Author>"}{print}' $schTasksXML
#cat $schTasksXML
#导入XML文件到计划任务:
gsudo SCHTASKS /Create /F /XML $(cygpath -aw "$schTasksXML") /TN "${schtasksURI//\\\\/\\}"
echo -e "Done..."
帮助信息截图:
运行结果截图:
附件:XML文件 update-delayexec-schtasks-init.xml 之模板代码如下:
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2021-09-29T14:45:34.6728472</Date>
<Author>DESKTOP-ENTJJOK\Administrator</Author>
<URI>\Cygwin自用\定时执行命令:tplink</URI>
</RegistrationInfo>
<Principals>
<Principal id="Author">
<UserId>S-1-5-21-1929780610-3846960160-3848736843-500</UserId>
<LogonType>InteractiveToken</LogonType>
</Principal>
</Principals>
<Settings>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
</Settings>
<Triggers>
<TimeTrigger>
<StartBoundary>2021-10-19T12:00:00</StartBoundary>
</TimeTrigger>
</Triggers>
<Actions Context="Author">
<Exec>
<Command>D:\Extra_bin\cygwin-hide.exe</Command>
<Arguments>tplink</Arguments>
</Exec>
</Actions>
</Task>
依赖的脚本代码:/v/bin/restore-tasks-from-xml
#!/bin/bash
#解析Windows计划任务备份XML文件,以供导入使用
SCRIPTPATH=$(realpath $0)
display_usage() {
echo -e "$SCRIPTPATH\n"
echo -e "\trestore-tasks-from-xml:\n \
解析Windows计划任务导出的XML文件,拆分为单任务XML,以便导入恢复Windows计划任务"
echo -e "\nUsage:\n\trestore-tasks-from-xml [*xml file]"
echo -e "Example:\n\trestore-tasks-from-xml /home/Administrator/.backup/Windows_schtasks_20210511-144010.xml"
}
# if less than two arguments supplied, display usage
if [ $# -lt 1 -o "$*" == "-h" ]
then
display_usage
exit 1
fi
# check whether user had supplied -h or --help . If yes display usage
#if [[ ( $# == "--help") || $# == "-h" ]]
if [[ ( $* == "--help") || $* == "-h" ]]
then
display_usage
exit 0
fi
xmlFile="$1"
#单个任务XML文件前缀
taskXMLprefix="/tmp/xml-task-"
if [ ! -f "$xmlFile" ];
then
echo -e "XML文件不存在!"
display_usage
exit 1
else
xmlFile=$(cygpath -au "$xmlFile")
fi
#导入路径,为空则导入备份文件保存的原先的路径
importPATH=""
if [ ! -z "$2" ];
then
importPATH=$(echo '\'$2'\'|tr -s '\\')
fi
declare -a taskFileList
#获取任务完整路径及名称
tasksName=$(xmllint --xpath "//Tasks/*[local-name()='Task']/*[local-name()='RegistrationInfo']/*[local-name()='URI']/text()" $xmlFile)
#echo "$tasksName"
#echo "----------"
#任务个数
tasksCount=$(echo "$tasksName"|grep -c '')
echo -e "共有 $tasksCount 个任务待导入..."
trap "rm -f ${taskXMLprefix}*.xml" 0
taskIndex=1
echo -e "生成单任务文件..."
declare -a tasksName1
while IFS=$(echo -e "\n") read a;
do
tasksName1+=("$a")
done <<<"${tasksName//\\/\\\\}"
OLD_IFS=$IFS
IFS=$(echo -e "\n")
for task in ${tasksName1[@]};
do
#taskName=$(basename $task)
taskName=$(echo "$task"|awk -F'\\' '{print $NF}')
importName=$task
[ ! -z "$importPATH" ] && importName=$importPATH${taskName}
echo -e "创建单任务XML: $taskName..."
taskFile="${taskXMLprefix}${taskName}.xml"
xmllint --xpath "//Tasks/*[local-name()='Task'][${taskIndex}]" $xmlFile >$taskFile
#taskFileList=(${taskFileList[*]} $taskFile)
#sid替换为当前电脑的User Sid
userSidInfo=$(PATH="$ORIGINAL_PATH" cmd /c whoami /user)
userSid=$(echo "$userSidInfo"|tail -n 1|awk '{print $2}')
awk -i inplace '/<UserId>/{getline;print "<UserId>'"${userSid}"'</UserId>"}{print}' $taskFile
echo -e "导入任务:【$importName】 from file: $taskFile"
#非强制覆盖
#gsudo SCHTASKS /Create /XML $(cygpath -aw $taskFile) /TN $importName
gsudo SCHTASKS /Create /F /XML $(cygpath -aw $taskFile) /TN $importName
let taskIndex+=1
done
IFS=$OLD_IFS
echo -e "多任务XML导入操作完毕..."
本文来自博客园,作者:晴云孤魂,转载请注明原文链接:https://www.cnblogs.com/cnhack/p/15423136.html