Pass in a command string to AutoHotkey - powershell

I want to run a single AutoHotkey command. A script seems kindof overkill.
In bash and powershell, you can run a command by passing it in as a string to the shell:
pwsh -Command ls
bash -c ls
Is there a way to do this with AutoHotKey.exe? In the documentation, all I see is that you can pass the name of a script file to execute. If powershell supported process substitution <(ls), I could do
AutoHotKey.exe <(echo "ls")
But I don't think there's a way to do this in powershell.
Is there another way other than creating a complicated version of process substitution myself?

The linked docs state:
[v1.1.17+]: Specify an asterisk (*) for the filename to read the script text from standard input (stdin). For an example, see ExecScript().
For instance, from PowerShell:
'MsgBox % "Hello, world."' | AutoHotKey.exe *

I'm not sure if you're looking for what the other answer states, or then for this what I'm about to write:
You can pass arguments into an AHK script and those arguments are then found from the built in variable A_Args.
Example AHK script:
for each, arg in A_Args
output .= arg "`n"
MsgBox, % output
PowerShell command:
& "C:\Path\To\My\Script.ahk" arg1 arg2 "this is the 3rd argument" arg4
This would be if you have AHK installed. If you have some portable AHK setup, you'd pass in the example script to AutoHotkey.exe and then the desired arguments.

Related

How to run Bash commands with a PowerShell Core alias?

I am trying to run a Bash command where an alias exists in PowerShell Core.
I want to clear the bash history. Example code below:
# Launch PowerShell core on Linux
pwsh
# Attempt 1
history -c
Get-History: Missing an argument for parameter 'Count'. Specify a parameter of type 'System.Int32' and try again.
# Attempt 2
bash history -c
/usr/bin/bash: history: No such file or directory
# Attempt 3
& "history -c"
&: The term 'history -c' is not recognized as the name of a cmdlet, function, script file, or operable program.
It seems the issue is related to history being an alias for Get-History - is there a way to run Bash commands in PowerShell core with an alias?
history is a Bash builtin, i.e. an internal command that can only be invoked from inside a Bash session; thus, by definition you cannot invoke it directly from PowerShell.
In PowerShell history is an alias of PowerShell's own Get-History cmdlet, where -c references the -Count parameter, which requires an argument (the number of history entries to retrieve).
Unfortunately, Clear-History is not enough to clear PowerShell's session history as of PowerShell 7.2, because it only clear's one history (PowerShell's own), not also the one provided by the PSReadLine module used for command-line editing by default - see this answer.
Your attempt to call bash explicitly with your command - bash history -c - is syntactically flawed (see bottom section).
However, even fixing the syntax problem - bash -c 'history -c' - does not clear Bash's history - it seemingly has no effect (and adding the -i option doesn't help) - I don't know why.
The workaround is to remove the file that underlies Bash's (persisted) command history directly:
if (Test-Path $HOME\.bash_history) { Remove-Item -Force $HOME\.bash_history }
To answer the general question implied by the post's title:
To pass a command with arguments to bash for execution, pass it to bash -c, as a single string; e.g.:
bash -c 'date +%s'
Without -c, the first argument would be interpreted as the name or path of a script file.
Note that any additional arguments following the first -c argument would become the arguments to the first argument; that is, the first argument acts as a mini-script that can receive arguments the way scripts usually do, via $1, ...:
# Note: the second argument, "-", becomes $0 in Bash terms,
# i.e. the name of the script
PS> bash -c 'echo $0; echo arg count: $#' self one two
self
arg count: 2

Why is an unwanted empty line output on running a PowerShell script from within a Windows batch file?

When I execute the following hello.ps1 in PowerShell, I get an output like I would expect, a Hello World! with a newline.
#powershell script hello.ps1
"Hello World!"
Output of run
PS C:\test>.\hello.ps1
Hello World!
PS C:\test>
But when I call the script from a bat file, I get two (2) newlines output in Windows console.
:: Windows CMD script name: mytest.bat
powershell -ExecutionPolicy Bypass hello.ps1
Now the output has two newlines, note the empty line after Hello World!.
C:\test>mytest.bat
Hello World!
C:\test
How can I avoid this second newline?
Mandragor -
Firstly, that is not two lines, it's just one.
Secondly, this is not a PowerShell specific thing.
You are calling one shell from another, and both must provide you a response and return to interactive mode. Calling an external command for one shell to another must be processed by the called shell first and results returned from the called shell to the calling shell, then exit the called process, and that calling shell must complete its process and any stdout stuff, and exit that process.
Double-clicking a bat/cmd file, starts cmd.exe, task manager shows
that process is running
In your bat/cmd you are calling another standalone exe, task manager
shows that the process is running. It must execute and return stdout
results, then close/exit.
cmd.exe the completes, and returns its stdout stuff, if any, and places the cursor at the next line for more interactive work.
Hence, the two responses, only if you ask for cmd.exe stdout stuff will you see it, vs just null.
You'll always have one more line return to get you back to the calling process.
Also, point of note:
Write-Host except in specific circumstances, and what you are doing
is not one of them.
You only need that bypass policy setting if you are in a restricted
environment. RemoteSigned is now the PowerShell default, meaning all local scripts will run, and remote scripts must be signed.
Simple strings should use single quotes in most cases. Double quotes
are for expanding variable content and other formatting scenarios.
Try the same thing calling any other shell, you'll get the same results. Heck, even calling another bat/cmd from a bat/cmd.
Example:
Running mytest.bat in cmd.exe that only contains these two lines, to show that only 1 CRLF is actually returned, per process, then you get bat the interactive shell.
powershell -File D:\Temp\hello.ps1
call d:\temp\hello.bat
# Results
Hello World!
D:\Temp>call d:\temp\hello.bat
D:\Temp>echo 'Hello World!'
'Hello World!'
D:\Temp>
You can change your powershell script like this:
#powershell script hello.ps1
if ($args[0] -eq "cmd") {
write-host "Hello World!" -nonewline
}
else {
"Hello World!"
}
Then change the caller batch file:
#echo off
powershell -executionpolicy Bypass TheNameOfPowershellScript.ps1 "cmd"
Now when run from cmd use the caller batch file and from powershell directly use the ps1 script. It will give the expected result(Tested).

Run output of command as powershell incantation

I have a bash script that generates a program incantation (docker run ... with a bunch of arguments) and prints it to standard output. I want to run the output from powershell, with the optional possibility to tack on other arguments afterwards.
I've tried
D:\> & $(wsl ./my-bash-script.sh) some more args
but I get the error
The term 'docker run --rm -ti [<redacted>]' is not recognized as the name of cmdlet, function, script file, or operable program. Check the spelling of the name, or ifa path was included, verify that the path is correct and try again.
Apparently, & interprets the entire output as a single command, when in fact it's only the first word of the output that is a command; the rest is arguments.
Is there a way to do what I want?
Yes, & only supports a command name or path as its first argument - you cannot include arguments.
What you need is Invoke-Expression:
Invoke-Expression "$(wsl ./my-bash-script.sh) some more args"
Note: You'd only need & inside the string if the .sh script output a command with a quoted executable path.
Also note that PowerShell will not always interpret a given command line the same way as a POSIX-like shell (such as Bash), given that it recognizes additional metacharacters at the start of arguments; notably, arguments prefixed with # or { will not be taken as literals by PowerShell.
As an aside: While this is a legitimate use case for Invoke-Expression - assuming you trust the output from your .sh script - Invoke-Expression should generally be avoided.

Is there a way to have a readable SINGLE cmd/powershell script?

In olden days, I remember a trick we used to use to run Perl scripts within Windows cmd.exe as a simple invocation of a cmd file rather than having to run perl.exe with the script name, something like:
#rem = '
#perl -x -S %0 %*
#goto :eof
#rem ';
<insert Perl script here>
This used the rather neat trick of exploiting differences in how cmd.exe and perl.exe would handle the input script. Windows' cmd.exe would read the first three lines as three separate commands which would:
have a comment with no echo;
run perl with the same input file and arguments, without echo; and
goto the end of the file (exit) with no echo.
On the other hand, perl.exe would treat the first four lines as an assignment statement, then go on to execute the Perl script proper.
Now it's often a pain to run Powershell scripts from the cmd.exe command line since you have to use something like:
powershell -file go.ps1
I'm wondering if there's a way to encode both batch and powershell commands into a single cmd file, similar to the Perl trick, in such a way that it starts running under cmd.exe but switches to Powershell quickly after that.
If that were possible, you could run your script go.cmd simply by entering:
go
at the command line, rather than some convoluted invocation of powershell.
I know that you can ship separate cmd and ps1 files but that's in fact what I'm trying to get away from. I'm looking for a single file solution if possible.
I also know that you can base-64 encode your script or execute it as a string, provided you replace all newlines with semicolons. But that means the Powershell stuff is no longer easily editable or readable in the resulting file.
Fortunately or otherwise, Powershell knows exactly what to do with .cmd files: use CMD to run them.
#set $a=%0 && powershell -encodedcommand ZwBjACAAKAAoAGQAaQByACAAZQBuAHYAOgBgACQAYQApAC4AdgBhAGwAdQBlACAALQByAGUAcABsAGEAYwBlACAAJwAiACcALAAnACcAKQAgAHwAIABzAGUAbABlAGMAdAAgAC0AcwBrAGkAcAAgADIAIAB8ACAAcABvAHcAZQByAHMAaABlAGwAbAAgAC0A
#exit /b
"Hello from Powershell! You know it's me because cmd would never know that 2 + 2 = $(2 + 2)!"
The encoded command is
gc ((dir env:`$a).value -replace '\"','') | select -skip 2 | powershell -
Which fetches the contents of the script invoking itself, skips the stuff intended for CMD and, yes, runs Powershell on the rest.
Since we're piping commands through stdin and not having them in a proper script file there may be weirdness for more complicated scripts, doubly so if you start nesting these hybrids. I wouldn't trust this in production, but hey, it's something.

How do I call exec in psake to an executable with a variable path?

I can't seem to call this executable correctly in my psake deploy script.
If I do this:
exec { "$ArchiverOutputDir\NServiceBus.Host.exe /install" }
It simply outputs this (and is clearly not calling the executable - just outputting the value of that expression):
c:\ReloDotNet2_ServiceEndpoints\Archiver\NServiceBus.Host.exe /install
But if I do this:
exec { c:\ReloDotNet2_ServiceEndpoints\Archiver\NServiceBus.Host.exe /install }
I get the expected output from the executable.
How do I correctly call an executable with a variable in the path to the executable in psake? If this is actually a PowerShell issue, please feel free to correct the question to reflect that insight.
I
Classic PowerShell issue. Try this instead:
exec { & "$ArchiverOutputDir\NServiceBus.Host.exe" /install }
PowerShell not only executes commands, it also evaluates expressions e.g.:
C:\PS> 2 + 2
4
C:\PS> "hello world"
hello world
What you have given to PowerShell at the beginning of a pipeline is a string expression which it faithfully evaluates and prints to the console. By using the call operator &, you're telling PowerShell that the following thing is either the name of a command (in a string) to be executed or a scriptblock to be executed. Technically you could also use . "some-command-name-or-path". The only difference is that for PowerShell commands, & creates a new scope to execute the command in and . doesn't. For external exes it makes no difference as far as I can tell which one you use but & is typically used.