Sort Files Czy wiesz, że istnieje język skryptowy AutoHotkey (AHK Script), który służy do pisania zautomatyzowany akcji na danym komputerze. Dzięki nie mu możesz na przykład podpiąć daną akcję na skrót klawiszowy. Otwierać swoje ulubione foldery pod F7. Zablokować ALT+F4.

Dodatkowo modyfikować zawartość schowka kopiuj/wklej.

Na przykład modyfikować tekst z tabulatorami tak, aby była to ładna tekstowa tabelka. 

Ponownie otworzyć zamknięte wcześniej aplikacje. Automatycznie przerzucać pliki z 1 folderu do drugiego.

AHK Script to potężne narzędzie. Z ciekawości przejrzałem te wszystkie skrypty i wybrałem 1, który rzeczywiście dla mnie jest użyteczny i od ponad 3 miesięcy działa w tle. Może, kiedy przepiszę go na Pythona czy C# na razie  AHK Script zdaje egzamin, jeśli chcesz pisać szybko takie skrypty.

Aby pisać skrypty musisz najpierw zainstalować odpowiednie biblioteki ze strony głównej: 

https://www.autohotkey.com/ 

Skrypty AutoHotKey

Po instalacji każdy plik z rozszerzeniem "ahk" będzie skryptem, który może zostać skompilowany do pliku "exe".

Gdy plik exe uruchomisz będziesz mógł go zobaczyć  na pasku narzędzi

Program  AutoHotKey na pasku zadan

Idea jest tak, że potem swój skompilowany skrypt umieszczać do programów startujących wraz z systemem Windows.

Jeśli chodzi o program do pisania skryptów, to może to być notatnik lub Visual Studio Code. Musisz tylko zainstalować te dodatki.

Dodatek do Visual Studio Code : AutoHotKey by slevesqueDodatek do Visual Studio Code : AutoHotKey Plus by cweijan

Dadzą ci one kolorowanie składni oraz możliwość debugowania swojego skryptu.

Jak wygląda przykładowy kod ? Oto skrypt, który pod F7 uruchomić ci dany program.

;Launch Sublime Text
F7::Run "C:\Program Files\Sublime Text 2\sublime_text.exe"
return

Oto skrypt, który sprawi, że CAPSLOCK będzie zachowywał się jak SHIFT.

; Caps Lock acts as Shift
Capslock::Shift
return

Oto skrypt, który wyszuka zaznaczony tekst automatycznie w wyszukiwarce Google po wciśnięciu CTRL+SHIFT+C

^+c::
{
 Send, ^c
 Sleep 50
 Run, http://www.google.com/search?q=%clipboard%
 Return
}

Oto skrypt, który umieści znak specjalny pod ALT+Q

!q::SendInput {™}

Może chciałbyś automatycznie wpisać swój adres e-mail, gdy napiszesz dwie małyp

::@@::youremail@domain.com

Pora na małą instrukcję jak ustawiać takie klawisze w kodzie:

  • ^ to Ctrl
  • ! to Alt
  • # to Win
  • + to Shift

Jak widzisz, bardzo łatwo jest na pisać, nadpisać i stworzyć swoje zachowania klawiszowe.

Myszką też możesz sterować. Oto przykładowy skrypt do sterowania kursorem myszki przy użyciu Num pada:

https://www.autohotkey.com/docs/scripts/NumpadMouse.ahk

Możliwości tutaj się kończą. W teorii możesz zautomatyzować wysłanie e-mail w Outlook-u

HOTKEY:: ; whatever you want it to be
    app := ComObjActive("Outlook.Application")
    olMailItem := 0 
    MailItem := app.CreateItem(olMailItem)
    MailItem.Subject := "SUBJECT"
;MailItem.Recipient.Add("email@email.com").Type := TO: 1 CC: 2 BCC: 3
    MailItem.Recipients.Add("recipientTO@email.com").Type := 1
    MailItem.Recipients.Add("recipientCC@email.com").Type := 2
    MailItem.Recipients.Add("recipientBCC@email.com").Type := 3
    MailItem.Display
    Sleep 500
    send, BODY TEXT
return

Przejdźmy do króla tego wpisu, czyli do sortowania folderu pobrane w twoim komputerze

Cały skrypt wygląda tak:

;---------------------------------------------------------------------------------------------------------------------------------------;
; Initialization
;---------------------------------------------------------------------------------------------------------------------------------------;
    #NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
    SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
    SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.
    #SingleInstance Force

;---------------------------------------------------------------------------------------------------------------------------------------;
; User Variables
;---------------------------------------------------------------------------------------------------------------------------------------;
    ;Behaviour
        MonitoredFolder = D:\PanNiebieski\Pobrane
        UnzipTo = D:\PanNiebieski\Pobrane\Compressed
        HowOftenToScanInSeconds = 60 ;How long we wait before re-scanning the folder for any changes.
        ToolTips = 1 ;Show helper popups showing what the program is doing.
        OverWrite = 1 ;Overwrite duplicate files?
        RemoveEmptyFolders = 1 ;Delete any folders in the monitored folder that are now empty. (recursive)

    ;Zip files
        7ZipLocation = D:\PanNiebieski\Programy\7-Zip\7zG.exe  ;Needed to provide unzipping functionality.
        OpenExtractedZip = False ;Open the folder up after extraction has finished?
        DeleteZipFileAfterExtract = 1 ;Recycle the zip file after a successful extract.
        UnzipSuccessSound = 1 ;Play a jingle when unzipped something.

    ;What filetypes belong to what group, and what their folder name should be sorted into.
        FiletypeObjectArray := [] ;Array needs to be initiated first to work.
        PushFiletypeToArray(FiletypeObjectArray,["zip","7z","rar","r00","001"], "Compressed")
        PushFiletypeToArray(FiletypeObjectArray,["jpg","bmp","gif","gifv","webm","png","jpeg","swf","tga","tiff","exr","psd"], "Images")
        PushFiletypeToArray(FiletypeObjectArray,["txt","nfo","rtf","pdf","docx","doc"], "Documents")
        PushFiletypeToArray(FiletypeObjectArray,["xlsm","xlsx","csv"], "Excel")
        PushFiletypeToArray(FiletypeObjectArray,["mp3","flac","wav"], "Audio")
        PushFiletypeToArray(FiletypeObjectArray,["avi","mpg","mpeg","mov","mp4","mkv","wmv"], "Videos")
        PushFiletypeToArray(FiletypeObjectArray,["exe","msi","jar","cmd","bat","ahk"], "Programs")
        PushFiletypeToArray(FiletypeObjectArray,["ttf","otf"], "Fonts")
        PushFiletypeToArray(FiletypeObjectArray,["h3m"], "Heroes3Maps")
        PushFiletypeToArray(FiletypeObjectArray,["tpi"], "TouchPortal")
        PushFiletypeToArray(FiletypeObjectArray,["smc"], "SNESGames")
        PushFiletypeToArray(FiletypeObjectArray,["adf","lha"], "AmigaGames")
        PushFiletypeToArray(FiletypeObjectArray,["vsix"], "VisualStudio Extensions")
;---------------------------------------------------------------------------------------------------------------------------------------;
; Main
;---------------------------------------------------------------------------------------------------------------------------------------;
;Start the folder monitor
    WaitTimeBetweenScans := HowOftenToScanInSeconds * 1000
    SetTimer, SearchFiles, %WaitTimeBetweenScans%
    GoSub,SearchFiles ; Immediately do a scan
    return

;---------------------------------------------------------------------------------------------------------------------------------------;
; Functions
;---------------------------------------------------------------------------------------------------------------------------------------;    
    ;Utilities
        HasVal(haystack, needle)
        {
            for index, value in haystack
                if (value = needle)
                    return index
            if !IsObject(haystack)
                throw Exception("Bad haystack!", -1, haystack)
            return 0
        }
        
        MakeFolderIfNotExist(TheFolderDir)
        {
            ifnotexist,%TheFolderDir%
                FileCreateDir,%TheFolderDir%
        }        
        
        RemoveEmptyFolders(Folder)
        {
            global Tooltips
            Loop, %Folder%\*, 2, 1
            {
              FL := ((FL<>"") ? "`n" : "" ) A_LoopFileFullPath
                Sort, FL, R D`n ; Arrange folder-paths inside-out
                Loop, Parse, FL, `n
                {
                  FileRemoveDir, %A_LoopField% ; Do not remove the folder unless is  empty
                  If ! ErrorLevel
                    {
                       Del := Del+1,  RFL := ((RFL<>"") ? "`n" : "" ) A_LoopField
                        if Tooltips
                        {
                            Tooltip,Removing empty folder %FL%
                            SetTimer, RemoveToolTip, 3000
                        }
                    }
                }
            }
        }
        return
        
        FindZipFiles(Folder,GoalObjectDestination)
        {
            global FiletypeObjectArray
            global MonitoredFolder
            global Tooltips
            i = 0
            
            loop % FiletypeObjectArray.Count() 
            {
                i ++
                ;Get a ref to the object that holds the array of extensions we want.
                    if FiletypeObjectArray[i].Destination != GoalObjectDestination                        
                        continue
                    o := FiletypeObjectArray[i]
                
                ;Unzip
                    if o ;Without this it may end up unzipping to the root of C drive? i THINK "" defaults to C:\ when using Loop Files
                    {
                        Loop, Files, %MonitoredFolder%\*.*,R
                        {
                            if HasVal(o.Extensions,A_LoopFileExt)
                                UnZip(A_LoopFileName,A_LoopFileDir,A_LoopFileFullPath)
                        }
                    }
            }
        }
        return
        
        UnZip(FileFullName,Dir,FullPath)
        {
            global 7ZipLocation ;Saves having to re-pass this dir each time you use this function.
            global DeleteZipFileAfterExtract
            global OpenExtractedZip
            global Tooltips
            global UnzipTo
            global UnzipSuccessSound
            
            ;Get filename
                StringGetPos,ExtentPos,FileFullName,.,R
                FileName := SubStr(FileFullName,1,ExtentPos)
                if Tooltips
                {
                    Tooltip,Unzipping %FileName% > %Dir%\%FileName%
                    SetTimer, RemoveToolTip, 3000
                }
                MakeFolderIfNotExist(UnzipTo . "\" . FileName)
                Runwait, "%7ZipLocation%" x "%FullPath%" -o"%UnzipTo%\%FileName%"
            sleep,2000
            
            IfExist %UnzipTo%\%FileName%
            {
                if DeleteZipFileAfterExtract
                    Filerecycle, %FullPath%
                if OpenExtractedZip
                    run, %UnzipTo%\%FileName%
                if UnzipSuccessSound
                    soundplay, *64
            }
            else
                msgbox,,Oh Noes!,Something went wrong and I couldn't unzip %FileName% to %UnzipTo%\%FileName%
        }

        
    ;Objects
        PushFiletypeToArray(InputArray,FiletypesArray,Destination)
        {
            InputArray.Push(MakeFiletypeObject(FiletypesArray,Destination))
            return InputArray
        }

        MakeFiletypeObject(InputArray,Destination)
        {
            object := []
            object.Extensions := InputArray
            object.Destination := Destination
            return object
        }
        
        GetDestination(TheFile)
        {
            global FiletypeObjectArray
            for i in FiletypeObjectArray
            {
                if HasVal(FiletypeObjectArray[i].Extensions,A_LoopFileExt)
                    Destination := FiletypeObjectArray[i].Destination
            }
        return Destination
}

;---------------------------------------------------------------------------------------------------------------------------------------;
; Subroutines
;---------------------------------------------------------------------------------------------------------------------------------------;
    ;Main
        SearchFiles:
        Loop, Files, %MonitoredFolder%\*
        {
            DestinationFolder := GetDestination(A_LoopFileFullPath)
            if (DestinationFolder = "Compressed")
                UnZip(A_LoopFileName,A_LoopFileDir,A_LoopFileFullPath)
            else if DestinationFolder
            {
                DestinationFolder := MonitoredFolder . "\" . DestinationFolder
                MakeFolderIfNotExist(DestinationFolder)
                FileMove,%A_LoopFileFullPath%,%DestinationFolder%\*.*,%OverWrite% ; *.* is needed else it could be renamed to no extension! (If dest folder failed)
                    if Tooltips
                    {
                        Tooltip,Moving %A_LoopFileName% > %DestinationFolder%
                        SetTimer, RemoveToolTip, 3000
                    }
            }
        }
        if RemoveEmptyFolders
            RemoveEmptyFolders(MonitoredFolder)
        FindZipFiles(MonitoredFolder,"Compressed")
    
    ;Other
        RemoveToolTip:
            SetTimer, RemoveToolTip, Off
            ToolTip
        return

^Esc::ExitApp

W zmiennych określasz zachowanie swojego sortującego programu.

;Behaviour
    MonitoredFolder = D:\PanNiebieski\Pobrane
    UnzipTo = D:\PanNiebieski\Pobrane\Compressed
    HowOftenToScanInSeconds = 60 ;How long we wait before re-scanning the folder for any changes.
    ToolTips = 1 ;Show helper popups showing what the program is doing.
    OverWrite = 1 ;Overwrite duplicate files?
    RemoveEmptyFolders = 1 ;Delete any folders in the monitored folder that are now empty. (recursive)

;Zip files
    7ZipLocation = D:\PanNiebieski\Programy\7-Zip\7zG.exe  ;Needed to provide unzipping functionality.
    OpenExtractedZip = False ;Open the folder up after extraction has finished?
    DeleteZipFileAfterExtract = 1 ;Recycle the zip file after a successful extract.
    UnzipSuccessSound = 1 ;Play a jingle when unzipped something.

Tutaj określasz jaki folder ma być monitorowany. Gdzie pliki zip mają być wypakowywane. Jak często ma się odbywać skan twojego folderu. Czy plik, które się powtarzają, mają być zastąpione czy nie? Czy puste foldery mają być automatycznie kasowane?

Dodatkowo musi wskazać ścieżkę do programi 7-Zip, który będzie wypakowywał twoje pliki.

Zostało jeszcze określenie czy po wypakowaniu pliku zip automatycznie ma Ci wyskoczyć okno z tym folderem?

Gdy to wiesz, pozostało Ci określić, jakie pliki mają być sortowane gdzie w twoim folderze.

;What filetypes belong to what group, and what their folder name should be sorted into.
    FiletypeObjectArray := [] ;Array needs to be initiated first to work.
    PushFiletypeToArray(FiletypeObjectArray,["zip","7z","rar","r00","001"], "Compressed")
    PushFiletypeToArray(FiletypeObjectArray,["jpg","bmp","gif","gifv","webm","png","jpeg","swf","tga","tiff","exr","psd"], "Images")
    PushFiletypeToArray(FiletypeObjectArray,["txt","nfo","rtf","pdf","docx","doc"], "Documents")
    PushFiletypeToArray(FiletypeObjectArray,["xlsm","xlsx","csv"], "Excel")
    PushFiletypeToArray(FiletypeObjectArray,["mp3","flac","wav"], "Audio")
    PushFiletypeToArray(FiletypeObjectArray,["avi","mpg","mpeg","mov","mp4","mkv","wmv"], "Videos")
    PushFiletypeToArray(FiletypeObjectArray,["exe","msi","jar","cmd","bat","ahk"], "Programs")
    PushFiletypeToArray(FiletypeObjectArray,["ttf","otf"], "Fonts")
    PushFiletypeToArray(FiletypeObjectArray,["h3m"], "Heroes3Maps")
    PushFiletypeToArray(FiletypeObjectArray,["tpi"], "TouchPortal")
    PushFiletypeToArray(FiletypeObjectArray,["smc"], "SNESGames")
    PushFiletypeToArray(FiletypeObjectArray,["adf","lha"], "AmigaGames")
    PushFiletypeToArray(FiletypeObjectArray,["vsix"], "VisualStudio Extensions")

Według tego kodu pliki:

  • "jpg","bmp","gif","gifv","webm","png","jpeg","swf","tga","tiff","exr","psd"
    • idą do folderu Images
  • "txt","nfo","rtf","pdf","docx","doc"
    • idą do folderu Documents
  • "xlsm","xlsx","csv"
    • idą do folderu Excel
  • "mp3","flac","wav"
    • idą do folderu Audio
  • "avi","mpg","mpeg","mov","mp4","mkv","wmv"
    • idą do folderu Video
  • "exe","msi","jar","cmd","bat","ahk"
    • idą do folderu Programs

Analogicznie możesz dodać swoje foldery do tego automatycznego porządkowania folderu. Jak dodałem rozszerzenie do plików ADF, które są obrazami dyskietek z Amigi. Dodałem także folder do map z Heroes 3.

Jak działa dalej skrypt? Skrypt tak naprawdę raz 60 sekund uruchamia pętle na każdy pliku znajdującym się w folderze.

SearchFiles:
Loop, Files, %MonitoredFolder%\*
{
    DestinationFolder := GetDestination(A_LoopFileFullPath)
    if (DestinationFolder = "Compressed")
        UnZip(A_LoopFileName,A_LoopFileDir,A_LoopFileFullPath)
    else if DestinationFolder
    {
        DestinationFolder := MonitoredFolder . "\" . DestinationFolder
        MakeFolderIfNotExist(DestinationFolder)
        FileMove,%A_LoopFileFullPath%,%DestinationFolder%\*.*,%OverWrite% ; *.* is needed else it could be renamed to no extension! (If dest folder failed)
            if Tooltips
            {
                Tooltip,Moving %A_LoopFileName% > %DestinationFolder%
                SetTimer, RemoveToolTip, 3000
            }
    }
}
if RemoveEmptyFolders
    RemoveEmptyFolders(MonitoredFolder)
FindZipFiles(MonitoredFolder,"Compressed")
    
    ;Other
RemoveToolTip:
    SetTimer, RemoveToolTip, Off
    ToolTip
return

Czy warto mieć taki skrypt uruchomiony? O ile pierwsze uruchomienie było dramatyczne po miałem dużo plików "zip", które nawet nie pamiętam, co trzymały, to później stwierdziłem definitywnie, że ten automatyczny porządek przyspiesza moją pracę.

Wiem, gdzie są obrazki, które przed chwilą ściągnąłem. Wiem, gdzie są instalki, które kiedyś pobrałem.

AHK Script to naprawdę potężne narzędzie i kto wiem, może kiedyś go użyje ponownie.