Expand Command Line Arguments Autohotkey - windows-10

TLDR; I am trying to find an equivalent to bash $# in autohotkey
I am developing an autohotkey script which will run a specific program when triggered.
<^#i::Run D:\Scripts\myprog.exe "arg_1" "arg_2"
; Runs with whatever arguments I provide on pressing Ctrl + Win + I
I want the program to be started with specific command line arguments; that I provide during starting the script.
> ahk_prog.exe arg_1 arg_2
I basically want to be able to convert my command line arguments provided during starting the program to be able to run the Run Command every time I trigger it.
I tried this:
<^#i::Run D:\Scripts\myprog.exe %A_Args% ; A_Args is the array of CLI arguments
; Runs with whatever arguments I provide on pressing Ctrl + Win + I
I also tried this(this one does not compile at all)
<^#i::Run D:\Scripts\myprog.exe "%*%" ; Trying Batch Like syntax since %1% and %2% are valid
; Runs with whatever arguments I provide on pressing Ctrl + Win + I
Both of them do not work. Is there a way to do this?

A_Args(docs) is an array, so you'll have join it into a string.
For example:
for each, arg in A_Args
arg_list .= """" arg """ "
arg_list := RTrim(arg_list) ;trim trailing space (might be unnecessary)
MsgBox, % arg_list
Added quotes around the argument, since they will of course be needed if your argument has spaces in it.
Then you can add it to your Run command like so:
for each, arg in A_Args
arg_list .= """" arg """ "
arg_list := RTrim(arg_list) ;trim trailing space (might be unnecesary)
<^#i::Run, % "D:\Scripts\myprog.exe " arg_list
Could also be done as a one-liner like so:
<^#i::Run, % "D:\Scripts\myprog.exe " RTrim(Format(StrReplace(Format("{:0" A_Args.length() "}", ""), 0, """{}"" "), A_Args*))

Related

PowerShell, getting string syntax correct in 7-zip script

I can't seem to solve this, been struggling with it for a while (maybe it's simple; I just can't see it as I've been looking at it for so long).
I can get the 7z.exe syntax to work, but when I try and put it together into a simple script, it fails.
e.g., if I run .\zip.ps1 "C:\test\test.zip" "C:\test\test1.txt" "C:\test\test2.txt*
Instead of zipping up the 2 files required, it zips up everything in the C:\test folder, completely ignoring my arguments.
How can I adjust the below string syntax so that 7z.exe will correctly respect the input arguments and compress them from within a PowerShell script?
"Received $($args.Count) files:"
$parent = Split-Path (Split-Path $args[0]) -Leaf
$sevenzip = "C:\Program Files\7-Zip\7z.exe"
$zipname = "$($parent) $(Get-Date -Format 'yyyy_MM_dd HH_mm_ss').zip"
$args_line = $args | foreach-object { "`"$_`"" }
# $args_line = '"' + $($args -join """ """) + '"' # Want to use """ here so that it can capture not only files with spaces, but files with ' in the filename
''
"Zip Name : $zipname"
''
"Arguments : $args_line"
''
if (Test-Path $sevenzip) {
if (Test-Path "C:\0\$zipname") { rm "C:\0\$zipname" -Force }
''
'String output of the line to run:'
"& ""$sevenzip"" a -r -tzip ""C:\0\$zipname"" $args_line" # Taking this output and pasting onto console works.
''
& "$sevenzip" a -r -tzip "C:\0\$zipname" "$args_line" # This does not work
} else {
"7z.exe was not found at '$sevenzip', please check and try again"
}
The error that I get is:
Files read from disk: 0
Archive size: 22 bytes (1 KiB)
Scan WARNINGS for files and folders:
1 : The system cannot find the file specified.
----------------
Pass $args directly to your & "$sevenzip" call:
& "$sevenzip" a -r -tzip "C:\0\$zipname" $args
This makes PowerShell pass the array elements as individual arguments, automatically enclosing them in "..." if needed (based on whether they contain spaces).
Using arrays as arguments for external programs is in effect an implicit form of array-based splatting; thus, you could alternatively pass #args.
Generally, note that in direct invocation[1] you cannot pass multiple arguments to an external program via a single string; that is, something like "$args_line" cannot be expected to work, because it is passed as a single argument to the target program.
If you want to emulate the resulting part of the command line, for display purposes:
($argListForDisplay = $args.ForEach({ ($_, "`"$_`"")[$_ -match ' '] })) -join ' '
Note:
Each argument is conditionally enclosed in "..." - namely based on whether it contains at least one space - and the resulting tokens are joined to form a single, space-separated list (string).
The assumption is that no argument has embedded " chars.
A simplified example:
& { # an ad-hoc script block that functions like a script or function
# Construct the argument list *for display*
$argListForDisplay = $args.ForEach({ ($_, "`"$_`"")[$_ -match ' '] }) -join ' '
#"
The following is the equivalent of:
Write-Output $argListForDisplay
"#
# Pass $args *directly*
# Simply prints the argument received one by one, each on its own line.
# Note: With PowerShell-native commands, generally use #args instead (see below).
Write-Output $args
} firstArg 'another Arg' lastArg # sample pass-through arguments
Output:
The following is the equivalent of:
Write-Output firstArg "another Arg" lastArg
firstArg
another Arg
lastArg
Note: Write-Output is used in lieu of an external program for convenience. Technically, you'd have to use #args instead of $args in order to pass the array elements as individual, positional arguments, but, as stated, this is the default behavior with externals programs. Write-Output, as a PowerShell-native command, receives the array as a whole, as a single argument when $args is used; it just so happens to process that array the same way as if its elements had been passed as individual arguments.
[1] You can use a single string as an -ArgumentList value for Start-Process. However, Start-Process is usually the wrong tool for invoking console applications such as 7z.exe - see this answer.

execute external program in lua without userinput as arguments in lua

I want to execute an external program in lua. Usually this can be done with
os.execute("run '"..arg0.."' 'arg1' arg2")
The problem with this approach is if I want to pass user input as string to an external program, user input could be '; evil 'h4ck teh system' ' and the script from above would execute like this:
/bin/bash -c "run ''; evil 'h4ck teh system' '' 'arg1' arg2"
Another problem occurs when I have '$var' as argument and the shell replaces this with its environment variable. In my particular case I have something like [[program 'set title "$My Title$"']] – so nested strings – and program parses "$My Title$" (with escape sequences) differently than '$My Title$' (as it is). Because I want to set the title as it, the best way is to have arguments like this: 'My Title'. But now the command have to be:
os.execute([[run "set title '$My Title$'"]])
But now – as I said – $My will be replaced with an empty string, because the environment does not know any variable named $My and because, I never wanted it to be replaced.
So I am looking for the usual approach with
execv("run", {"set title '"..arg0.."'", arg1, arg2})
local safe_unquoted = "^[-~_/.%w%%+,:#^]*$"
local function q(text, expand) -- quoting under *nix shells
-- "expand"
-- false/nil: $var and `cmd` must NOT be expanded (use single quotes)
-- true: $var and `cmd` must be expanded (use double quotes)
if text == "" then
text = '""'
elseif not text:match(safe_unquoted) then
if expand then
text = '"'..text:gsub('["\\]', '\\%0')..'"'
else
local new_text = {}
for s in (text.."'"):gmatch"(.-)'" do
new_text[#new_text + 1] = s:match(safe_unquoted) or "'"..s.."'"
end
text = table.concat(new_text, "\\'")
end
end
return text
end
function execute_commands(...)
local all_commands = {}
for k, command in ipairs{...} do
for j = 1, #command do
if not command[j]:match"^[-~_%w/%.]+$" then
command[j] = q(command[j], command.expand)
end
end
all_commands[k] = table.concat(command, " ") -- space is arguments delimiter
end
all_commands = table.concat(all_commands, ";") -- semicolon is commands delimiter
return os.execute("/bin/bash -c "..q(all_commands))
end
Usage examples:
-- Usage example #1:
execute_commands(
{"your/program", "arg 1", "$arg2", "arg-3", "~/arg4.txt"},
{expand=true, "echo", "Your program finished with exit code $?"},
{"ls", "-l"}
)
-- The following command will be executed:
-- /bin/bash -c 'your/program '\''arg 1'\'' '\''$arg2'\'' arg-3 ~/arg4.txt;echo "Your program finished with exit code $?";ls -l'
$arg2 will NOT be expanded into value because of single quotes around it, as you required.
Unfortunately, "Your program finished with exit code $?" will NOT be expanded too (unless you explicitly set expand=true).
-- Usage example #2:
execute_commands{"run", "set title '$My Title$'", "arg1", "arg2"}
-- the generated command is not trivial, but it does exactly what you need :-)
-- /bin/bash -c 'run '\''set title '\''\'\'\''$My Title$'\''\'\'' arg1 arg2'

escape a whole array of arguments for use in sh-like shells with a standard tool

I'm looking for a standard tool capable of taking all of its arguments and turning it into a single string suitable for use as multiple arguments in an automatically generated bash/sh/zsh script. Such a command is extremely useful in various disciplines of script-fu. An example of its usage:
% shsafe 'A big \nasty string '\'' $HOME $PATH' 'another string \\'
'A big \nasty string '\'' $HOME $PATH' 'another string \\'
Using it in another script:
% sshc host rm 'file/with spaces and $special chars'
where sshc contains
#!/bin/bash
# usage: sshc host command [arg ...]
# Escapes its arguments so that the command may contain special
# characters. Assumes the remote shell is sh-like.
host=$1
shift
exec ssh "$host" "$(shsafe "$#")"
Another example:
#!/bin/bash
# Run multiple commands in a single sudo session. The arguments of
# this script are passed as arguments to the first command. Useful if
# you don't want to have to type the password for both commands and
# the first one takes a while to run.
sudo bash -c "pacman -Syu $(shsafe "$#") && find /etc -name '*.pacnew'"
I couldn't find a suitable solution to this problem in the pre-existing commands, so I made up my own, called shsafe. It uses the fact that single quotes, '', turn off absolutely all shell expansion, except for ' itself.
shsafe:
#!/usr/bin/env python
from sys import *
n = len(argv)
if n == 1:
exit(0)
i = 1
while True:
stdout.write("'" + argv[i].replace("'", "'\\''") + "'")
i += 1
if i == n:
break
stdout.write(' ')
stdout.write('\n')
Is there any standard tool capable of doing this to its arguments?
Note that the printf command with a format string consisting of just the %q formatter is not good enough for this, because it won't keep multiple arguments separated:
% printf %q arg1 arg2
arg1arg2
I did eventually figure out a decent way of doing this:
% printf "$'%q' " 'crazy string \ $HOME' 'another\ string'
$'crazy\ string\ \\\ \$HOME' $'another\\\ string'
It's a little error prone what with the quotes everywhere, so it's not ideal, IMO, but it's a solid solution that should work anywhere. If it's being used a lot, you could always turn it into a shell function:
shsafe () {
printf "$'%q' " "$#"
}

getting raw command line arguments in node.js

How can I get the raw command line arguments in a node.js app,
given this example command:
node forwardwithssh.js echo "hello arguments"
process.argv will be [ "node", "/path/to/forwardwith.js", "echo", "hello arguments" ]
And there's no way to reconstruct the original echo "hello arguments" from that (ie. join(" " won't put quotes back).
I want the whole original raw string after the script name.
what I want is easily obtained in bash scripts with "$*", is there any equivalent way to get that in node.js?
Note: the intention in particular is to receive a command to be executed somewhere else (eg. thru ssh)
Wrap each of the args in single quotes, and escape and single quotes within each arg to '\'':
var cmd_string = process.argv.map( function(arg){
return "'" + arg.replace(/'/g, "'\\''") + "'";
}).join(' ');
That would give you a cmd_string containing:
'node' '/path/to/forwardwith.js' 'echo' 'hello arguments'
which can be run directly in another shell.

Escaping "%" in Vim when shelling out, so it's not expanded to filename?

Say I have this vimscript as "/tmp/example.vim":
let g:input = "START; % END"
exec("! clear && echo " . shellescape(g:input))
If I open that file and run it with :so %, the output will be
START; /tmp/example.vim END
because the "%" is expanded to the buffer name. I want the output to be
START; % END
I can use the generic escape() method to escape percent signs in particular. This works:
let g:input = "START; % END"
exec("! clear && echo " . escape(shellescape(g:input), "%"))
But is that really the best way? I'm sure there're more characters I should escape. Is there a specific escape function for this purpose? Or a better way to shell out?
For use with the :! command, you need to pass the optional {special} argument to shellescape():
When the {special} argument is present and it's a non-zero
Number or a non-empty String (|non-zero-arg|), then special
items such as !, %, # and <cword> will be preceded by
a backslash. This backslash will be removed again by the |:!|
command.
:exec("! clear && echo " . shellescape(g:input, 1))
You need to properly escape the '%'. So it should be:
let g:input = "START; \\% END"
This seems to do it:
let g:input = "START; % END"
echo system("echo " . shellescape(g:input))
It should be noted I don't really care about the output; I'll use this with silent in a larger script.

Resources