Why does the error message appear when `IF` compar

2019-03-21 06:11发布

问题:

I simplified the code. The following three are work.

for /L %a in (1,1,9) do @(if %a NEQ 0 (echo %a))

&

for /L %a in (1,1,9) do @(if not %a == 0 (echo %a))

&

(for /L %a in (1,1,9) do @(if not %a == 0 (echo %a)))|sort /R

But the next one didn't work,

(for /L %a in (1,1,9) do @(if %a NEQ 0 (echo %a)))|sort /R

What's the problem of NEQ in the piped block command?

more simplified,

This works, (if 3 == 3 echo yes)|sort

This doesn't work, (if 3 NEQ 2 echo yes)|sort

Part of my code.

@echo off
setlocal enabledelayedexpansion
set Unx_path=.....\bin\UnxUtils\
(
for /F %%a in ('^""%Unx_path%pclip.exe"^|"%Unx_path%sed.exe" -r "s/^^$/none/"^|"%Unx_path%sed.exe" -rf "script_file"^"') do @(
  if not "%%a" == "none" (
    "%Unx_path%grep.exe" -iEe "%%a" "file4search"|"%Unx_path%sed.exe" -r "s/^^[^,]+$/,&/";"s/^^([^.]+)[.][^.]+$/\1/"|"%Unx_path%gawk.exe" "BEGIN{FS=\",\";ORS=\" \"}{print $2}"|"%Unx_path%sed.exe" "s/%%a//I g";"s/.*/%%a &|/";"s/ -/ /g";"s/ |/\n/g";"s/ /\t/g";"s/~/\t/g"
  ) else (
    echo none
  )
)
)|"%Unx_path%gclip.exe"
exit /b

回答1:

try this:

set "myline=if 3 NEQ 2 echo yes"

( %myLin^e%)|sort

or from batch file (double expansion works differently from batch file and the console):

set "myline=if 3 NEQ 2 echo yes"

( %%myLine%%)|sort

The "mystery" was solved by jeb here and here . Though you are facing the issue before the pipe it is the same bug because the cmd creates two threads on each side of the pipe.

Here's how the for loop can be made to work:

set "line=if %a NEQ 0"
(for /L %a in (1,1,9) do @( %lin^e% echo %a))|sort /R


回答2:

For testing purposes the cmdcmdline variable can be used.
But this fails, when somewhere in the expression is a syntax error, as the complete code block will be dropped.

@echo off
echo dummy | (
echo %%cmdcmdline%
if 1 NEQ 2 echo Work
)

This results into "2" can't be syntactically evaluated here (From german "2" kann syntaktisch an dieser Stelle nicht verarbeitet werden.)

So exactly in the case of a syntax error, you can't see the cause!

I build a small debug variable for this case

@echo off
setlocal
(set \n=^
%=empty=%
)
set "debug=echo ### %%cmdcmdline%% ^) %%\n%%"
echo dummy | (
    %debug%
    if 1 NEQ 2 echo Work
)

When you add the %debug% you will see the cmdcmdline, but the code will not be executed, so you can examine how different modification take effect.

Like

...
| ( 
  %debug%
  break & if 1 NEQ 2 echo THIS FAILS
)

but

...
| ( 
  %debug%
  break ^& if 1 NEQ 2 echo This Works !!!
)

The trick is to add ^) %%\n%%, this closes the debug expression code block with ) and cancels further parsing of the remaing code by the linefeed %%\n%%.



回答3:

Base on the idea & method from npocmaka & jeb.

Another way was found to solve the error of IF command in piped code.

TO ADD AN ESCAPE SPACE AT RIGHT POSITION


For EQU NEQ LSS LEQ GTR or GEQ,

  1. ONE ^ has to be added after 1st compared string/number,

  2. At least TWO SPACE have to be added between this ^ and EQU NEQ LSS LEQ GTR or GEQ.

Example,

echo pipe|IF 1234^  GTR 124 echo true

true can be replaced by %^cmdcmdline% to see the parser.

(IF 1234^  NEQ 124 echo right)|more

For IF DEFINED & IF EXIST

  1. At least ONE SPACE has to be added after IF DEFINED or IF EXIST,
  2. At least TWO SPACE have to be added before variable or file/folder,
  3. ONE ^ has to be added between above added SPACE.

Example,

echo pipe|IF DEFINED ^  username echo true
echo pipe|IF EXIST ^  c:\windows echo true

true can be replaced by %^cmdcmdline% to see the parser

(IF DEFINED ^  username echo right)|more
(IF EXIST ^  c:\windows echo right)|more

But so far I didn't know why all of them have to work like above.


Something interesting,

echo pipe|if 123^   equ   ======  123 ======    echo %^cmdcmdline%

All = are ignored.

Updated (2016-04-23),

As npcmaka mentioned, =,, are also delimiters.

CASE 1

echo pipe|if not defined^===neq === echo %^cmdcmdline%

Outputs,

C:\Windows\system32\cmd.exe  /S /D /c" if not defined=== neq echo %cmdcmdline%"

CASE 2

echo pipe|if not defined^ === neq === echo %^cmdcmdline%

Outputs,

C:\Windows\system32\cmd.exe  /S /D /c" if not defined == = neq === echo %cmdcmdline%"

One space appears between ===.

CASE 3

echo pipe|if not defined^,,,neq ,,, echo %^cmdcmdline%

Outputs,

C:\Windows\system32\cmd.exe  /S /D /c" if not defined,NEQ echo %cmdcmdline%"

Some phenomenon can be found, which may be known how to parse...

  • neq is identified as variable in all CASE. (do set neq=blah before will occur no output for all CASE)

  • In CASE 2, == in Delimiter === seems be identified as comparison and auto be added one space after

  • According to the rule in CASE 2, in CASE 3, input neq is lowercase and output NEQ is uppercase. But in CASE 1 & 2, due to existed == before neq, this time, neq is kept in lowercase, which is not identified as comparison.

So it has several steps in parser period. But has some bugs of adding or deleting delimiters. Right?

CASE 4

The next code seems trigger cmd executing infinitely,

echo pipe|if not defined^ == echo %^cmdcmdline%

it runs like if not "defined" == "echo" recursive command



回答4:

I think the easiest work-around to get if statements working within pipes is the following (the outer surrounding pair of parentheses is mandatory):

(if^ comparison_expression conditional_command_line)
(if^ comparison_expression (conditional_command_line) else other_conditional_command_line)

I tested it with all keywords like not, exist and defined, else clauses, all possible comparison operators (==, EQU, NEQ, etc.), for either side of the pipe, and in cmd and batch-files.
Also I also successfully tested the if statements embedded within the body of a for loop (in which case the outer parentheses are no longer required).