AHK to group files by Type - autohotkey

How can I script a AHK to group files in Windows 10 explorer by Type and Sort by Date
This is the script I have now for Details layout and sort by name.
#o:: ; Win+O
PostMessage, 0x111, 28747,, SHELLDLL_DefView1, A ;View, Details
;PostMessage, 0x111, 28753,, SHELLDLL_DefView1, A ;View, List
PostMessage, 0x111, 31492,, SHELLDLL_DefView1, A ;View, Sort by, Name ;toggle ascending/descendingreturn
I would lile to expand this to Group by Type and Sort by DATE

Related

How to simulate multiple enter key press of batch file execution cmd or windows powershell?

I have batch file which I want to execute, during execution it asks for user input twice to press enter key. I want to skip this completely either by simulating the enter key press or by somehow completely overcoming it.
Edit : I can't edit the bat file to skip the program asking for user input
I have tried echo | <yourfinecommandhere> but this just simulates one enter key press. I am unable to simulate multiple enter key press
In cmd simply group multiple echos. For example this will print 2 newlines to the pipe
(echo[& echo[) | command
echo( is the most reliable way to print a new line in cmd, but in this case echo[ is probably better. Space is significant here so there must be no space after [
In PowerShell it's easier. For example to print 3 newlines use
"`n`n`n" | command
Actually the above will print 4 new lines, because there's an implicit new line after the string. But that won't affect the output. If you want exactly 3 new lines then use
Write-Output -NoNewline "`n`n`n" | command

Powershell: I need help to modify a lengthy SQL script file

I have a lengthy SQL script which contains MANY/multiple sections like this (amongst other script sections):
USE [NAVDB]
GO
IF NOT EXISTS (SELECT name FROM sys.database_principals
WHERE name = '##Placeholder##')
CREATE USER [MyDomain\adcsuser]
FOR LOGIN [MyDomain\adcsuser]
WITH DEFAULT_SCHEMA = [MyDomain\adcsuser]
GO
GRANT CONNECT TO [MyDomain\adcsuser] AS [dbo]
GO
I need to parse this script tile and modify only the IF NOT EXISTS...CREATE USER... lines of the script such that "##Placeholder##" is replaced by the text within the square brackets [] in the same line immediately following the CREATE USER string.
So, the line in the above snippet would become:
IF NOT EXISTS (SELECT name FROM sys.database_principals
WHERE name = 'MyDomain\adcsuser')
CREATE USER [MyDomain\adcsuser]
FOR LOGIN [MyDomain\adcsuser]
WITH DEFAULT_SCHEMA = [MyDomain\adcsuser]
The file is many hundreds of lines long with many dozen (at least) of these sections in it.
NotePad++ find-replace and macros couldn't handle it because of the "\" in the names between the []s and I couldn't find how to make NP++ copy the text between []s.
Also, I tried reviewing other related answers like: How can I replace every occurrence of a String in a file with PowerShell?, but remain stymied so far.
Because of the complex nature of the script file structure I'm leery of "read the whole file and just use Regex find/replace" approaches, so I was looking for a more... RBAR (Row By Agonizing Row) approach.
Dynamic SQL or parameterized approaches and similar suggestions are NOT APPROPRIATE for this situation as THE SCRIPT WAS ALREADY DYNAMICALLY GENERATED from an existing Production system (and I do not have the source code for the utility which generated this as output). I literally can't make wholesale structural changes like that.
Also, after reading this post once again, I should point out that the whole "IF NOT EXISTS...WITH DEFAULT_SCHEMA [*]" command is on ONE LINE in the script file (if that wasn't sufficiently clear).
For NotePad++ the find and replace support regex.
Example of how to find and replace all lines containing "CREATE USER [someusername]" with your replacement ##Placeholder## would be:
The .* is wildcard, brackets are special characters in regex so to include them as part of the search you have to escape them. So \[.*\] would find anything wrapped in brackets. I just added CREATE USER as an example on finding all those specific lines.
Making sure to select "Regular expression" in Search Mode.
PowerShell, with everything on one line, you can read in each line, find the match, extract the user and then replace; copying that line back out to a new file.
Input test file:
Example PowerShell script:
#create new output file
new-item "C:\temp\test2.txt" -Force
foreach ($line in (get-content "C:\temp\test.txt"))
{
#Find the the user between the brackets and remove the brackets.
$user = ([regex]"\[.*\]").Match($line).value.replace("[","").replace("]","")
#Replace PlaceHolder with user pulled from the brackets and write to new file
$line -replace '##PlaceHolder##', $user | out-file "C:\temp\test2.txt" -Append
}
Then the contents of the output file:
#Tim Mylott,
The entire issue arose from a script that was generated off my production system from Powershell's DBATools' Export-DbaLogin command.
The output script from that tool was like your first text.txt file in that it issues blind CREATE USER commands without first testing to see if the user already existed.
This was inadequate for our needs (our production DB and system have been around for years and now has old, bad 'lint' all over the place in it), so I took the script output into Notepad++ and added the IF NOT EXIST... test logic (see the script in the original question for details) as a prefix to the CREATE USER commands in the script with a global search & replace. Only, I had to put ##Placeholder## in for the username in the SELECT's WHERE clause for the test.
This left me with a script where I had to replace the ##Placeholder## text with the actual username strings in the existing CREATE USER text on the same line in the script file.
The solution, in NP++, which you led me to, was to use Regex in the NP++ Search to select the userid string in the same line. From there, it was fairly easy and straightforward to use a NP++ macro recording to automate the search & replace.
First, I found online a regex to select the text between the first matching pair of []s, which is: (?<=[).+?(?=])
The NP++ script recording was basically this (start Macro recording):
Find:##PlaceHolder## (non-regex search) [NP++ finds the next line to alter]
[Home] (to find the beginning of the line)
Find: (?<=[).+?(?=]) (regex search) [NP++ selects the UserID string in the CREATE USER part of the line)
[Ctrl+C] (copy UserID string to clipboard once Find dialog is closed)
[Home] (to find the beginning of the line again)
Find:##PlaceHolder## (non-regex search) [NP++ finds the ##Placeholder## text in the current line and selects it]
[Ctrl+V] (paste in the UserID string from the clipboard)
(this leaves the cursor on the same location where the corrected command can be visually verified as correct)
In NP++ Use the Play Macro button to manually replay this recorded macro a few times to assure the macro has it right...
Then, use the play multiple times (until the end of file is reached), and Voila!
The script file is all fixed up.
[Note: the DBATools GitHub now has 2 bug reports/requests to add this IF NOT EXISTS logic to the CREATE USER lines in the generated script, so this issue will eventually go away, but for now... there is at least one reasonable solution using NotePad++.]
So, Thank You, Tim, for leading me to a quick answer.
That said, though, your Powershell part of the answer was wrong, so no points there! (it was doing the wrong thing and putting "##Placeholder##" INTO the script, rather than replacing the existing "##PlaceHolder##" string in the script with the actual UserID strings already IN the script.
To repeat, the problem was (logically) to go:
-- FROM THIS:
IF NOT EXISTS (SELECT ... WHERE name = '##PlaceHolder##') CREATE USER [MyDomain\AUser2]...
IF NOT EXISTS (SELECT ... WHERE name = '##PlaceHolder##') CREATE USER [MyDomain\AUser3]...
IF NOT EXISTS (SELECT ... WHERE name = '##PlaceHolder##') CREATE USER [MyDomain\AUser5]...
IF NOT EXISTS (SELECT ... WHERE name = '##PlaceHolder##') CREATE USER [MyDomain\AUser1]...
IF NOT EXISTS (SELECT ... WHERE name = '##PlaceHolder##') CREATE USER [MyDomain\AUser7]...
IF NOT EXISTS (SELECT ... WHERE name = '##PlaceHolder##') CREATE USER [MyDomain\AUser8]...
IF NOT EXISTS (SELECT ... WHERE name = '##PlaceHolder##') CREATE USER [MyDomain\AUserA]...
IF NOT EXISTS (SELECT ... WHERE name = '##PlaceHolder##') CREATE USER [MyDomain\AUserB]...
IF NOT EXISTS (SELECT ... WHERE name = '##PlaceHolder##') CREATE USER [MyDomain\AUserC]...
-- TO THIS:
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser2') CREATE USER [MyDomain\AUser2]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser3') CREATE USER [MyDomain\AUser3]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser5') CREATE USER [MyDomain\AUser5]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser1') CREATE USER [MyDomain\AUser1]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser7') CREATE USER [MyDomain\AUser7]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUser8') CREATE USER [MyDomain\AUser8]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUserA') CREATE USER [MyDomain\AUserA]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUserB') CREATE USER [MyDomain\AUserB]...
IF NOT EXISTS (SELECT ... WHERE name = 'MyDomain\AUserC') CREATE USER [MyDomain\AUserC]…
Revised Script from Tim's excellent start (version 2) :-)
#create new output file
new-item "C:\temp\test2.sql" -Force
foreach ($line in (get-content "C:\temp\test.sql"))
{
if ($line.Contains("##Placeholder##"))
{
#Find the the user between the brackets and remove the brackets.
$user = ([regex]"\[.+?\]").Match($line).value.replace("[","").replace("]","")
#Replace PlaceHolder with user pulled from the brackets and write to new file
$line -replace '##Placeholder##', $user | out-file "C:\temp\test2.sql" -Append
}
else
{
out-file "C:\temp\test2.sql" -Append -InputObject $line
}
}
...Had to tweak the Regex (to less permissive) and output all the rest of the script lines too.

How to do multiple actions with AutoHotKey

I haven't used AHK before so it's probably a typographical error but would love any help. I'm trying to click into the search bar on File Explorer, search for the phrase .pdf and then select all of the files. Then I want to click the back button so I can go to the parent folder and then paste the files. Lastly, I want to click home and then click the folder at the top and delete it. The purpose for all of this is to take all the PDFs out of the subfolders and put them into the main folder, and then delete the subfolders.
I've tried this.
^e::
Click 3510,201
Send, *.pdf*{Enter} ^a ^x
Click 2601,200
^v
Send {Home}
Click 2896,266
Send {Delete}
Return
Currently, when I try it, it deletes all of the files in my folder.
There appear to be a few issues with your code and addressing each may make your code work, but I suggest a different, more robust, approach.
Try using a file-loop to find all files in a location (and sub-locations) with the PDF extension and FileMove to move these to your desired location.
https://www.autohotkey.com/docs/commands/LoopFile.htm
https://www.autohotkey.com/docs/commands/FileMove.htm
Edit (per comments)
There are a few ways to grab the path from file explorer. The easiest I can think is to send a ^{f4} which will select the full path, from there you can send a ^c copy it to the clipboard and use it for the file-loop path.
Edit2 (working example)
f1::
WinGetClass , vClass , A
If !(vClass = "CabinetWClass") ; Stops here if file explorer isn't the active window
Return
clipboard := ""
Send , ^{f4}{esc}^c
ClipWait , 1
If ErrorLevel ; Stops here if nothing was copied
Return
Loop , Files , % clipboard . "\*.pdf" , R
FileMove , %A_LoopFileLongPath% , %clipboard%
Return
Note that this will fail for named locations like "Documents", Downloads", or "This PC"; it must be a full path to work. Also, this will not delete the subfolders, but see FileRemoveDir for help with that.
https://www.autohotkey.com/docs/commands/FileRemoveDir.htm
Edit3 (using FileRemoveDir to delete empty folders)
This snippet will delete empty folders, starting with the deepest level and working to the top level. I take no credit for this as it by SKAN from the original AHK forums.
SetBatchLines -1
FileSelectFolder, Folder, , % (Del:=0), Purge Empty Folders
If ( ErrorLevel Or Folder="" )
Return
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
}
MsgBox, 64, Empty Folders Purged : %Del%, %RFL%
This can be adapted to fit the example from Edit2 and you should be good to go!

How do I retrieve the filename of the current script in AutoHotkey?

I would like to retrieve the filename of the currently running script in AutoHotkey and save it to a variable.
For example, if my script were named sample-script.ahk, I would like to display a message saying "The current script is sample-script.ahk".
How do I retrieve the filename of the currently active script in AutoHotkey?
Use: A_ScriptName The filename of the current script, without its path, e.g. MyScript.ahk.
MsgBox, The current script is %A_ScriptName%
See: https://autohotkey.com/docs/Variables.htm#BuiltIn
and more particularly, https://autohotkey.com/docs/Variables.htm#ScriptName

Displaying List of AutoHotkey Hotkeys

I’ve written script that contains numerous hotkeys (general structure is as below). I would like to create another one that when pressed displays a list of all of the hotkeys and their corresponding descriptions that the script contains in a nice, formatted table.
The formatting and display are tenuous since AutoHotkey’s output is limited to message-boxes, but possible. More problematic is getting the hotkeys and corresponding descriptions.
The hotkeys all call the same function with different arguments. I considered adding a variable to the function so that depending on the value, the function either performs the normal function when triggered by the normal hotkeys, or builds a string or something when triggered from the special display hotkey.
I cannot figure out a way to programmatically access the script’s hotkeys at all. I checked the docs and there don’t seem to be any A_ variables that can be used for this purpose, nor does the Hotkey command lend itself well (it can be used to test if a hotkey exists, but looping through the innumerable combinations is, at best, tedious).
Failed attempts:
I tried using Elliot’s suggestion of parsing the script itself (replacing the path with %A_ScriptFullPath%, and while it does work for a raw script, it does not when the script is compiled
I tried assigning the entire hotkey section of the script to a variable as a continuation section and then parsing the variable and creating hotkeys using the Hotkey command. This worked well right up until the last part because the Hotkey command cannot take arbitrary commands as the destination and requires existing labels.
The ListHotkeys command is not applicable because it only displays the hotkeys as plain text in the control window.
Does anyone know how I can display a list of the hotkeys and either their corresponding arguments or comments?
Example script:
SomeFunc(foobar)
{
MsgBox %foobar%
}
!^#A::SomeFunc("a") ; blah
^+NumpadMult::SomeFunc("c") ; blivet
^+!#`::SomeFunc("b") ; baz
^#Space::SomeFunc("d") ; ermahgerd
…
Example desired “outputs”:
C+A+ W+ A a | C+ S+ NumpadMult b
------------------+----------------------
C+A+S+W+ ` c | C+ W+ Space d
    or
Ctrl Alt Shift Win Key Action
-----------------------------------------
× × × A blah
× × NumpadMult baz
× × × × ` blivet
× × Space ermahgerd
etc.
The only thing I can think of is to read each line of your script individually and parse it. This code reads your script (script.ahk) one line at a time and parses it. This should get you started. Additionally, you could parse the line to check for the modifiers as well.
Loop
{
FileReadLine, line, C:\script.ahk, %A_Index%
if ErrorLevel
break
If Instr(line, "::")
{
StringSplit, linearray, line, ::,
key := linearray1
StringSplit, commandarray, linearray3, `;
action := commandarray2
hotkeyline := "key: " . key . "`tAction: " . action
final .= hotkeyline . "`r"
}
}
msgbox % final
return
I found a solution. It is not perfect (or ideal), and hopefully a proper, built-in method will become available in the future, but it works well (enough) and for raw and compiled scripts.
What I did was to use the FileInstall command which tells the compiler to add a file to the executable (and extract it when run).
Sadly, the FileInstall command will not allow the use of variables for the source file, so I cannot simply include the script itself (FileInstall, %A_ScriptFullPath%, %A_Temp%\%A_ScriptName%, 1).
As a work-around, I ended up extracting all of the desired hotkeys to a second file which I then parse as Elliot suggested, then delete, and #Include at the end of my script (it must be at the end since hotkeys will terminate the autoexecute section).
;;;;; Test.ahk ;;;;;
; Add hotkey file to executable and extract to Temp directory at runtime
FileInstall, Hotkeys.ahk, %A_Temp%\Hotkeys.ahk, 1
Loop
{
;Read a line from the extracted hotkey script and quit if error
FileReadLine, line, %A_Temp%\Hotkeys.ahk, %A_Index%
if ErrorLevel
break
;Trim whitespace
line=%line%
; Parse the line as in Elliot’s answer, but with tweaks as necessary
ParseHotkey(line)
…
}
FileDelete, %A_Temp%\Hotkeys.ahk ; Delete the extracted script
DisplayHotkeys() ; I ended up bulding and using a GUI instead
#Include, Hotkeys.ahk ; It is included at compile-time, so no A_Temp
;;;;; Hotkeys.ahk ;;;;;
z::MsgBox foo
y::MsgBox bar