How to change current directory inside IF statemen

2019-07-29 16:39发布

问题:

I'm trying to install an app from my PC running Windows 10 using adb and I need to use command cd before I'm installing the app.

But I see a strange behavior. If I use the cd command before the if statement, it's working well. But if I use the cd command inside the if statement, it is not working.

Here is what I tried:

  1. This one is working well:

    cd %~f0\..\..\apps\app_sample\samples\sample_app\build\outputs\apk
    if "%arg1%" == "aaa" (
        adb install %CD%\sample_app-debug.apk
    )
    
  2. This one results on execution in an error:

    if "%arg1%" == "aaa" (
        cd %~f0\..\..\apps\app_sample\samples\sample_app\build\outputs\apk
        adb install %CD%\sample_app-debug.apk
    )
    

    The error output is:

    Failed to stat
    C:\Users\ntuser\Documents\workspace\Team\script\sample_app-debug.apk:
    No such file or directory

    It looks like it did not do the cd command in this case.

回答1:

Whenever Windows command interpreter cmd.exe encounters the beginning of a command block starting with ( and ending with matching ), the entire block is preprocessed before the command left to the command block is executed at all. This means all environment variables using %variable% syntax are replaced by current value of the referenced environment variable before executing the command which later executes the entire command block. This behavior can be seen on running a batch file from within a command prompt window without @echo off being usually at top of the batch file.

This means on your batch file with the IF command and the command block to execute on condition being true that %CD% is replaced by current directory before the command IF is executed and so also before the command CD is executed at all.

There are two possibilities:

  1. explicitly enable delayed environment variable expansion and reference the environment variable in the command block with using syntax !variable!
    or
  2. don't use a command block by using GOTO or a subroutine and CALL.

Solution 1 with enabled delayed environment variable expansion:

setlocal EnableDelayedExpansion
if "%arg1%" == "aaa" (
    cd "%~dp0..\apps\app_sample\samples\sample_app\build\outputs\apk"
    adb.exe install "!CD!\sample_app-debug.apk"
)
endlocal

Solution 2 with not using a command block at all and instead use GOTO:

if "%arg1%" == "aaa" goto AAA
rem Other commands
goto :EOF

:AAA
cd "%~dp0..\apps\app_sample\samples\sample_app\build\outputs\apk"
adb.exe install "%CD%\sample_app-debug.apk"
goto :EOF
rem Or use a different GOTO to continue processing somewhere else in batch file.

Another solution 2 is using a subroutine and command CALL:

if "%arg1%" == "aaa" call :AAA
rem Other commands executed even if above condition is true.

rem Avoid a fall through to the subroutine below.
goto :EOF

:AAA
cd "%~dp0..\apps\app_sample\samples\sample_app\build\outputs\apk"
adb.exe install "%CD%\sample_app-debug.apk"
rem Exit the subroutine and continue on line below calling it.
goto :EOF

But I think your code can be even more easier because it is most likely not necessary to specify the *.apk file with full path.

if "%arg1%" == "aaa" (
    cd "%~dp0..\apps\app_sample\samples\sample_app\build\outputs\apk"
    adb.exe install sample_app-debug.apk
)

And perhaps also working is:

if "%arg1%" == "aaa" adb.exe install "%~dp0..\apps\app_sample\samples\sample_app\build\outputs\apk\sample_app-debug.apk"

It is better to use here %~dp0 which references the drive and path of the batch file ending with a backslash instead of %~f0 which is the name of the batch file with file extension and full path.

For example with batch file being C:\Temp\Test\Install.bat the command line

cd "%~f0\..\..\apps\app_sample\samples\sample_app\build\outputs\apk"

results in execution of

cd "C:\Temp\Test\Install.bat\..\..\apps\app_sample\samples\sample_app\build\outputs\apk"

and the current directory is C:\Temp\apps\app_sample\samples\sample_app\build\outputs\apk after this command with in real invalid directory name Install.bat in path which is not a directory.

The command line

cd "%~dp0..\apps\app_sample\samples\sample_app\build\outputs\apk"

is much better as it results in execution of

cd "C:\Temp\Test\..\apps\app_sample\samples\sample_app\build\outputs\apk"

which makes also C:\Temp\apps\app_sample\samples\sample_app\build\outputs\apk the current directory, but with a completely correct path in comparison to above cd command line.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /? ... explains %~dp0 and %~f0.
  • cd /?
  • endlocal /?
  • goto /?
  • if /?
  • rem /?
  • set /? ... explains delayed environment variable expansion on an IF and a FOR example.
  • setlocal /?

I don't have adb installed. So I can only assume that it is an executable with file extension .exe and not a batch file with file extension .bat or .cmd which would require the usage of call adb.bat ... or call adb.cmd ... in this batch file.