Invoke-Expression -Command Not Finding File Even Though It Exists - powershell

all,
I am trying to run a command in Power Shell and I am told the file does not exist as it stands right now for the Power Shell code. If I do the same in DOS, it finds the file and executes correctly. The only difference is that there is an additional open and closed double quote around the file name. Can this be done in Power Shell the same way? If so, how can this be done as I am not familiar with Power Shell.
DOS:
IF EXISTS F:\lvcecstos.CSV F:\LaserVault\ContentExpress\ContentExpress.exe /CID:1 /CSV:<b>"F:\lvcecstos.CSV"</b> /ClearSubscription
Power Shell:
Invoke-Expression -Command "F:\LaserVault\ContentExpress\ContentExpress.exe /CID:1 /CSV:<b>F:\lvcecstos.csv</b> /ClearSubscription"
Thanks in advance for any suggestions. I am told by the Laservault engineers that created this software package that we use that the double quotes around the file name is required. Doesn't make sense to me as to why though, but this is something I am unable to get around.

You can add double quotes around the file path as follows:
Invoke-Expression -Command 'F:\LaserVault\ContentExpress\ContentExpress.exe /CID:1 /CSV:"F:\lvcecstos.csv" /ClearSubscription';
Notice how the command is wrapped in single quotes instead of double quotes.
Ultimately, it depends on how the ContentExpress.exe program is interpreting the file path, though. For all you know, it could be appending the valued passed to "CSV" to the "current working directory."
You can also use the Start-Process cmdlet, instead of Invoke-Expression or Invoke-Command.
$Program = 'F:\LaserVault\ContentExpress\ContentExpress.exe';
$ArgumentList = '/CID:1 /CSV:"F:\lvcecstos.csv" /ClearSubscription';
Start-Process -Wait -NoNewWindow -FilePath $Program -ArgumentList $ArgumentList;

If you have PowerShell v3 (which you should) you could use the "magic parameter" --% (see here).
& F:\LaserVault\ContentExpress\ContentExpress.exe --% /CID:1 /CSV:"F:\lvcecstos.csv" /ClearSubscription
Otherwise trying to preserve double quotes around arguments could become very, very painful. See for instance this answer to a similar question.

$lvcecstos = "F:\lvcecstos.CSV"
If(Test-Path $lvcecstos)
{
Invoke-Expression -Command "& F:\LaserVault\ContentExpress\ContentExpress.exe /CID:1 /CSV:$lvcecstos /ClearSubscription"
}

Related

PowerShell run Ultraedit Script

Is it possible to run an UltraEdit macro or script from the PowerShell? Something like following:
uedit64.exe c:\temp\test.txt /s,e="c:\temp\script.js"
I have nothing special. I just want to open the log file with UltraEdit and as soon the log file is opened the UltraEdit Script should be executed on that. The following code opens the log file but does not execute the script on that.
$ultraEdit = "C:\...\UltraEdit\uedit64.exe"
$logFile = "C:\...\res.log"
$scriptFile = "C:\...\ultraEditScript.js"
Start-Process -FilePath $ultraEdit -ArgumentList "$logFile /s=`"$scriptFile`""
Absolutely! Powershell has a few different "call" operators.
https://ss64.com/ps/call.html
Take a look at the documentation for Start-process.
https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.management/start-process?view=powershell-6
Start-Process -FilePath "c:\pathtoexe\uedit64.exe" -ArgumentList "c:\temp\test.txt /s,e=`"c:\temp\script.js`""
Should work for you (change the path of course.
Yes, it is possible. The problem with your current example is surrounding quoting rules with arguments:
uedit64.exe c:\temp\test.txt '/s,e="c:\temp\script.js"'
This form should work. When you use commas, powershell will interpret that as an array. The safest way to pass arguments to an external executable is to use the stop-parser operator (--%) to avoid powershell's interpretation, but note this falls back to the cmd parser on Windows:
#requires -Version 3
uedit64.exe --% C:\Temp\test.txt /s,e="C:\Temp\script.js"
What the difference in parsers means is that you can't expand variables (if you wanted $path\script.js) in the arguments after the stop-parser, but you can still utilize environment variables using the batch syntax %VAR%.
As a best-practice, it's recommended to fully-qualify your path and use the call operator for clarity:
& C:\Temp\uedit64.exe
Thanks everyone,
The problem was with Select-String that split the matched lines, therefore, the script did not perform any action due to improper file structure.
These two works great :-)
1. & $ultraEdit /fni $logFile /S=$scriptFile
2. Start-Process -FilePath $ultraEdit -ArgumentList "$logFile /S=$scriptFile"

Command line arguments for msiexec break on PowerShell if they contain space

I'm trying to set a public property in an InstallShield installer with a value containing space. While running the MSI installer, I'm using below command on PowerShell prompt. Since the value contains a space so I used double quotes to pass the value
msiexec -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"
It breaks the command as the argument value C:\new folder\data.txt has a space in the string new folder. It results in showing up below error prompt of msiexec:
It suggests that arguments passed to the msiexec command has some problem.
But if I execute the same command on Windows default command prompt then it runs fine:
Few other options that I've tried to make things work on PowerShell prompt are as below:
Using single quote in place of double quotes
Using a back tick (`) character before space in the argument as per this answer.
Try with this
msiexec -i "myinstaller.msi" MYDIRPATH=`"C:\new folder\data.txt`"
The escape character in PowerShell is the grave-accent(`).
Note:
This answer addresses direct, but asynchronous invocation of msiexec from PowerShell, as in the question. If you want synchronous invocation, use Start-Process with the -Wait switch, as shown in Kyle 74's helpful answer, which also avoids the quoting problems by passing the arguments as a single string with embedded quoting.
Additionally, if you add the -PassThru switch, you can obtain a process-information object that allows you to query msiexec's exit code later:
# Invoke msiexec and wait for its completion, then
# return a process-info object that contains msiexec's exit code.
$process = Start-Process -Wait -PassThru msiexec '-i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"'
$process.ExitCode
Note: There's a simple trick that can make even direct invocation of msiexec synchronous: pipe the call to a cmdlet, such as Wait-Process
(msiexec ... | Wait-Process) - see this answer for more information.
To complement Marko Tica's helpful answer:
Calling external programs in PowerShell is notoriously difficult, because PowerShell, after having done its own parsing first, of necessity rebuilds the command line that is actually invoked behind the scenes in terms of quoting, and it's far from obvious what rules are applied.
Note:
While the re-quoting PowerShell performs behind the scenes in this particular case is defensible (see bottom section), it isn't what msiexec.exe requires.
Up to at least PowerShell 7.1, some of the re-quoting is downright broken, and the problems, along with a potential upcoming (partial) fix, are summarized in this answer.
Marko Tica's workaround relies on this broken behavior, and with the for now experimental feature that attempts to fix the broken behavior (PSNativeCommandArgumentPassing, available since Core 7.2.0-preview.5), the workaround would break. Sadly, it looks like then simply omitting the workaround won't work either, because it was decided not to include accommodations for the special quoting requirements of high-profile CLIs such as msiexec - see GitHub issue #15143.
To help with this problem, PSv3+ offers --%, the stop-parsing symbol, which is the perfect fit here, given that the command line contains no references to PowerShell variables or expressions: --% passes the rest of the command line as-is to the external utility, save for potential expansion of %...%-style environment variables:
# Everything after --% is passed as-is.
msiexec --% -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"
If you do need to include the values of PowerShell variables or expressions in your msiexec call, the safest option is to call via cmd /c with a single argument containing the entire command line; for quoting convenience, the following example uses an expandable here-string (see the bottom section of this answer for an overview of PowerShell's string literals).
$myPath = 'C:\new folder\data.txt'
# Let cmd.exe invoke msiexec, with the quoting as specified.
cmd /c #"
msiexec --% -i "myinstaller.msi" MYDIRPATH="$myPath"
"#
If you don't mind installing a third-party module, the ie function from the Native module (Install-Module Native) obviates the need for any workarounds: it fixes problems with arguments that have embedded " chars. as well as empty-string arguments and contains important accommodations for high-profile CLIs such as msiexec on Windows, and will continue to work as expected even with the PSNativeCommandArgumentPassing feature in effect:
# `ie` takes care of all necessary behind-the-scenes re-quoting.
ie msiexec -i "myinstaller.msi" MYDIRPATH="C:\new folder\data.txt"
As for what you tried:
PowerShell translated
MYDIRPATH="C:\new folder\data.txt" into
"MYDIRPATH=C:\new folder\data.txt" behind the scenes - note how the entire token is now enclosed in "...".
Arguably, these two forms should be considered equivalent by msiexec, but all bets are off in the anarchic world of Windows command-line argument parsing.
This is the best way to install a program in general with Powershell.
Here's an example from my own script:
start-process "c:\temp\SQLClient\sqlncli (x64).msi" -argumentlist "/qn IACCEPTSQLNCLILICENSETERMS=YES" -wait
Use Start-Process "Path\to\file\file.msi or .exe" -argumentlist (Parameters) "-qn or whatever" -wait.
Now -wait is important, if you have a script with a slew of programs being installed, the wait command, like piping to Out-Null, will force Powershell to wait until the program finishes installing before continuing forward.

Can't pass a script block as a parameter to powershell.exe via -Command

I'm trying this
$Global:commandBlock={
Start-Transcript -path $projectFolder\gruntLog.txt;
grunt $argList;
Stop-Transcript
}
$cmdProc=start-process powershell -ArgumentList ('-command `$Global:commandBlock') -WorkingDirectory $fwd -PassThru -NoNewWindow:$NoNewWindow
And keep getting $commandBlock : The term '$Global:commandBlock' is not recognized as the name of a cmdlet, function, script file, or operable program.
My guess was it has to do with scope. But making variable global didn't help. Adding -args $commandBlock like that:
-ArgumentList ('-command `$Global:commandBlock -args "-commandBlock:$commandBlock"')
-ArgumentList ('-command `$Global:commandBlock -args $commandBlock"')
didn't help
And I'm not sure that I escape variables correctly in the block, read this, but not sure how to apply to my script.
There's a few things which I think are keeping this from working. First, when you're using single quotes, ' you're instructing PowerShell to operate literally. This means that it won't expand variables. Not what you're looking for.
A better way to do this is to do it with an subexpression like this.
$Global:commandBlock={
'ham' >> C:\temp\test.txt
}
$cmdProc=start-process powershell -ArgumentList ("-command $($Global:commandBlock)") -PassThru -NoNewWindow:$NoNewWindow
This will give you the desired results.
Subexpressions are pretty sweet. It lets you embed a mini-scriptblock within a string, and it's then expanded out in the parent string.
"today's date is $(get-date), on system: $($env:COMPUTERNAME)"
today's date is 02/14/2017 11:50:49, on system: BEHEMOTH
There are two major issues (leaving the obvious mistake of attempting to reference a variable inside a single-quoted string aside):
Any argument you want to pass to a new powershell instance via -Command must be escaped in non-obvious ways if it contains " and/or \ chars, which is especially likely if you're passing a piece of PowerShell source code.
The escaping issue can generally be solved by Base64-encoding the source-code string and passing it via the -EncodedCommand parameter - see this answer of mine to a related question for how to do that, but a more concise alternative is presented below.
If the source code being passed references any variables that only exist in the calling session, the new instance won't see them.
The solution is not to reference session-specific variables in the source code being passed, but to pass their values as parameter values instead.
To solve the local-variable-not-seen-by-the-new-instance problem, we must rewrite the script block to accept parameters:
$scriptBlock={
param($projectFolder, $argList)
# For demonstration, simply *output* the parameter values.
"folder: [$projectFolder]; arguments: [$argList]"
}
Now we can apply the necessary escaping, using PetSerAl's sophisticated -replace expression from his comment on the question.
We can then invoke the resulting string with & {...} while passing it parameter values (I'm omitting the -WorkingDirectory and -PassThru parameters for brevity):
# Parameter values to pass.
$projectFolder = 'c:\temp'
$argList='-v -f'
Start-Process -NoNewWindow powershell -ArgumentList '-noprofile', '-command',
(('& {' + $scriptBlock.ToString() + '}') -replace '\"|\\(?=\\*("|$))', '\$&'),
"'$projectFolder'",
"'$argList'"
For an explanation of the regular expression, again see this answer.
Note how the variable values passed as parameters to the script block are enclosed in '...' inside a "..."-enclosed string in order to:
pass the values as a single parameter value.
protect them from another round of interpretation by PowerShell.
Note: If your variable values have embedded ' instances, you'll have to escape them as ''.
The above yields:
folder: [c:\temp]; arguments: [-v -f]
Alternative with a temporary, self-deleting script file:
Using -File with a script file has the advantage of being able to pass parameter values as literals, with no concern over additional interpretation of their contents.
Caveat: As of PowerShell Core v6-beta.3, there is a problem when passing parameter values that start with -: they are not bound as expected; see this GitHub issue.
To work around this problem, the sample script block below accesses only the first parameter by name, and relies on all remaining ones binding via the automatic $Args variable.
# Define the script block to be executed by the new PowerShell instance.
$scriptBlock={
param($projectFolder)
# For demonstration, simply *output* the parameter values.
"folder: [$projectFolder]; arguments: [$Args]"
}
# Parameter values to pass.
$projectFolder = 'c:\temp'
$argList='-v -f'
# Determine the temporary script path.
$tempScript = "$env:TEMP\temp-$PID.ps1"
# Create the script from the script block and append the self-removal command.
# Note that simply referencing the script-block variable inside `"..."`
# expands to the script block's *literal* content (excluding the enclosing {...})
"$scriptBlock; Remove-Item `$PSCommandPath" > $tempScript
# Now invoke the temporary script file, passing the arguments as literals.
Start-Process -NoNewWindow powershell -ArgumentList '-NoProfile', '-File', $tempScript,
$projectFolder,
$argList
Again, the above yields:
folder: [c:\temp]; arguments: [-v -f]
I've messed around with the syntax for passing args to a new powershell instance and have found the following works. So many variations fail without a good error message. Maybe it would work in your case?
$arg = "HAM"
$command = {param($ham) write-host $ham}
#please not its important to wrap your command
#in a further script block to stop it being processed to a string at execution
#The following would normally suffice "& $command $arg"
Start-Process powershell -ArgumentList "-noexit -command & {$command} $arg"
Also simply using the Invoke-Command gives you the -ArgumentList parameter to opperate against the given Command that you are missing with the standard powershell.exe parameters. This is probably a bit cleaner looking.
Start-Process powershell -ArgumentList "-noexit -command invoke-command -scriptblock {$command} -argumentlist $arg"
No need for any extra complex escaping or unwanted persisted variables. Just keep the script block in curly braces so it remains a script block on arrival in the new session. At least in this simple case...
If you have several string parameters that contain spaces. I found popping the string in a single parenthesis and separating with commas works well. You could also probably pass a predefined array as a single argument.
Start-Process powershell -ArgumentList "-noexit -command invoke-command -scriptblock {$command} -argumentlist '$arg1', '$arg2', '$arg3'"
Will this work:
$Global:commandBlock={
Start-Transcript -path $projectFolder\gruntLog.txt;
grunt $argList;
Stop-Transcript
}
& $Global:commandBlock

Use variable for filepath parameter of Start-Process

I'd like run a .exe which could be in a number of locations.
$runpath = "$servicepackfolder\SQLServer2008SP1-KB968369-IA64-ENU.exe"
Start-Process -FilePath $runpath -arg "/x:.\$buildfolder\PCU"
Or this way, specifying the WorkingDirectory:
Start-Process 'SQLServer2008SP1-KB968369-IA64-ENU.exe' -WorkingDirectory $servicepackfolder -arg "/x:.\$buildfolder\PCU"
But it seems the variables are not being interpreted as strings.
Start-Process : This command cannot be
executed due to the error: The system
cannot find the file specified.
I am in the correct directory and if I take the output from the $runpath variable and substitute it for the variable in the Start-Process call, I get the expected behavior.
Will this work, or am I stuck hardcoding these paths. Trying to automate the slipstream build process for SQL 2008.
I can duplicate the behavior you see if I add -NoNewWindow but if I don't specify that parameter it works as expected for my test case:
start-process sizeof.exe -WorkingDirectory C:\temp -ArgumentList 1
The new window flashes up and goes away but I can see it is running the specified exe from my temp dir.
Better late than never, but I've found a workaround for this when having the same issue, not sure if it is classed as a bug or not -
Powershell doesn't always handle un-escaped backslashes or quotes in the strings that are stored in a variable / created by string processing all that well for -FilePath, so for your line:
$runpath = "$servicepackfolder\SQLServer2008SP1-KB968369-IA64-ENU.exe"
Try the following (or equivalent) before using $runpath:
$cleanpath = $runpath.replace("\","\\").replace('"',"")
The .replace("\","\\").replace('"',"") escapes the slashes and eliminates the quotes that the string handling and passing introduce, which seems to clear this issue (for some cases).
Bit late for you I imagine but hopefully this helps other people googling for this one.

Executing powershell.exe from powershell script (run in ISE but not in script)

I'm new to these awesome Power shell world. I have a problem with script and really apreciate your help.
I have a script "cmd4.ps1" that needs to run another script "Transfer.ps1" that needs to receive 3 strings params and it needs to be run as other process thead different to "cmd4.ps1".
cmd4.ps1:
$Script="-File """+$LocalDir+"\Remote\Transfer.ps1"" http://"+$ServerIP+"/Upload/"+$FileName+" "+$ServerIP+" "+$LocalIP
Start-Process powershell.exe -ArgumentList $Script
After ejecution, the $Script cointain a value similar to
-File "c:\temp re\Remote\Transfer.ps1" http://10.1.1.1/Upload/file.txt 10.1.1.1 10.1.1.10
containing the syntax to use -File parameter to run a script of Powershell.exe, and three parameters that Transfer.ps1 needs ("http://10.1.1.1/Upload/file.txt", 10.1.1.1, 10.1.1.10).
When I write these instructions in PowerShell ISE I can see every values are right and PowerShell.exe is executed with right values, everything work fine!, but if I put these instructions inside "cmd4.ps1" script it doesn't work, I mean something is not right with parameters because I can see it start powershell but it never ends.
-ArgumentList is expecting an array of string arguments instead of a single string. Try this instead:
$ScriptArgs = #(
'-File'
"$LocalDir\Remote\Transfer.ps1"
"http://$ServerIP/Upload/$FileName $ServerIP $LocalIP"
)
Start-Process powershell.exe -ArgumentList $ScriptArgs
Note that you can simplify the string construction of the args as shown above.
Why don't you put this in cmd4.ps1?
& "c:\temp re\Remote\Transfer.ps1" "http://10.1.1.1/Upload/file.txt" "10.1.1.1" "10.1.1.10"