Summary: Does the set /p command force the CMD interpreter to evaluate a Pipeline, even if it's escaped? Is it possible to avoid this?
Consider the following line from a batch file. The purpose of the code below is to output Echo F | Xcopy /Y 'FilePath1' 'FilePath2' to the log file.
<nul (set /p "Lineout=Echo F ^| Xcopy /Y '%~1' '%~2'") >>%log%
I successfully get a string output to %log% as expected, but in the batch file's console, I get Invalid Parameters warnings for each time this is listed. I am assuming the issue is from the | pipeline somehow invoking the Xcopy command, but I don't understand why it's doing this, even though I've escaped it! (I do NOT want xcopy to run!)
Echo Echo F ^| Xcopy /Y '%~1' '%~2' >>%log%
If I Echo that line specifically as shown above, I get no error on the console and I still get my output, and xcopy isn't ran. Is it simply because the pipeline is unescapable, and thus breaking CMD out of the STDOUT and truly invoking this command?
I'm using it inside a function where args 1 and 2 are files, but am using single quotes for the strings since I know the double quotes will break the <nul set/p text output approach.
Is it because I'm executing the set /p inside a () block? I've tried |, ^|, and ^^| inside the double quotes and I still get the error as though | is not being escaped. I even get the error when I use 2>&1 to force the output of STDERR to the log file! (Since I do not care that the command doesn't run, I'm willing to output STDERR to log or nul - I successfully run it later in the subroutine - this is just a line out to the log for clarity on what was ran to copy the files)
What's weirder still - with both | and ^| I see ^^| in the log output. Why is it appending TWO (2) ^ to the log in the set /p? With ^^|, I see ^^^^| in the log output and still get the Invalid Parameters error! Do I need three (3) ^ to properly escape?
I must be missing something simple, or chalk this up to other problems inherent with the Pipeline...
If I'm using a Pipeline, should I also send the Command's output to nul inside the () block like below, or will this simply be overruled by the outer redirect to %log%? I have to assume the | will not be escaped no matter what I do, and when I execute the <nul set /p line to output the line, 2>&1 inside the set /p command, the STDERR would at least be sent to the logfile or nul depending on how it interprets...
<nul (set /p "Lineout=Echo F ^| Xcopy /Y '%~1' '%~2' >nul 2>&1") >>%log%
I want to assume the Pipeline is simply executing the command to the right of it inside the block, and the redirect external to the block is working as one would expect for any code block in ()
I believe the answer to "Does the set /p command force the CMD interpreter to evaluate a Pipeline, even if it's escaped? Is it possible to avoid this?" is "Yes, on condition."
As mentioned in a comment by @Mofi, the initial use of <nul (set /p "lineOut=Echo F | Xcopy /Y '%~1' '%~2'")>>%log% works as expected. . .
What isn't taken into account in how I asked the question initially (because I thought it would not matter) is whether or not the string Echo F | XCopy /Y '%~1' '%~2' is interpreted the same way when it's sent as another argument for another routine when a specific use-case is handled.
I had made the assumption that these two examples of code are equivalent - they are NOT.
REM Works as expected where '%~1' and '%~2' are file paths enclosed in a double quote line to be sent to a file.
<nul (set /p "lineOut=Echo F | Xcopy /Y '%~1' '%~2'")>>%log%
REM Does NOT work where %* contains the string listed above:
<nul (set /p "lineOut=:RoutineName %*")>>%log%
The use-case scenario which I thought would be considered equivalent when I was first testing is shown below. It wasn't until much more testing that I isolated the %* as being the largest contributing factor.
As @Mofi stated in another comment, this use of %* fails because the | (as well as other operators - &<>) will be interpreted directly because they're stripped out of the " string " context and cannot be interpreted as a string.
REM Does NOT work - the CMD Interpreter fails to ignore the pipeline when using this approach.
Call :WriteOut "Echo F | Xcopy /Y '%~1' '%~2'" %log% "Yes"
:WriteOut [Verbiage] [Log File] [To Log?]
REM This is the line which I thought compared Apples to Apples. It does NOT!
<nul (set /p "lineOut=:WriteOut %*") >>%log%
REM This line worked fine
<nul (set /p "lineOut=%~1")
REM this line also worked fine
if not "%~3"=="" (
<nul (set /p "lineOut=%~1")>>%log%
)
I've found if I use another parameter as needed, I can effectively get this to output to the log correctly with no errors:
REM with Parameter 4, we have success.
Call :WriteOut "Echo F | Xcopy /Y '%~1' '%~2'" %log% "Yes" "Yes"
:WriteOut [Verbiage] [Log File] [To Log?] [Lineout Contains Special Characters?]
If "%~4"=="" (
<nul (set /p "lineOut=:WriteOut %*") >>%log%
) else (
>>%log% Echo(:WriteOut %*
)
REM . . . code . . .
I could also do this as I have in the past (without realizing this also solved the problem for me before I ever had it)
:WriteOut [Verbiage] [Log File] [To Log?] [Lineout Contains Special Characters?]
REM Break apart Args supplied, and send output safely to log file
For %%a in (%*) do (
set /a argCnt+=1
REM Assumes you have !cr! and !lf! defined appropriately previously
<nul (set /p "lineOut=:WriteOut Arg!argCnt! = %%~a!cr!!lf!")>>%log)
)
REM . . . Code . . .