escape content of variable for cd - powershell

I am using Powershell, I loop through files and create a folder with each file's name to put some data there.
$files = #("[som]video.mkv")
$tmp_location = "."
# to reproduce just do it on files with a filename like [somen_id]restofname.ext
foreach ($file in $files){
$base_input = ([io.fileinfo]$file).basename
# base input may be a file called: [somen_id]restofname.ext
$tmp_dir = "$tmp_location/$base_input"
mkdir $tmp_dir # this line works and the directory is created
# do some stuff first before cd
cd $tmp_dir #this does not work
}
cd fails to handle the tmp_dir variable when it has special characters like [], but mkdir (and even rm) create/delete that directory just fine, which is a very inconsistent behavior in Powershell, I would expect it to either fail for all or work for all!
Any idea how to escape the variable such that it becomes readable to cd
(ofc in real life my array is not just 1 filename written by hand, but this example shows the error too)
Thanks

tl;dr
Use the -LiteralPath parameter to pass a path meant to be interpreted literally (verbatim) to the Set-Location cmdlet, which cd is a built-in alias of; by default (via the positionally implied -Path parameter), it is interpreted as a wildcard expression:
# * "cd" is a built-in alias of "Set-Location"
# * "sl" is the preferable, PowerShell-idiomatic built-in alias
# * Interactively, using PowerShell's elastic syntax,
# you can shorten "-LiteralPath" to "-l", given that no other parameter name
# (currently) starts with "l"
# * In PowerShell (Core) 7+, "-lp" is an official alias.
cd -LiteralPath $tmp_dir
The same applies analogously to rm (unlike what your question implies), which is a built-in alias of Remove-Item.
By contrast, mkdir (which is a wrapper function for New-Item -ItemType Directory) implicitly treats its argument literally.
Read on for details.
As for what you tried:
While cd and mkdir look like their cmd.exe counterparts, they are not:
cd is a built-in alias of the Set-Location cmdlet.
mkdir is a built-in wrapper function for the New-Item cmdlet, with implicitly applied argument -ItemType Directory.
(On Unix-like platforms, mkdir isn't an alias at all and instead refers to the external /bin/mkdir utility).
To learn what command a given name (ultimately) refers to in PowerShell, use the Get-Command cmdlet; e.g. Get-Command cd)
Thus,
cd $tmp_dir
is equivalent to the following call, given that Set-Location binds a positional argument (one not preceded by the target parameter name) to its -Path parameter:
Set-Location -Path $tmp_dir
Most PowerShell cmdlets interpret -Path arguments as wildcard expressions, including - perhaps surprisingly - Set-Location.[1]
Typically, the distinction between -Path and -LiteralPath doesn't matter, given that * and ? - the usual wildcard characters - aren't even allowed in file and directory names (at least on Windows).
However, the problem is that in PowerShell's wildcard language [ and ] also have special meaning (they form character sets - e.g. [abc] and/or character ranges - e.g. [a-c]), which conflicts with literal use of [ and ] in file names, and necessitates the use of -LiteralPath for disambiguation.[2]
By contrast, New-Item's (possibly positionally implied) -Path parameter acts like -LiteralPath, because interpreting a path argument as a wildcard expression in the context of creating a file or directory is pointless. That's why you had no problem creating a directory whose path literally contains [ and ] with mkdir.[3]
Why aliases named for a different shell's commands - such as cd and mkdir - are best avoided in PowerShell:
In a problematic attempt to ease the migration pain for cmd.exe users (and in part also for users of POSIX-compatible shells), PowerShell decided to define built-in aliases and wrapper functions that are named for cmd.exe's internal commands, whereas PowerShell's analogous internal commands, the so-called cmdlets, have very different names.
The latter isn't problematic per se - except that by their use you're missing out on the benefits of PowerShell's standard verb-noun naming convention (e.g., Set-Location), which also extends to how aliases are formed, given that the approved verbs have official alias forms (e.g., s for Set-; therefore, sl is another, but PowerShell-idiomatic Set-Location alias).
What is problematic, however, is that PowerShell commands have very different syntax from cmd.exe's.
If you use names such as cd and mkdir, you'll be tempted to think that, e.g. cd and mkdir function the same way as in cmd.exe - which is only true in the most basic of use cases, however.
It's best to use the true PowerShell command names or - for brevity in interactive use - their PowerShell aliases, which are (reasonably) predictably formed, as discussed above (e.g., sl for Set-Location and ni for New-Item)
[1] By contrast, cmd.exe's cd command does not accept wildcards. With Set-Location, wildcard support is of conceptual necessity limited, because the wildcard expression must resolve to exactly one matching directory.
[2] Alternatively, with -Path you can escape [ and ] as `[ and `], respectively, but this kind of escaping doesn't work consistently as of PowerShell 7.3.0 - see GitHub issue #7999.
[3] This parameter-naming inconsistency is unfortunate; arguably, -LiteralPath should at least be supported as a parameter alias name for -Path. That said, as of PowerShell 7.3.0, there is actually a case where -Path currently is interpreted as a wildcard expression, namely when combined with the -Name parameter, but this should be considered a bug - see GitHub issue #17106.

Related

Why am I not able to see the hidden files using " ls -a" command in Powershell...?

after initializing it with git init commamnd, I am getting the following error. Please help to resolve it.
tl;dr
Don't use ls -a in PowerShell (on Windows); use the following instead:
# 'gci' is the PowerShell-idiomatic alias of the 'Get-ChildItem' cmdlet,
# which is the analog of the 'ls' utility on Unix-like platforms.
# -Force asks that *hidden* file be shown too.
gci -Force
On Windows, ls is a built-in alias of the Get-ChildItem cmdlet, whose syntax is very different from that of the standard ls utility available on Unix-like platforms.
PowerShell aliases named for the commands of a different shell / the standard utilities of a different platform are best avoided, given their syntactic incompatibility - see the bottom section of this answer.
Use Get-Command to determine which command a given name (ultimately) refers to.
Use Get-Help or Get-Command -Syntax to see a command's syntax diagram.
-Force is the switch parameter that requests that Get-ChildItem show hidden items too, analogous to ls -A on Unix (there's is no ls -a analogue, which would include listing . and .., which is rarely useful, however).
As for what you tried:
ls -a translates to Get-ChildItem -a.
PowerShell's parameter names are typically words (e.g. -Attributes) rather than mere letters (e.g., -a)
If a given parameter name is a prefix of a full parameter name, PowerShell infers the full parameter name, as long as the prefix is unique, a feature that PowerShell calls elastic syntax, meant for interactive convenience only (in scripts, parameter names should always be spelled out in full, both for readability and long-term stability).
If it isn't unique, the error you've encountered occurs, given that PowerShell cannot predictably infer which parameter you meant to target.
In determining the uniqueness of a parameter-name prefix, parameter aliases are considered as well, even though the error message only mentions the original names of these parameters:
Specifically, parameter -Directory -File -Hidden -ReadOnly -System are mentioned in the error message, all of which have alias names that start with a.
You can list the relevant parameters and their aliases as follows:
(Get-Command Get-ChildItem).ParameterSets.Parameters | Where-Object Aliases -Like a* | Select-Object Name, Aliases -Unique

Powershell script is failing when files with a single quote are passed through script. Alternate batch file is also failing with & and ! characters

This is a deceptively complex issue, but I'll do my best to explain the problem.
I have a simple wrapper script as follows called VSYSCopyPathToClipboard.ps1:
param (
[Parameter(Mandatory,Position = 0)]
[String[]]
$Path,
[Parameter(Mandatory=$false)]
[Switch]
$FilenamesOnly,
[Parameter(Mandatory=$false)]
[Switch]
$Quotes
)
if($FilenamesOnly){
Copy-PathToClipboard -Path $Path -FilenamesOnly
}else{
Copy-PathToClipboard -Path $Path
}
Copy-PathToClipboard is just a function I have available that copies paths/filenames to the clipboard. It's irrelevant to the issue, just assume it does what it says.
The way the wrapper is called is through the Windows right click context menu. This involves creating a key here: HKEY_CLASSES_ROOT\AllFilesystemObjects\shell\.
Mine looks like this:
The command is as follows:
"C:\Tools\scripts\BIN\SingleInstanceAccumulator.exe" -q:' "-c:pwsh -noprofile -windowstyle hidden -Command "C:\Tools\scripts\VSYSCopyPathToClipboard.ps1" -Path $files" "%1"
And similarly for the "Copy as Filename":
"C:\Tools\scripts\BIN\SingleInstanceAccumulator.exe" -q:' "-c:pwsh -noprofile -windowstyle hidden -Command "C:\Tools\scripts\VSYSCopyPathToClipboard.ps1" -FilenamesOnly -Path $files" "%1"
I am using a tool here called SingleInstanceAccumulator. This allows me to pass multiple selected files to a single instance of PowerShell. If I didn't use this program and ran my command with multiple files selected it would launch multiple instances of PowerShell for each file selected. It's the next best thing to creating your own shell extension and implementing IPC etc.
This has been working great until today when I encountered a file with a single quote in its filename (I.E.testing'video.mov) and the entire script failed. It's failing because the delimiter I'm using with SingleInstanceAccumulator is also a single quote and PowerShell sees no matching quote... thus errors out.
I could fix this if my variables were static by just doubling up the offending single quote, but since my parameters are files I have no opportunity to escape the single quote beyond renaming the file itself ... which is a non-solution.
So now I have no clue how to handle this.
My first try at solving the problem was as such:
Create a batch file and redirect my registry command to it.
Change the SingleInstanceAccumulator delimiter to '/' (All files will be separated by a forward slash.)
Replace the offending single quote to two single quotes.
Replace the '/' delimiters with single quotes.
Finally pass the whole argument list back to Powershell.
This image demonstrates how the above process looks:
This is the batch file's code:
#echo off
setlocal EnableDelayedExpansion
:: This script is needed to escape filenames that have
:: a single quote ('). It's replaced with double single
:: quotes so the filenames don't choke powershell
:: echo %cmdcmdline%
set "fullpath=%*"
echo Before
echo !fullpath!
echo ""
echo After
set fullpath=%fullpath:'=''%
set fullpath=%fullpath:/='%
echo !fullpath!
:: pwsh.exe -noprofile -windowstyle hidden -command "%~dpn0.ps1 -Path !fullpath!
pause
Once I got that wired up I started celebrating ... until I hit a file with an ampersand (&) or an exclamation point (!). Everything fell apart again. I did a whole bunch of google-fu with regards to escaping the & and ! characters but nothing suggested worked at all for me.
If I pass 'C:\Users\futur\Desktop\Testing\Video Files\MOV Batch\testing&video.mov' into my batch file, I get 'C:\Users\futur\Desktop\Testing\Video Files\MOV Batch\testing back.
It truncates the string at the exact position of the ampersand.
I feel like there has to be a way to solve this, and that I'm missing something stupid. If I echo %cmdcmdline% it shows the full commandline with the &, so it's available somehow with that variable.
In conclusion: I'm sorry for the novel of a post. There is a lot of nuance in what I'm trying to accomplish that needs to be explained. My questions are as follows:
Can I accomplish this with Powershell only and somehow pre-escape single quotes?
Can I accomplish this with a batch file, and somehow pre-escape & and ! (and any other special characters that would cause failure)?
Any help at all would be hugely appreciated.
Edit1:
So in the most hideous and hackish way possible, I managed to solve my problem. But since it's so horrible and I feel horrible for doing it I am still looking for a proper solution.
Basically, to recap, when I do either of these variable assignments:
set "args=%*"
set "args=!%*!"
echo !args!
& and ! characters still break things, and I don't get a full enumeration of my files. Files with & get truncated, etc.
But I noticed when I do:
set "args=!cmdcmdline!"
echo !args!
I get the full commandline call with all special characters retained:
C:\WINDOWS\system32\cmd.exe /c ""C:\Tools\scripts\VSYSCopyPathToClipboardTest.bat" /C:\Users\futur\Desktop\Testing\Video Files\MOV Batch\KylieCan't.mov/,/C:\Users\futur\Desktop\Testing\Video Files\MOV Batch\The !Rodinians - Future Forest !Fantasy - FFF Trailer.mov/,/C:\Users\futur\Desktop\Testing\Video Files\MOV Batch\Yelle - Je Veu&x Te Voir.mov/,/C:\Users\futur\Desktop\Testing\Video Files\MOV Batch\Erik&Truffaz.mov/,/C:\Users\futur\Desktop\Testing\Video Files\MOV Batch\my_file'name.mov/,/C:\Users\futur\Desktop\Testing\Video Files\MOV Batch\testing&video.mov/"
So what I did was simply strip out the initial C:\WINDOWS\system32\cmd.exe /c ""C:\Tools\scripts\VSYSCopyPathToClipboardTest.bat" part of the string:
#echo off
setlocal enableDelayedExpansion
set "args=!cmdcmdline!"
set args=!args:C:\WINDOWS\system32\cmd.exe=!
set args=!args: /c ""C:\Tools\scripts\VSYSCopyPathToClipboard.bat" =!
set args=!args:'=''!
set args=!args:/='!
set args=!args:~0,-1!
echo !args!
pwsh.exe -noprofile -noexit -command "%~dpn0.ps1 -Path !args!
And... it works flawlessly. It handles any crazy character I throw at it without needing to escape anything. I know It's totally the most degenerate garbage way of approaching this, but not finding a solution anywhere leads me to desperate measures. :)
I am probably going to make the string removal a bit more universal since it literally breaks if I change the filename.
I am still VERY much open to other solutions should anyone know of a way to accomplish the same thing in a more elegant way.
A fully robust solution based on PowerShell's -Command (-c) CLI parameter that can handle ' characters in paths as well as $ and ` ones requires a fairly elaborate workaround, unfortunately:[1]
Use an aux. cmd.exe call that echoes the $files macro as-is and pipe that to pwsh.exe; make SingleInstanceAccumulator.exe double-quote the individual paths (as it does by default), but use no delimiter (d:"") in order to effectively output a string in the form "<path 1>""<path 2>""...
Make pwsh.exe reference the piped input via the automatic $input variable and split it into an array of individual paths by " (removing empty elements that are a side effect of splitting with -ne ''). The necessity for providing the paths via the pipeline (stdin) is discussed in more detail in this related answer.
The resulting array can safely be passed to your scripts.
Also, enclose the entire -Command (-c) argument passed to pwsh.exe in \"...\" inside the "-c:..." argument.
Note: You may get away without doing this; however, this would result in whitespace normalization, which (however unlikely) would alter a file named, say, foo bar.txt to foo bar.txt (the run of multiple spaces was normalized to a single space).
Escaping " characters as \" is necessary for PowerShell's -Command (-c) CLI parameter to treat them verbatim, as part of the PowerShell code to execute that is seen after initial command-line parsing, during which any unescaped " characters are stripped.
Therefore, the first command stored in the registry should be (adapt the second one analogously; note that there must be no space between the echo $files and the subsequent |):
"C:\Tools\scripts\BIN\SingleInstanceAccumulator.exe" -d:"" "-c:cmd /c echo $files| pwsh.exe -noprofile -c \"& 'C:\Tools\scripts\VSYSCopyPathToClipboard.ps1' -Path ($input -split '\\\"' -ne '')\"" "%1"
Note:
If you modified your scripts to accept the paths as individual arguments rather than as an array, a much simpler solution via the -File CLI parameter (rather than -Command (-c)) is possible. This could be as simple as decorating the $Path parameter declaration with [Parameter(ValueFromRemainingArguments)] and then invoking the script without naming the target parameter explicitly (-Path):
"C:\Tools\scripts\BIN\SingleInstanceAccumulator.exe" -d:" " "-c:pwsh.exe -noprofile -File \"C:\Tools\scripts\VSYSCopyPathToClipboard.ps1\" $files" "%1"
Note the use of -d:" " to make SingleInstanceAccumulator.exe space-separate the (double-quoted by default) paths. Since -File passes the pass-through arguments verbatim, there is no concern about what characters the paths are composed of.
Self-contained PowerShell sample code:
The following code defines a Copy Paths to Clipboard shortcut-menu command for all file-system objects (except drives):
No separate .ps1 script is involved; instead, the code passed to -Command / -c directly performs the desired operation (copying the paths passed to the clipboard).
The following helps with troubleshooting:
The full command line with which PowerShell was invoked ([Environment]::CommandLine) is printed, as is the list of paths passed ($file)
-windowstyle hidden is omitted to keep the console window in which the PowerShell commands visible and -noexit is added so as to keep the window open after the command has finished executing.
Prerequisites:
Download and build the SingleInstanceAccumulator project using Visual Studio (using the .NET SDK is possible, but requires extra work).
Place the resulting SingleInstanceAccumulator.exe file in one of the directories listed in your $env:Path environment variable. Alternatively, specify the full path to the executable below.
Note:
reg.exe uses \ as its escape character, which means that \ characters that should become part of the string stored in the registry must be escaped, as \\.
The sad reality as of PowerShell 7.2 is that an extra, manual layer of \-escaping of embedded " characters is required in arguments passed to external programs. This may get fixed in a future version, which may require opt-in. See this answer for details. The code below does this by way of a -replace '"', '\"' operation, which can easily be removed if it should no longer be necessary in a future PowerShell version.
# RUN WITH ELEVATION (AS ADMIN).
# Determine the full path of SingleInstanceAccumulator.exe:
# Note: If it isn't in $env:PATH, specify its full path instead.
$singleInstanceAccumulatorExe = (Get-Command -ErrorAction Stop SingleInstanceAccumulator.exe).Path
# The name of the shortcut-menu command to create for all file-system objects.
$menuCommandName = 'Copy Paths To Clipboard'
# Create the menu command registry key.
$null = reg.exe add "HKEY_CLASSES_ROOT\AllFilesystemObjects\shell\$menuCommandName" /f /v "MultiSelectModel" /d "Player"
if ($LASTEXITCODE) { throw }
# Define the command line for it.
# To use *Windows PowerShell* instead, replace "pwsh.exe" with "powershell.exe"
# SEE NOTES ABOVE.
$null = reg.exe add "HKEY_CLASSES_ROOT\AllFilesystemObjects\shell\$menuCommandName\command" /f /ve /t REG_EXPAND_SZ /d (#"
"$singleInstanceAccumulatorExe" -d:"" "-c:cmd /c echo `$files| pwsh.exe -noexit -noprofile -c \\"[Environment]::CommandLine; `$paths = `$input -split [char] 34 -ne ''; `$paths; Set-Clipboard `$paths\\"" "%1"
"# -replace '"', '\"')
if ($LASTEXITCODE) { throw }
Write-Verbose -Verbose "Shortcut menu command '$menuCommandName' successfully set up."
Now you can right-click on multiple files/folders in File Explorer and select Copy Paths to Clipboard in order to copy the full paths of all selected items to the clipboard in a single operation.
[1] An alternative is to use the -f option instead, which causes SingleInstanceAccumulator.exe to write all file paths line by line to an auxiliary text file, and then expands $files to that file's full path. However, this requires the target scripts to be designed accordingly, and it is their responsibility to clean up the auxiliary text file.

Powershell call cmd.exe command like copy /b

I saw this already Fast and simple binary concatenate files in Powershell
I'm not interested by the answer above I'm interested about what's wrong with syntax below :
when I call a cmd.exe command like copy /b:
function join-file {
copy /b $($args[0])+$($args[1]) $($args[2])
}
I get an error Copy-Item : A positional parameter cannot be found
As the error alludes to, copy is actually just an alias for Copy-Item and it does not have a /b parameter. You can call cmd to use its copy command.
function join-file {
cmd /c copy /b $($args[0])+$($args[1]) $($args[2])
}
Note: This answer complements Doug Maurer's helpful answer, which provides an effective solution (for file names without spaces).
There's a subtlety in how PowerShell parses unquoted compound tokens such as $($args[0])+$($args[1]) (by compound token I mean directly concatenated distinct syntax constructs):
$($args[0])+$($args[1]) results in two arguments[1] - although with the specific command at hand (cmd.exe's internal copy command) that happens not to be a problem:
Argument 1: The value of $($args[0])
Argument 2: A verbatim + directly followed by the value of $($args[1])
To avoid this problem, enclose the whole compound token in "...", so as to predictably treat it as an expandable string.
The upshot:
To be safe, use double-quoting ("...") explicitly to enclose compound tokens that involve variable references or subexpressions.
By contrast, to reference a variable or even method call in isolation, neither quoting nor enclosing in $(...), the subexpression operator, are needed.
Applied naively to your command (see the better solution below):
# Note: See better solution below.
function join-file {
# Note the "..." around the first argument, and the absence of quoting
# and $(...) around the second.
cmd /c copy /b "$($args[0])+$($args[1])" $args[2]
}
However, if $args[0] or $($args[1]) contained spaces, the copy command would malfunction; it is therefore more robust to pass the file names and the + as separate arguments, which copy also supports:
function join-file {
# Pass the arguments individually, which obviates the need for quoting
# and $(...) altogether:
cmd /c copy /b $args[0] + $args[1] $args[2]
}
[1] You can verify this as follows: $arr='foo', 'bar'; cmd /c echo $($arr[0])+$($arr[1]), which yields: foo +bar (note the space).

cd Program Files error: positional parameter cannot be found

PS C:\> cd Program Files
When I give this command, I don't know why, but it is not accepting Program Files. The same command is working perfectly fine in cmd.
This is the error it shows:
Set-Location : A positional parameter cannot be found that accepts argument 'Files'.
At line:1 char:1
+ cd Program Files
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Set-Location], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.SetLocationCommand
tl;dr
Since your directory name contains spaces, you must quote it, e.g.:
# Note: In PowerShell, 'cd' is an alias of 'Set-Location'
cd 'Program Files'
As stated in Maximilian Burszley's helpful answer, Program Files is parsed as two arguments, because spaces are used to separate command-line arguments.
Your attempt to use cd Program Files may be inspired by cmd.exe (the legacy Command Prompt), where this syntax indeed works; however, even there it conceptually violates the usual rules of argument parsing.
Therefore, you need to use a form of quoting in order to pass a value that contains spaces as a single argument.
You have several options to implement this quoting:
Use '...' around values that are literals, i.e., that should be used verbatim; a '...' string is called a verbatim (single-quoted) string.
Use "..." around values in which you want to embed variable references (e.g., $HOME) or subexpressions (e.g., $(Get-Date); that is, "..." strings perform string interpolation, and they're called expandable (double-quoted) strings.
Use ` to quote (escape) a single character; `, the so-called backtick, is PowerShell's general escape character.
Therefore, you could use any of the following:
cd 'Program Files'
cd "Program Files" # as there are no $-prefixed tokens, same as 'Program Files'
cd Program` Files # `-escape just the space char.
Also, you can use tab-completion to expand the (space-less) prefix of a space-containing path to its full form with quoting applied implicitly.
E.g., if you're in C:\ and you type:
cd Program<tab>
PowerShell automatically completes the command to:
cd '.\Program Files\'
Note how the Program Files (along with .\ to refer to the current dir. and a trailing \ to indicate a dir.) was automatically single-quoted.
Using wildcard expressions as arguments:
As noted, in PowerShell cd is a built-in alias of the Set-Location cmdlet.
Passing a path positionally - e.g. Set-Location 'C:\Program Files' - implicitly binds it to the -Path parameter; that is it, is equivalent to Set-Location -Path 'C:\Program Files'
-Path interprets its argument as a wildcard expression, so that you can do something like Set-Location C:\Win* in order to change to C:\Windows (assuming that the wildcard expression matches only one directory).
The tricky thing is that - unlike in cmd.exe - it isn't just * and ? that have special meaning in PowerShell wildcard expressions, but [ and ] as well (for character-set and character-range expressions such as [abc] and [a-c]), so that Set-Location Foo[1] will not work for changing to a directory literally named Foo[1].
In that case, you must use the -LiteralPath parameter -
Set-Location -LiteralPath Foo[1] - to ensure that the path is interpreted literally (verbatim).
cd C:\Program` Files\
` just escape the space in folder name.
The reason is invalid syntax. Each argument to a powershell command is separated by space, so what you're actually doing is something similar to:
Set-Location -Path Program -Arg2 Files
But Set-Location (aliased: cd) does not have any positional arguments for a second position, so it can't bind it to any parameters and use it in the command, hence the terminating error.
If you want a simpler cd alias, you could do something like this (in your $profile):
function ChangeDirectory {
param(
[Parameter(
Position = 0,
Mandatory,
ValueFromRemainingArguments
)]
[string[]] $Path
)
Set-Location -Path ($Path -join ' ')
}
Set-Alias -Name cd -Value ChangeDirectory
Do note, however, that if you're not specifying a relative path (.\), it will use the root path of your current drive (most likely, C:\). This can be tuned in the function to test for both locations (relative and drive-rooted), but logic for figuring out which one to use if they both exist would be tricky (or can always default to relative).
Use quotes (double or single) to go to the folder or path contains white spaces or special symbols.
Forexample:-
cd "C:\Users\LAPTOP0534\OneDrive - My Content (Active Directory)"
OR
cd 'OneDrive - My Content (Active Directory)\My Folder'
For your case it will be PS C:\> cd "Program Files"
Just run your command prompt as administrator .
See the picture attached.
It will run easily
This is because of the space between the sentences. Enter code here You complete the tab.
Replace PS F: > cd new folder with PS F: > cd "new folder" in the PowerShell. Or look at F: > cd './new folder'. I'm a beginner and you should try this.

Powershell: Pipe external command output to another external command

How can I get PowerShell to understand this type of thing:
Robocopy.exe | Find.exe "Started"
The old command processor gave a result, but I'm confused about how to do this in PowerShell:
&robocopy | find.exe "Started" #error
&robocopy | find.exe #("Started") #error
&robocopy #("|", "find.exe","Started") #error
&robocopy | &find #("Started") #error
&(robocopy | find "Started") #error
Essentially I want to pipe the output of one external command into another external command. In reality I'll be calling flac.exe and piping it into lame.exe to convert FLAC to MP3.
Cheers
tl;dr
# Note the nested quoting. CAVEAT: May break in the future.
robocopy.exe | find.exe '"Started"'
# Alternative. CAVEAT: doesn't support *variable references* after --%
robocopy.exe | find.exe --% "Started"
# *If available*, use PowerShell's equivalent of an external program.
# In lieu of `findstr.exe`, you can use Select-String (whose built-in alias is scs):
# Note: Outputs are *objects* describing the matching lines.
# To get just the lines, pipe to | % ToString
# or - in PowerShell 7+ _ use -Raw
robocopy.exe | sls Started
For an explanation, read on.
PowerShell does support piping to and from external programs.
The problem here is one of parameter parsing and passing: find.exe has the curious requirement that its search term must be enclosed in literal double quotes.
In cmd.exe, simple double-quoting is sufficient: find.exe "Started"
By contrast, PowerShell by default pre-parses parameters before passing them on and strips enclosing quotes if the verbatim argument value doesn't contain spaces, so that find.exe sees only Started, without the double quotes, resulting in an error.
There are three ways to solve this:
PS v3+ (only an option if your parameters are only literals and/or environment variables): --%, the stop-parsing symbol, tells PowerShell to pass the rest of the command line as-is to the target program (reference environment variables, if any, cmd-style (%<var>%)):
robocopy.exe | find.exe --% "Started"
This answer details the limitations of --%.
PS v2 too, or if you need to use PowerShell variables in the parameters: apply an outer layer of PowerShell quoting (PowerShell will strip the single quotes and pass the contents of the string as-is to find.exe, with enclosing double quotes intact):
robocopy.exe | find.exe '"Started"'
Caveat: It is only due to broken behavior that this technique works. If this behavior gets fixed (the fix may require opt-in), the above won't work anymore, because PowerShell would then pass ""Started"" behind the scenes, which breaks the call - see this answer for more information.
If an analogous PowerShell command is available, use it, which avoids all quoting problems. In this case, the Select-String cmdlet, PowerShell's more powershell analog to findstr.exe can be used, as shown above.
#Jobbo: cmd and PowerShell are two different shells. Mixing them is sometimes possible but as you realized from Shay's answer, it won't get you too far. However, you may be asking the wrong question here.
Most of the time, the problem you are trying to solve like piping to find.exe are not even necessary.
You do have equivalent of find.exe, in fact more powerful version, in Powershell: select-string
You can always run a command and assign results to a variable.
$results = Robocopy c:\temp\a1 c:\temp\a2 /MIR
Results are going to be STRING type, and you have many tools to slice and dice it.
PS > $results |select-string "started"
Started : Monday, October 07, 2013 8:15:50 PM
Invoke it via cmd:
PS> cmd /c 'Robocopy.exe | Find.exe "Started"'