What is the way to escape colon in powershell? - powershell

I have the following powershell in a script file:
cd "$env:systemdrive:\$env:APPDATA\Mozilla\Firefox\Profiles\*.default"
Expanded, I want this to do something similar to this:
cd "C:\Users\Bob\AppData\Roaming\Mozilla\Firefox\Profiles\f1wkii3l.default"
But when I run it, I get:
cd : Cannot find drive. A drive with the name '\C' does not exist.
I am guessing the colon I put in there to be at the end of C:\ is causing problems.
I tried:
cd "${env:systemdrive}:\${env:APPDATA}\Mozilla\Firefox\Profiles\*.default"
But then I get the error:
cd : Cannot find a provider with the name 'C'.
How can I escape the colon so that powershell just sees it as normal text?
NOTE: I looked at this question: Escaping a colon in powershell and the answer is all about .NET and does not answer my question (though the question is very similar).

Other answers are valid for troubleshooting and for your task at hand. One suggestion I have, and what I consider a best-practice, is making use of Join-Path anytime you are dealing with paths so you don't have to worry about trailing or beginning path separators.
This example
$d1 = "${env:APPDATA}\Mozilla\Firefox\Profiles\*.default"
$d2 = Join-Path ${env:APPDATA} "\Mozilla\Firefox\Profiles\*.default"
$d3 = Join-Path ${env:APPDATA} "Mozilla\Firefox\Profiles\*.default"
Write-Output "d1 = '$d1'"
Write-Output "d2 = '$d2'"
Write-Output "d3 = '$d3'"
Produces
d1 = 'C:\Users\xxx\AppData\Roaming\Mozilla\Firefox\Profiles\*.default'
d2 = 'C:\Users\xxx\AppData\Roaming\Mozilla\Firefox\Profiles\*.default'
d3 = 'C:\Users\xxx\AppData\Roaming\Mozilla\Firefox\Profiles\*.default'
All are acceptable, and I would tend to use the d3 version in my own scripts.

You don't need to escape anything there. The APPDATA environment variable already includes the drive, so you only need
cd "${env:APPDATA}\Mozilla\Firefox\Profiles\*.default"
${env:systemdrive}:\${env:APPDATA} would create a path C:\C:\Users\..., which is indeed invalid.

If you write-host those environmental variables, you'll see that:
PS C:\>write-host $env:systemdrive
C:
PS C:\>write-host $env:appdata
C:\Users\****\AppData\Roaming
So your current attempt expands to C:C:\Users\****\AppData\Roaming\...
So all you need is the command:
cd "$env:Appdata\Mozilla\Firefox\Profiles\*.default"

Related

How to run command line or PowerShell script if file could exist in two locations?

We have multiple systems with the same Dell program but different versions. The version number is the same but one is in the x86 folder and the other is in the x64 one as shown below. Could someone help me convert this to a command line or PowerShell script so that it would correctly from whichever location exists? It should only exist in one of the two locations and only needs to be run once.
Dell Command | Update
"%ProgramFiles%\Dell\CommandUpdate\dcu-cli.exe" /configure -updatetype=bios,firmware,driver,application,utility,others
"%ProgramFiles%\Dell\CommandUpdate\dcu-cli.exe" /applyupdates -reboot=enable -autosuspendbitlocker=enable
Dell Command | Update for Windows Universal
%programfiles(x86)%\Dell\CommandUpdate\dcu-cli.exe" /configure -updatetype=bios,firmware,driver,application,utility,others
%programfiles(x86)%\Dell\CommandUpdate\dcu-cli.exe" /applyupdates -reboot=enable -autosuspendbitlocker=enable
Using PowerShell, iterate over the paths and use Test-Path to make sure the path exists:
$possiblePaths =
"$env:ProgramFiles\Dell\CommandUpdate\dcu-cli.exe",
"${env:ProgramFiles(x86)}\Dell\CommandUpdate\dcu-cli.exe"
$correctPath = foreach( $path in $possiblePaths ) {
if( Test-Path -PathType Leaf $path ) {
$path
break
}
}
if( !$correctPath ) {
throw "Could not find dcu-cli.exe at any of the following paths: $(#( $possiblePaths ) -join ', ')"
}
# Execute found binary
& $correctPath /configure -updatetype='bios,firmware,driver,application,utility,others'
After the loop, check that $correctPath doesn't have a "falsey" evaluation (in this case, not $null or [string]::Empty). If it evaluates to $False, the file was not found in any of the possible locations, and we throw an error.
To execute, use the call-operator & to execute the path stored in $correctPath. You can include any parameters after this as if you were invoking the binary by literal name instead of as a variable. The sample above uses one of your argument strings from the question as an example.

Running a command with arguments assistance

I have a command which runs a program in silent mode, it uses an XML file for the data repository and a word template to create multiple word documents based on a filter xml file.
The command I use is:
"P:\ath to\executable" -Username:Admin -Password:Pa55w0rd -Datadefinition:"C:\Data.xml" -Datafilter:"C:\Filter.xml" -wordtemplate:"C:\Batch\Paul1.dotx" -Targetdocument:="C:\Batch\Paul1.pdf" -filetype:PDF -Log:"C:\Logs\error.log" -Usage:DOCGENSILENT
I need to run this as a PowerShell script which I have mostly managed:
set-executionpolicy unrestricted
$datadefinition = Get-Content "C:\Data file.xml"
$datafilter = Get-Content "C:\Filter for data file.xml"
$wordTemplate = Get-Content "C:\"C:\Template\Paul1.dotx"
$targetFolder = Get-Content "C:\"C:\Paul\Paul.pdf"
Stop-Job = "Executable path" -Username:Admin -Password:Pa55w0rd -Datadefinition:%dataDefinition% -Datafilter:%dataFilter% -wordtemplate:%wordTemplate% -Targetdocument:%targetFolder% -filetype:docx -Log:%logPath% -Usage:DOCGENSILENT
Stop-Job 1
set-executionpolicy restricted
Write-Host -NoNewLine "Press any key to continue..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
My issue is that the script starts the executable but then doesnt pass the Variables, can anyone guide me in the right direction to fix this?
Getting this working depends on the behavior of your executable. Some things I noticed:
Shouldn't this:
$wordTemplate = Get-Content "C:\"C:\Template\Paul1.dotx"
be this:
$wordTemplate = "C:\Template\Paul1.dotx"
Are you sure you need Get-Content? (Aside from that, the path and quoting in your sample are not correct.)
Shouldn't this:
$targetFolder = Get-Content "C:\"C:\Paul\Paul.pdf"
be this:
$targetDocument = "C:\Paul\Paul.pdf"
I doubt Get-Content is correct here, since presumably your output file doesn't exist yet? I also renamed the variable so it makes more sense in your command.
In fact, are you sure you need Get-Content for any of those? Aren't you specifying filenames, not the content of the files?
In PowerShell, variables are prefixed with $ rather than being surrounded by %.
Using Set-ExecutionPolicy within a script to enable scripts to run is pointless, because the script is already running. (That is, if execution policy prevented script execution, PowerShell wouldn't let you run the script in the first place.)
If my guesses regarding your variables are correct, I think your script should look something like this (note also that I specified a $logFile variable, which I didn't see in your script):
$datadefinition = "C:\Users\Administrator\data\Sample Model_146_object type(s).xml"
$datafilter = "C:\Users\Administrator\data\Sample Model_146_object type(s).xml"
$wordtemplate = "C:\Users\Administrator\Templates\Base object.docx"
$targetdocument = "C:\Users\Administrator\Result\sample test15"
$logfile = "C:\Users\Administrator\Logs\C4W Error.log"
& "C:\Program Files (x86)\Communicator4Word.exe" -Username:Admin -Password: -Datadefinition:$datadefinition -Datafilter:$datafilter -wordtemplate:$wordtemplate -Targetdocument:$targetdocument -filetype:docx -Log:$logfile -Usage:DOCGENSILENT
I don't know the behavior of Communicator4Word.exe when you use -Password: with no password after it. (Is that a syntax error, or should you just omit -Password: altogether?)

How to deal with paths on PowerShell?

What is the best practice approach to deal with paths to scripts/folders on PowerShell ?
Example 1 (relative to the target script location) :
$SettingsModule = (Get-Item -Path ('{0}\..\Settings\SettingsReader.psm1' -f $PSScriptRoot)).FullName
$AzureSqlUtilsModule = (Get-Item -Path ('{0}\UtilsSqlAzure.psm1' -f $PSScriptRoot)).FullName
# code here ...
Example 2 (path is informed via parameter):
$PfxPath = (Join-Path -Path $SecureRepoRoot -ChildPath $appCredentials.PfxPath)
# code here ...
Or should the common paths to my local folders/repositories/scripts be handle with environment variables ? Or is there a better alternative ?
I have a Linux background, and ideally I would use environment variables to define a common paths, however I'm not sure if this is a best approach on Windows/PowerShell.
Depends on what you are doing. I would go for relative paths as of powershells modularity. When you move or update a module, you give all relevant resources with it. I don't like external dependencies that much, so I stick with that approach. Env might not be present or vary on systems - the path of your module or script is always clear.

Commands executed in PowerShell with variables surrounded in quotes fail. Why?

I'm having a surprisingly difficult time embedding variables with quotes to an external command with PoSH. For example, this command
dfsradmin membership list /rgname:`"stuff I want`"
gives me the following expected result:
Failed:
Replication group with name stuff I want cannot be found.
This command, however
$group = "stuff I want"
dfsradmin membership list /rgname:`"$group`"
fails with this error:
Failed:
The subobject "/rgname:"stuff is not a valid subobject.
Is this a bug with Powershell or am I missing/misunderstanding something?
Yeah there are known issues in Powershell ( including v2.0) around this: http://connect.microsoft.com/PowerShell/feedback/details/376207/executing-commands-which-require-quotes-and-variables-is-practically-impossible
See if the alternatives discussed in the link above work for you. I cannot try it out as I don't have that executable.
Also echoargs.exe is a useful tool that you can use to see what arguments have been recevied from Powershell.
I found that defining
$quote = '"'
and then using /command$quote"test"$quote works as well
There's no need to add back ticks in front of quotes. Does this work for you?
$group = "stuff I want"
dfsradmin membership list /rgname:"$group"
So I was able to get around this by executing it in CMD.exe and doing string manipulations to get what I need.
$str = &cmd /c 'dfsradmin membership list /rgname:"blah blah"'
$str = &cmd /c "dfsradmin membership list /rgname:$blah" # with vars
Thanks for the help! I hope this has been resolved in Powershell 3.0.
I found a workaround which doesn't call cmd but uses Invoke-Expression instead. The command has to be put in a variable first:
$var = "string with spaces"
$command = "first part " + [char]96 + [char]34 + $var + [char]96 + [char]34 + " second part"
Invoke-Expression $command
Not that pretty but it works. You can replace [char]96 with '`' and [char]34 with '"' if you prefer. Easy to create a function which does it if you use it a lot.
All of the above did not work for me but based on Carlos idea, this is the solution that worked posted here
# get msdeploy exe
$MSDeploy = ${env:ProgramFiles}, ${env:ProgramFiles(x86)} |
ForEach-Object {Get-ChildItem -Path $_ -Filter 'MSDeploy.exe' -Recurse} |
Sort-Object -Property #{Expression={[version]$_.VersionInfo.FileVersion}} -Descending |
Select-Object -First 1 -ExpandProperty FullName
#build deploy command
$deplyCmd = """""$MSDeploy"" -verb:sync -dest:iisApp=""Default Web Site"" -enableRule:DoNotDeleteRule -source:iisApp=""$ExtraWebFilesFolder"""
#execute
&cmd /c $deplyCmd
I know this is old thread but just posting here in case my solution works for somebody as it worked for me.
This particular command (dfsradmin) expects natively seen quotes so I just enclosed value with quotes in single quotes thus passing quotes as well:
dfsradmin membership list /rgname:'"stuff I want"'
or if using through variable:
$group = '"stuff I want"'
dfsradmin membership list /rgname:$group

Getting the arguments of the last invoked command in powershell?

I want to be able to get the argument portion of the previous command. $^ seems to return just the command and not the args. Get-History -count 1 returns the last full command including the command and the args. I could just .Replace the first instance, but I am not sure if it is correct.
Scenario is that sometimes I want to do something like this. Let's assume that $* are the args to the last command:
dir \\share\files\myfile.exe
copy $* c:\windows\system32
Any ideas how to get the last args correctly?
UPDATE: finished my method for doing this.
function Get-LastArgs
{
$lastHistory = (Get-History -count 1)
$lastCommand = $lastHistory.CommandLine
$errors = [System.Management.Automation.PSParseError[]] #()
[System.Management.Automation.PsParser]::Tokenize($lastCommand, [ref] $errors) | ? {$_.type -eq "commandargument"} | select -last 1 -expand content
}
Now I can just do:
dir \\share\files\myfile.exe
copy (Get-LastArgs) c:\windows\system32
To reduce typing, I did
set-alias $* Get-LastArgs
so now I still have to do
copy ($*) c:\windows\system32
if anybody has any ideas for making this better please let me know.
For the last argument (not all!) in the interactive hosts like Console and ISE it is the automatic variable $$.
Help
man about_Automatic_Variables
gets
$$
Contains the last token in the last line received by the session.
Other hosts may or may not implement this feature (as well as the $^ variable).
There is no easy way to get the last args in this fashion without parsing the history item itself, and this is no trivial matter. The reason is that the "last arguments" may not be what you think they are after you take splatting, pipelines, nested subexpressions, named and unnammed arguments/parameters into the equasion. In powershell v2 there is a parser available for tokenizing commands and expressions, but I'm not sure you want to go that route.
ps> $psparser::Tokenize("dir foo", [ref]$null) | ? {
$_.type -eq "commandargument" } | select -last 1 -expand content
foo