How would you automate the process of shortening one-liners?
-
Friday, February 01, 2013 4:19 PM
Let's say for example you have a few one-liners that you have in your script.
$p = Get-Process -Name notepad -Verbose $p | Stop-Process
I would like to have a custom function that would convert it to something like the following:
$p = gps -n notepad -v $p | spps
All Replies
-
Friday, February 01, 2013 4:23 PMModerator
Why? I generally don't recommend using aliases in scripts as there's no guarantee that all aliases will be identical in everyone's PowerShell. For example, I removed the standard default alias 'dir' (get-childitem) and run my own custom 'dir.ps1' script instead. IMO scripts should always use full cmdlet names (for maximum robustness).
Bill
- Marked As Answer by MichaelLWest Friday, February 01, 2013 10:30 PM
-
Friday, February 01, 2013 4:29 PMModerator
Personally, I try to never use aliases. For your own use I guess it's OK, but certainly it does not make the script any more efficient or faster, and it makes it more difficult to understand and troubleshoot (if not for you, for others).
My guess is that a function for this would need to look for every possible parameter and substitute the alias.
Richard Mueller - MVP Directory Services
-
Friday, February 01, 2013 4:59 PM
Bill and Richard are completely correct.
In any case, one-liners and aliases are things more suitable to interactive use. I mean, who types the name of the Get-Childitem cmdlet at the console prompt instead of GCI or TYPE?
that said, the two one-liners given by the OP are really two parts of a single one-liner one might run interactively as:
gps -n notepad -v | Spps
Of course, storing the result of the gps command in a variable might be useful in making sure one is stopping the intended processes. But that would require a third command:
$a = gps -n notepad -v $a $a | spps
In a similar situation, I do the same thing (interactively) with these two commands:
gps -n notepad -v gps -n notepad -v | Spps
but even then I still type "gps -n notepad -v" only once, and enter the second command by tapping the up arrow key, then typing " | Spps"
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Friday, February 01, 2013 5:15 PM
A few more comments on the OP's apparent interest in "optimizing" script code:
Most attempts to reduce line lengths and/or line counts in script do so at the expense of understandability and maintainability, let alone the time spent tweaking code for no practical result.
The main purpose of script code is to translate the scripter's intent into something that the scripting engine can execute accurately, with a minimum of rewrites and time spent debugging. For that purpose, one's principal goal should be clarity - even when that means increasing the line count, and possibly the line length.
I recently saw a solution for a scripting question about if-else-endif structures given in this form:
if (<condition>) {#do if true} else {#do if false}certainly not incorrect, however what if either or both of the two code blocks need to be increased in size? I ALWAYS code conditionals with the control structures on their own lines:
if (<condition>) { #do if true } else { #do if false }In my opinion, this is no harder to follow and understand than the earlier example, and no less efficient to run. And when it comes to adding the code that does the actual work, the editing changes are trivial, and the result easy to understand on its own or to relate to the initial draft boilerplate code.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Friday, February 01, 2013 5:26 PM
A one-liner is not just a command on oneline. It is a complete soloution in a pipeline and has nothing to do with using aliases or with making the codeshorter.
There is not good reason to force a one line solution except that it allows us to use a pipeeloinne. Your examples are not one-liners and have nothing to do with one-liners.
Neither of these is one-liner:
$p = Get-Process -Name notepad -Verbose
$p | Stop-ProcessThe following is:
Get-Process -Name notepad -Verbose | Stop-Process
Note the use of the pipeline.
As noted above aliases are useful interactively but can cause issues in a script.
The number of characters in a command has little or nothing to do with efficiency or performance.
¯\_(ツ)_/¯
-
Friday, February 01, 2013 10:43 PM
I appreciate the feedback from everyone. I should have been more clear on why I wanted to "minify" the one-liner. I currently do follow the practice of no using aliases in my scripts. I was trying to come up with a quick way to squeeze a script into the command line parameter of a scheduled task, rather than trying to manage a script file.
The solution I ended with is converting a scriptblock into a base64 string and then pass that to the -encodedcommand switch. Below is an example:
$command = { Get-Process -Name notepad -Verbose | Stop-Process } $encodedCommand = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command)) | clip.exe powershell.exe -noprofile -encodedcommand CgAgACAARwBlAHQALQBQAHIAbwBjAGUAcwBzACAALQBOAGEAbQBlACAAbgBvAHQAZQBwAGEAZAAgAC0AVgBlAHIAYgBvAHMAZQAgAHwAIABTAHQAbwBwAC0AUAByAG8AYwBlAHMAcwAKAA==Also, thank you to all who corrected me on what is a "one-liner."
-
Friday, February 01, 2013 10:50 PM
I appreciate the feedback from everyone. I should have been more clear on why I wanted to "minify" the one-liner. I currently do follow the practice of no using aliases in my scripts. I was trying to come up with a quick way to squeeze a script into the command line parameter of a scheduled task, rather than trying to manage a script file.
The solution I ended with is converting a scriptblock into a base64 string and then pass that to the -encodedcommand switch. Below is an example:
$command = { Get-Process -Name notepad -Verbose | Stop-Process } $encodedCommand = [System.Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($command)) | clip.exe powershell.exe -noprofile -encodedcommand CgAgACAARwBlAHQALQBQAHIAbwBjAGUAcwBzACAALQBOAGEAbQBlACAAbgBvAHQAZQBwAGEAZAAgAC0AVgBlAHIAYgBvAHMAZQAgAHwAIABTAHQAbwBwAC0AUAByAG8AYwBlAHMAcwAKAA==Also, thank you to all who corrected me on what is a "one-liner."
What you are trying to do has nothing to do with one-liners. An encoded string is likely longer trhan teh original command.
Here is how to get a scriptblock in a schedules task.
powershell.exe -noprofile -command {Get-Process -Name notepad -Verbose | Stop-Process}
That is all it takes.
To put it on two lines:
powershell.exe -noprofile -command {$p=Get-Process -Name notepad ; Stop-Process $p}
The verbose parameter is useless in a scheduled task.
¯\_(ツ)_/¯
-
Friday, February 01, 2013 10:55 PM
The command contained the code for the question I posted. The issue I was having came with using single/double quotes in the script. The powershell.exe documentation recommends encoding for this reason. I want to uninstall VNCViewer without hitting the registry and when the path is unknown. There are many solutions, and so here is just one (using aliases, sorry):
$command = { $exe='VNCViewer.exe';$uninstexe='Unins000.exe';@((gci ("$($env:SystemRoot)\") $exe -r) + (gci ("$($env:ProgramFiles)\") $exe -r) + (gci ("$(${env:ProgramFiles(x86)})\") $exe -r)) | % { if($_) { start (Join-Path $_.Directory.FullName $uninstexe) -a '/silent','/verysilent' } else { exit -1 } }; }
-EncodedCommand Accepts a base-64-encoded string version of a command. Use this parameter to submit commands to Windows PowerShell that require complex quotation marks or curly braces.
- Edited by MichaelLWest Friday, February 01, 2013 10:58 PM
-
Friday, February 01, 2013 11:05 PM
personally, I find it much simpler to "manage a script file" than to "manage a sequence of powershell commands in the single line field available in a scheduled task", whether or not it is encoded. I don't care that there is enough space available to store a very long set of powershell commands, except that that makes it even more difficult to "read" the code. If (or, more likely, when) it fails, how will you interpret the error messages and examine your script for the possible causes?
And if you maintain a clear-text copy to process with the script you are hoping to write to create that obfuscated command, are you not already "managing a script file", or even two?
Like everyone else here I fail to see what advantage you see in this.
Al Dunbar -- remember to 'mark or propose as answer' or 'vote as helpful' as appropriate.
-
Friday, February 01, 2013 11:08 PMModerator
A Base64 encoded string is always at least one third again longer than the original string.
Richard Mueller - MVP Directory Services
-
Saturday, February 02, 2013 5:58 PM
Here is an example of how to do what I originally asked. Does not take into account custom aliases but gets the job done.
function Compress-Alias { [CmdletBinding()] param( [Parameter(Position=0, Mandatory=$true, ParameterSetName="File")] [string]$Path, [Parameter(Position=0, Mandatory=$true, ParameterSetName="Text")] [string]$Script ) if($PSCmdlet.ParameterSetName -eq "File") { if(Test-Path -Path $Path){ $Script = (Get-Content -Path $Path -Delimiter ([char]0)) } } $cmdlets = Get-Command | Where-Object {$_.CommandType -eq "Cmdlet"} | Select-Object Name foreach($cmdlet in $cmdlets) { $cmd = $cmdlet.name; if($Script -match "\b$cmd\b") { $alias = @(Get-Alias | Where-Object {$_.Definition -eq $cmd}); $Script = $Script -replace($cmd,$alias[0]) } } $Script }
-
Saturday, February 02, 2013 6:32 PM
As noted by all above; this is not necessarily a good idea. Just using a file is much easier and easier to support down the line.
That said - your "reverse engineering" of aliases is interesting. Usually we use code to do the opposite which is to remove all aliases and all shorthand. You code seeks to do the opposite.
¯\_(ツ)_/¯

