So I learned how to run a ps1 file from kotlin here, but the only purpose I have for that file is to run the commands, then delete itself. Is there any way to remove the hassle of creating a file that later will just be deleted?
I have a one lined string of commands, each separated by a semicolon.
Edit
Some people asked for the powershell code, here it is:
(I split it into lines, but really it's just one line)
The dollar signs without backslashes in front of them are references to variables in kotlin.
"\$ComObj = New-Object -ComObject WScript.Shell; " +
"\$ShortCut = \$ComObj.CreateShortcut(\"\$Env:${shortcut.absolutePath}\"); " +
"\$ShortCut.TargetPath = \"%windir%\\system32\\cmd.exe\"; " +
"\$ShortCut.Description = \"${f.nameWithoutExtension}\"; " +
"\$ShortCut.Arguments = \"/c \"\"start $url\"\"\"; " +
"\$ShortCut.FullName; \$ShortCut.WindowStyle = 7; " +
"\$ShortCut.IconLocation = \"$iconpath\"; " +
"\$ShortCut.Save(); " +
"Remove-Item \"${createPs.absolutePath}\"; "
(The code is written to a file before being run, something that I dont want)
If you can put your script in a variable and the variable will expand upon calling PowerShell you could do it this way
Runtime.getRuntime().exec("powershell.exe -command '\$yourvariable'")
Of course you'll need to experiment with the right escape chars to get quotes around the command you want to run.
I've had the most success with this type of thing by first running it in CMD.exe and figuring out the proper syntax. Then take it to your program. That way you'll know you get the PowerShell syntax down correctly first. From what I understand parsing for PowerShell is all done after the engine starts up. So when you call PowerShell in this manner you have to get the syntax just right.
Related
How to run a ps1 file in foreground?
I noticed when I execute my ps1 file, instead of view the log of the ps1 file execution, a Background job is started.
Is there anyway to run a ps1 file and get the same behavior we have when executing a sh or batch file?
Updates:
My ps1 file:
$scratchOrgName=$args[0]
Write-Host "Hello " & $scratchOrgName
ps1 file execution:
The & starts a new process. (It's called the background operator)
Change the code into something like
Write-Host "Hello" $scratchOrgName
or
Write-Host "Hello $scratchOrgName"
tl;dr
Unless you explicitly request that commands be run in the background (as you accidentally did, see next section), PowerShell commands do run in the foreground.
To achieve what you were (presumably) trying to do:
$scratchOrgName=$args[0]
"Hello $scratchOrgName"
Michaël Hompus' helpful answer provides the crucial pointers, but let me attempt a systematic overview:
Write-Host "Hello " & $scratchOrgName is composed of two statements:
Write-Host "Hello " & submits command Write-Host "Hello " as a background job, in PowerShell (Core) v6+ (in Windows PowerShell (v5.1-), you'd get an error, saying that & is reserved for future use). An object representing the newly created job is output and prints to the screen, as shown in your screenshot.
The post-positional use of & - i.e. placed after a command - is indeed the background operator, and is therefore equivalent to Start-Job { Write-Host "Hello " }
This contrasts with pre-positional use of &, which then acts as the call operator, for invoking command names or paths that are potentially quoted or contain / are variable values (e.g. & 'C:\Program Files\Node.js\node.exe')
$scratchOrgName - by virtue of PowerShell's implicit output behavior - outputs the value of that variable, which prints to the screen by default.
As for what you intended:
& is VBScript's string-concatenation operator; its PowerShell equivalent is +
A string-concatenation operation is an expression, and as such it must be enclosed in (...) in order to be passed as an argument to a command such as Write-Host.
Therefore, the direct PowerShell expression of your intent would be:
Write-Host ("Hello " + $scratchOrgName)
But, as also shown in Michaël's answer, this is more easily expressed via an expandable (double-quoted) string ("..."):
Write-Host "Hello $scratchOrgName"
Taking a step back: Write-Host is typically the wrong tool to use, unless the intent is to write to the display only, bypassing the success output stream and with it the ability to send output to other commands, capture it in a variable, or redirect it to a file.
To output a value, use it by itself, taking advantage of the aforementioned implicit output behavior (or use Write-Output, though that is rarely needed):
"Hello $scratchOrgName"
See this answer for more information.
Question about running a custom function in Powershell.
I'm on Windows 10 and I'd like to somehow print my monorepository's directory tree structure excluding node_modules. This is not supported out of the box but requires a custom function to be defined. I found one solution on StackOverflow (https://stackoverflow.com/a/43810460/9654273), which would enable using a command like:
tree -Exclude node_modules -Ascii > tree.txt
The problem is I don't know what to do with the provided source code :D The answer says "add to your $PROFILE, for instance", so I ran notepad $PROFILE in PowerShell, pasted the code snippet there, saved it and tried running the command. It didn't work because I did something wrong. According to the StackOverflow post's comments from anand_v.singh and mklement0 I was still running some other tree command, not the one I just attempted to define.
So how do I use a custom function in PowerShell? Starting point is that source code is on StackOverflow and I don't know where to paste it. Or do you know some other, easier way to print a directory tree on Windows 10 excluding node_modules?
I had the same problem with that function. The issue is the special characters in the hashtable at line 106:
$chars = #{
interior = ('├', '+')[$ndx]
last = ('└', '\')[$ndx] #'
hline = ('─', '-')[$ndx]
vline = ('│', '|')[$ndx]
space = ' '
}
I changed the special characters to ascii as follows:
$chars = #{
interior = ('+', '+')[$ndx]
last = ('\', '\')[$ndx] #'
hline = ('-', '-')[$ndx]
vline = ('|', '|')[$ndx]
space = ' '
}
The only downside is that you do not now have the option of using special graphics characters (the Ascii switch is still there, but does nothing). Maybe someone could tell us how to embed them properly.
I'm trying to make a system which turns the contents of a .txt file into a variable. This isn't my problem, though; for some reason, my files are reading characters I didn't enter, and can't use.
Please note: What I'm doing is in no way efficient, and I'm positive there are other ways to go about this, but this is the best way for me. Also, I'm not amazingly intelligent when it comes to coding. Just thought I'd throw that out there.
First, let me show you the system I have in place.
value.txt
4
This file has the contents which I'd like to make into a variable.
Batch Files
setcmdvar.bat
set cmdvar=
I leave this empty so that I can put the contents of value.txt at the end (more on this later).
start.bat
#echo off
call PowerShell.exe cd "C:\Users\%username%\Desktop\Folder"; $PSvar = Get-Content value.txt; $PSvar >> setcmdvar.bat
pause
call setcmdvar.bat
pause
echo The variable equals %cmdvar%.
pause
exit
The second line from start.bat creates this script in PowerShell:
PowerShell script
cd "C:\Users\%username%\Desktop\Folder\"
$PSvar = Get-Content value.txt; $PSvar >> setcmdvar.bat
This creates a variable in PowerShell, $PSvar, which equals the contents of value.txt; in our case, 4. Then, it puts $PSvar (4) at the end of setcmdvar.bat, using >>, which changes it to:
setcmdvar.bat (changed)
set cmdvar=4
Or, at least, it should, to my knowledge. Instead, it changes the file to this:
set Items=桔瑳楲杮椠業獳湩桴整浲湩瑡牯›⸢ †⬠䌠瑡来牯䥹普††††㨠倠牡敳䕲牲牯›㨨
嵛慐敲瑮潃瑮楡獮牅潲割捥牯䕤捸灥楴湯 †⬠䘠汵祬畑污晩敩䕤牲牯摉㨠吠牥業慮潴䕲灸捥整䅤䕴摮晏瑓楲杮
Or some other strange combination of characters. I looked one up, and it was Chinese. There's also some other characters like †, ⬠, ›, ⸢,
, and . I have no idea why these are being typed. Along with this, start.bat displays the following:
Press any key to continue . . .
(PowerShell script runs here)
'■s' is not recognized as an internal or external command,
operable program or batch file.
Press any key to continue . . .
The variable equals .
Press any key to continue . . .
(exit)
I did not type "■s," and I assume this may be the problem. Whatever it is, does anyone have any ideas?
P.S. I'm sorry if my code is complicated, or if it looks bad, but I have it this way for a reason. Mostly my incompetence, actually. But I think it's better that way.
Also, I know there are commands like for /f "delims=" %a in ('ver') do #set foobar=%a (I just took this off the internet) but I've tried commands like those, and I suppose I just don't understand them all that well, because they didn't work.
I appreciate the help!
It would probably be better to avoid the static setcmdvar.bat script and write it all in the script. Using -Encoding ascii is what keeps the output from being Unicode and having a BOM (Byte Order Mark) at the beginning. It has nothing to do with Chinese characters.
ECHO>"%USERPROFILE%\value.txt" 4
SET "CMDFILE=%USERPROFILE%\setcmdvar.bat"
powershell -NoLogo -NoProfile -Command ^
"'SET ""cmdvar=' + (Get-Content $Env:USERPROFILE\value.txt) + '""""' |" ^
"Out-File -FilePath %CMDFILE% -Encoding ascii"
pause
call "%CMDFILE%"
pause
echo The variable equals %cmdvar%
pause
EXIT /B 0
>> has its problems with mixing encodings. Plus it defaults to utf16. I recommend changing
$PSvar >> setcmdvar.bat
to
add-content setcmdvar.bat $PSvar
I'm not sure how to keep everything on one line. You can make setcmdvar.bat like this:
set cmdvar=^
So that it continues.
I'm trying to use [regex]::Replace with a match evaluator to selectively replace parts of a string. I'm writing and debugging the function in PowerShell ISE. What is strange is that running the replacement code causes one machine to output a string that is the content of the match evaluator script block while the other replaces the text correctly. I had no clue this was even possible nor why it is happening.
Given this code (borrowed from another stackoverflow answer):
$global_counter = 0
$callback = {
$global_counter += 1
"string-$($args[0])-" + $global_counter
}
$re = [regex]"match"
$re.Replace('zzz match match xxx', $callback)
Executing it on one machine causes the output (PowerShell Version 5.1.18362.145):
zzz string-match-1 string-match-1 xxx
But on another it outputs (PowerShell Version 5.1.17134.858):
zzz
$global_counter += 1
"string-$($args[0])-" + $global_counter
$global_counter += 1
"string-$($args[0])-" + $global_counter
xxx
Both are running in an x64 PowerShell ISE clean instance directly from reboot. Does anyone know why this is happening?
With debugging help from Jeroen I've managed to figure out why this is happening.
PowerShell has a security feature called Constrained Language Mode that prevents the use of any, but a core set of whitelisted types. What appears to be happening is that I'm defining a scriptblock that in turn is converted to a System.Text.RegularExpressions.MatchEvaluator before being passed to the Replace function. The match evaluator however is outside of this core set of types which means when the PowerShell engine tries to coerce the type onto an overload of Replace the only other valid one is Replace(string, string, string) (thanks Jeroen for pointing this out in the comments). The Replace function does its job, but with a regular string as a replacement thus resulting in the odd behaviour.
I'm not able to alter the language mode of my PowerShell session on the machine I'm currently working with as it is applied through Group Policies, but a workaround for me at least was to use an elevated PowerShell session and ISE to test my script.
To make it more obvious when I'm remoted to a live/production server, I thought it'd be handy to be able to colour the machine name I'm connected to when using remote PowerShell sessions.
However, I can't see a way to do this... The server name prefix seems to be independent of the Prompt function, and even if I could use that, I'm not sure how I could define a new Prompt only for the duration of the session.
Is there a way to customise this? Note: I don't want to color all server names the same, I'd like a distinction between local/production servers.
After some searching around it seems like you are correct that there is not built-in hook to override the pre-prompt [computername]: tag.
Luckily, I have a hacky workaround which could work for you!
To get color, we can just use Write-Host. Write-Host output from the prompt function will be fully left-justified, which is what we want. Unfortunately, the default [computername]: tag is inserted directly afterward. That results in the computer name being duplicated in the prompt, once with color and once without.
We get around this by returning a string containing backspace characters, so the un-colored [computername]: will be overwritten. This is the normal prompt string, typically the current path.
Finally, in case the normal prompt string is short and does not fully overwrite the un-colored [computername]: tag, we need to do some final cleanup by adding dummy space characters. That might push out the caret, though, so we need to add more backspaces to return the caret to the corrent position.
All-up, use this on your remote machine:
# put your logic here for getting prompt color by machine name
function GetMachineColor($computerName)
{
[ConsoleColor]::Green
}
function GetComputerName
{
# if you want FQDN
$ipProperties = [System.Net.NetworkInformation.IPGlobalProperties]::GetIPGlobalProperties()
"{0}.{1}" -f $ipProperties.HostName, $ipProperties.DomainName
# if you want host name only
# $env:computername
}
function prompt
{
$cn = GetComputerName
# write computer name with color
Write-Host "[${cn}]: " -Fore (GetMachineColor $cn) -NoNew
# generate regular prompt you would be showing
$defaultPrompt = "PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) "
# generate backspaces to cover [computername]: pre-prompt printed by powershell
$backspaces = "`b" * ($cn.Length + 4)
# compute how much extra, if any, needs to be cleaned up at the end
$remainingChars = [Math]::Max(($cn.Length + 4) - $defaultPrompt.Length, 0)
$tail = (" " * $remainingChars) + ("`b" * $remainingChars)
"${backspaces}${defaultPrompt}${tail}"
}
I use Posh-Git to accomplish this. See their Prompt Customization. I noticed that some of the docs are a bit out of date, if you just type $GitPromptSettings in PowerShell you will see all the properties available. Using Posh-Git has the added bonus of seeing Git stats on the prompt.
The command I use to set the machine name is ...
$GitPromptSettings.DefaultPromptPrefix = '$(get-content env:computername) '
Here is the color setting
$GitPromptSettings.DefaultPromptPath.ForegroundColor = 'Orange'