以GTD的思想整理目录的PowerShell脚本
这是花了一个晚上写的PowerShell脚本,可以把你的目录以GTD的思想整理得井井有条。
但是GTD功能已经完整并通过测试。github地址:victorwoo/Get-ThingsDone。
不了解GTD的同学请参考以下材料:
[《搞定》中文版] 1、[《Get Things Done》英文版] 2
#========================================================================
# Created on: 2013-3-7 16:21
# Created by: Victor.Woo
# Organization: www.vichamp.com
# Filename: Get-ThingsDone.ps1
#========================================================================
function Check-Enviroment
{
$gtdPath = "HKCU:\Software\Vichamp\GTD"
if ((Get-ItemProperty $gtdPath -ErrorAction SilentlyContinue).AutoStart -eq "False")
{
return
}
$runPath = 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run'
$run = Get-ItemProperty $runPath
if ($run.GTD -eq $null)
{
$title = '自动执行请求'
$message = '在当前 Windows 用户登录时自动运行此脚本,可以自动帮助您整理、规划当日的工作内容。如果您选择了“是”,但将来不希望自动启动,请执行 uninstall.cmd。是否在当前用户登录时自动执行脚本?'
$yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Windows 用户登录时自动运行此脚本。"
$no = New-Object System.Management.Automation.Host.ChoiceDescription "&No","Windows 用户登录时不运行此脚本,并且不再提示。"
$options = [System.Management.Automation.Host.ChoiceDescription[]]($yes,$no)
$result = $Host.UI.PromptForChoice($title,$message,$options,0)
switch ($result)
{
0 {
Set-ItemProperty -Path $runPath -Name GTD -Value $gtdCmd
}
1 {
md $gtdPath -Force
Set-ItemProperty -Path $gtdPath -Name AutoStart -Value "False"
}
}
}
}
function TryCreate-Directory ([Parameter(Mandatory = $True)] [string]$dirName)
{
$private:dir = Join-Path $baseDir $dirName
if (-not (Test-Path $dir))
{
Write-Output "$dir 不存在,正在创建。"
mkdir $dir | Out-Null
}
}
function TryCreate-Directories ()
{
Write-Output "正在检查目录完整性"
$dirNames |
% {
TryCreate-Directory $_
}
}
function Remove-Directories ()
{
$dirNames |
% {
$private:dir = Join-Path $baseDir $_
if (Test-Path $dir)
{
Write-Warning "正在移除$dir"
rm $dir -Recurse
}
}
}
function MoveTo-WithRenamming (
[Parameter(Mandatory = $True)] [System.IO.FileSystemInfo]$item,
[Parameter(Mandatory = $True)] [string]$targetDir)
{
function Get-NextFilePath ([string]$dir,[System.IO.FileInfo]$fileInfo)
{
$Private:targetFilePath = Join-Path $dir $fileInfo.Name
if (Test-Path $Private:targetFilePath)
{
$Private:index = 1
do {
$Private:targetFilePath = Join-Path $dir "$($fileInfo.BaseName) ($index)$($fileInfo.Extension)"
$Private:index++
}
while (Test-Path $Private:targetFilePath)
}
return [System.IO.FileInfo]$Private:targetFilePath
}
function Get-NextDirectoryPath ([string]$dir,[System.IO.DirectoryInfo]$directoryInfo)
{
$Private:targetDirectoryPath = Join-Path $dir $directoryInfo.Name
if (Test-Path $Private:targetDirectoryPath)
{
$Private:index = 1
do {
$Private:targetDirectoryPath = Join-Path $dir "$($directoryInfo.Name) ($index)"
$Private:index++
}
while (Test-Path $Private:targetDirectoryPath)
}
return [System.IO.DirectoryInfo]$Private:targetDirectoryPath
}
Write-Output "正在移动 $item 至 $targetDir 目录"
if ($item -is [System.IO.FileInfo])
{
# 待移动的是文件
[System.IO.FileInfo]$item = [System.IO.FileInfo]$item
$Private:targetFilePath = Join-Path $targetDir $item.Name
if (Test-Path $Private:targetFilePath)
{
# 目标文件已存在
$targetFileInfo = [System.IO.FileInfo]$Private:targetFilePath
$Private:targetFilePath = Get-NextFilePath $targetDir $item
if ($item.LastWriteTime -eq $targetFileInfo.LastWriteTime -and $item.Length -eq $targetFileInfo.Length)
{
# 文件时间和大小相同
Write-Warning "源文件 $item.FullName 与目标文件 $targetFileInfo.FullName 相同,删除源文件"
Remove-Item $item.FullName
}
else
{
Write-Warning "目标文件已存在,自动改名为$($Private:targetFilePath.Name)"
Move-Item $item.FullName $Private:targetFilePath | Out-Null
}
}
else
{
# 目标文件不存在
if (!(Test-Path $targetDir))
{
# 目标目录不存在,创建目标目录
md $targetDir | Out-Null
}
Move-Item $item.FullName $Private:targetFilePath | Out-Null
}
} elseif ($item -is [System.IO.DirectoryInfo])
{
# 待移动的是目录
[System.IO.DirectoryInfo]$item = [System.IO.DirectoryInfo]$item
$Private:targetDirectoryPath = Join-Path $targetDir $item.Name
if (Test-Path $Private:targetDirectoryPath)
{
$Private:targetDirectoryPath = Get-NextDirectoryPath $targetDir $item
Write-Warning "目标文件夹已存在,自动改名为$($Private:targetDirectoryPath.Name)"
}
Move-Item $item.FullName $Private:targetDirectoryPath | Out-Null
}
}
function Process-IsolatedItems
{
Write-Output "正在将游离内容移至 [STUFF] 目录"
Get-ChildItem $baseDir -Exclude ($dirNames + $reservedDirs + $reservedFiles) |
% {
MoveTo-WithRenamming $_ $stuffDir
}
}
function Process-TomorrowDir
{
Write-Output "正在处理 [TOMORROW] 目录"
Get-ChildItem $tomorrowDir |
% {
MoveTo-WithRenamming $_ $todayDir
}
}
function Process-CalendarDir
{
Write-Output "正在处理 [CALENDAR] 目录"
Get-ChildItem $calendarDir -File |
% {
MoveTo-WithRenamming $_ $stuffDir
}
Get-ChildItem $calendarDir -Directory |
% {
$regex = [regex]'(?m)^(?<year>19|20\d{2})[-_.](?<month>\d{1,2})[-_.](?<day>\d{1,2})$'
$match = $regex.Match($_.Name)
if ($match.Success)
{
$Private:year = $regex.Match($_.Name).Groups['year'].Value;
$Private:month = $regex.Match($_.Name).Groups['month'].Value;
$Private:day = $regex.Match($_.Name).Groups['day'].Value;
$Private:date = New-Object System.DateTime $Private:year,$Private:month,$Private:day
$now = (Get-Date)
$today = $now.Subtract($now.TimeOfDay)
if ($date -lt $today)
{
Write-Output "移动过期任务 $($_.Name) 到 [STUFF] 目录"
MoveTo-WithRenamming $_ $stuffDir
}
elseif ($date -eq $today)
{
Write-Output "移动今日任务 $($_.Name) 到 [TODAY] 目录"
MoveTo-WithRenamming $_ $todayDir
}
elseif ($date -eq $today.AddDays(1))
{
Write-Output "移动明日任务 $($_.Name) 到 [TOMORROW] 目录"
MoveTo-WithRenamming $_ $tomorrowDir
}
}
else
{
Write-Output "[CALENDAR] 目录下,$($_.Name) 名字不符合规范,将移动至 [STUFF] 目录"
MoveTo-WithRenamming $_ $stuffDir
}
}
}
function Process-ArchiveDir
{
Write-Output "正在检查 [ARCHIVE] 目录"
# 创建本月目录
$nowString = "{0:yyyy.MM}" -f (Get-Date)
$thisMonthDir = Join-Path $archiveDir $nowString
if (-not (Test-Path $thisMonthDir))
{
Write-Output "正在创建本月目录"
md $thisMonthDir
}
# 移除除本月之外的空目录
Get-ChildItem $archiveDir -Exclude $nowString -Recurse |
Where { $_.PSIsContainer -and @( Get-ChildItem -LiteralPath $_.FullName -Recurse | Where { !$_.PSIsContainer }).Length -eq 0 } |
% {
Write-Output "正在删除空目录$($_.FullName)"
Remove-Item -Recurse
}
# 移动所有文件到 本月存档 目录
Get-ChildItem $archiveDir -File |
% {
$lastWriteTime = "{0:yyyy.MM}" -f $_.LastWriteTime
$lastWriteDir = Join-Path $archiveDir $lastWriteTime
Write-Output "移动 [ARCHIVE] 目录下,$($_.Name) 游离文件至 $lastWriteDir 存档目录"
MoveTo-WithRenamming $_ $lastWriteDir
}
# 检查目录命名是否符合规范。
Get-ChildItem $archiveDir -Directory |
% {
$regex = [regex]'(?m)^(?<year>19|20\d{2})[-_.](?<month>\d{1,2})$'
$match = $regex.Match($_.Name)
if ($match.Success)
{
# Archive目录下的名字符合格式
$year = $regex.Match($_.Name).Groups['year'].Value;
$month = $regex.Match($_.Name).Groups['month'].Value;
$date = New-Object System.DateTime $year,$month,1
if ($date -gt (Get-Date))
{
Write-Output "[ARCHIVE] 目录下,$($_.Name) 名字不符合规范(存档日期超出当前时间),将移动至 [STUFF] 目录"
MoveTo-WithRenamming $_ $stuffDir
}
else
{
$formattedDate = "{0:yyyy.MM}" -f $date
if ($_.Name -ne $formattedDate)
{
$targetDirectory = [System.IO.DirectoryInfo](Join-Path $_.Parent.FullName $formattedDate)
Write-Warning "将 [ARCHIVE] 下的目录名 $($_.Name) 处理为规范格式 $($targetDirectory.Name)"
Move-Item $_.FullName $targetDirectory.FullName
}
}
} else
{
# Archive目录下的名字符不合格式
$lastWriteTime = $nowString = "{0:yyyy.MM}" -f $_.LastWriteTime
$lastWriteDir = Join-Path $archiveDir $lastWriteTime
Write-Output "移动 [ARCHIVE] 目录下,$($_.Name) 游离文件夹至 $lastWriteDir 存档目录"
MoveTo-WithRenamming $_ $lastWriteDir
}
}
}
function Explore-Dirs
{
if ((Get-ChildItem $stuffDir) -ne $null)
{
explorer $stuffDir
}
if ((Get-ChildItem $todayDir) -ne $null)
{
explorer $todayDir
}
}
$STUFF = "1.STUFF"
$TODAY = "2.TODAY"
$TOMORROW = "3.TOMORROW"
$UPCOMING = "4.UPCOMING"
$CALENDAR = "5.CALENDAR"
$SOMEDAY = "6.SOMEDAY"
$ARCHIVE = "7.ARCHIVE"
$dirNames = $STUFF,$TODAY,$TOMORROW,$UPCOMING,$CALENDAR,$SOMEDAY,$ARCHIVE
$reservedDirs = ".git","_gsdata_"
$reservedFiles = ".gitignore","Get-ThingsDone.ps1","README*.md","gtd_logo.png","LICENSE.md","GTD.cmd","uninstall.cmd"
$baseDir = Split-Path $MyInvocation.MyCommand.Path
$stuffDir = Join-Path $baseDir $STUFF
$todayDir = Join-Path $baseDir $TODAY
$tomorrowDir = Join-Path $baseDir $TOMORROW
$calendarDir = Join-Path $baseDir $CALENDAR
$archiveDir = Join-Path $baseDir $ARCHIVE
$gtdCmd = Join-Path $baseDir "GTD.cmd"
Get-Date | Write-Output
Check-Enviroment
TryCreate-Directories
Process-IsolatedItems
Process-TomorrowDir
Process-CalendarDir
Process-ArchiveDir
Explore-Dirs
######################### 开发临时用(在 ISE 中选中并按 F8 执行) #########################
return
{
return
# 创建游离内容。
$null | Set-Content (Join-Path $baseDir "to.del.file.txt")
md (Join-Path $baseDir "to.del.dir") | Out-Null
}
{
return
# 对代码排版。
Import-Module D:\Dropbox\script\DTW.PS.PrettyPrinterV1\DTW.PS.PrettyPrinterV1.psd1
Edit-DTWCleanScript D:\Dropbox\vichamp\GTD\Get-ThingsDone.ps1
}
{
return
# 移除所有目录
Remove-Directories
}