1

I am trying to convert a whole BATCH script to SHELL script with the help of this sort of converter manual.

I have almost finished, but I am struggling to convert this FOR LOOP:

for /f "tokens=*" %%a in ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') do (
    if not errorlevel 1 goto :cannot_patch
)

I know that for /f is

Loop command: against a set of files - conditionally perform a command against each item.

However, as I am a noob to SHELL SCRIPT (and BASH as well), my best try was:

for -f "tokens=*" a in ( '$ADB shell mkdir /usr/ui/^|find /i "File exists"' ); do
    if [ $? -nq 1 ]
    then
        cannot_patch
    fi
done

which does not work, resulting in a Syntax error: Bad for loop variable.

Any hint, link, or suggestion would be very much appreciated.

EDIT

I am trying to understand what exactly ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') is doing.

I thought those were sh commands, but it turns out I was wrong and that find /i is trying to

Search(ing) for a text string in a file & display all the lines where it is found.

(https://ss64.com/nt/find.html)

| is the pipe operator and "File exists" should be the error thrown by mkdir in case the command tries to create a directory that already exists.

So I think I could probably write this easier, but still, what does the ^ symbol in /usr/ui/^ do? Is it a regex?

EDIT2

It seems indeed that @glenn_jackman is right: probably I'd better understand what the code is trying to do.

So to give a better context, here is a bit more code of the original batch:

for /f "tokens=*" %%a in ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') do (
    if not errorlevel 1 goto :cannot_patch
)
:cannot_patch
echo Error: Cannot create directory!
echo Patch is already installed or system files exist and might be overwritten.
choice /m "Do you want to continue"
if errorlevel 2 goto :END
goto :continue_patch

To my understanding, the code is trying to run the adb shell mkdir command and, if it fails (throwing the "File exists" error), it will ask to the user if he/she wants to continue regardless.

So in this case, I guess the real problem is trying to write a code that does the same in SH, probably without the need of a for loop.

Still, I am finding it out...

aschipfl
  • 33,626
  • 12
  • 54
  • 99
umbe1987
  • 2,894
  • 6
  • 35
  • 63
  • 1
    The `for /F` loop is there to capture the output of the command line `%adb% shell mkdir /usr/ui/|find /i "File exists"`; not sure, but isn't there the `$` operator for such things? – aschipfl Nov 06 '20 at 14:47
  • Thanks for the feedback. It makes sense to use the `$` operator "somewhere". But...I sincerely don't know where exaclty I am suppose to place it... Also, I don't know how to replace the `"tokens=*"` option. – umbe1987 Nov 06 '20 at 14:54
  • 2
    This smells like an XY problem: Take a step back and describe what you want to do. It looks like you want to call `$ADB shell mkdir ...` and then do something if the directory already exists. If that's the case, then in sh or bash you don't need a for loop at all. I don't know CMD very well, but it seems that the CMD `for` command is designed to do tons of stuff beyond just iterating over a list of things. – glenn jackman Nov 06 '20 at 15:27
  • Exactly! please, see my edit to see that I came to the same hypothesis of not using a for loop (or at least, simplify the code, I am still trying to understand it so please forgive me). From the lines following this in the main code (I'll add this as well to my question) I read `echo Patch is already installed or system files exist and might be overwritten.`, so this is what I am testing. – umbe1987 Nov 06 '20 at 15:34
  • If `%adb% shell mkdir /usr/ui/` is just there to create a directory, why not just suppress an error message and go on? like in `cmd`, you would do `mkdir "D:\some\path" 2> nul`… – aschipfl Nov 06 '20 at 19:43

3 Answers3

1

The general syntax of your loop matches https://ss64.com/nt/for.html You should also look over https://ss64.com/nt/errorlevel.html and this SO reference that lists/explains error codes.

From the SO article, FOR /F │ 1 = No data was processed.
But the errorlevel page says

IF ERRORLEVEL 1 will return TRUE whether the errorlevel is 1 or 5 or 64

and the SO article also says

if %errorlevel% equ 5 (
    echo Can not access the directory, check rights
)

Apparently ^ is a CMD escape character, used here to escape the pipe character being passed as part of a compound command that (I'm guessing...) creates a directory and checks for a failure, so I'm parsing this as an attempt to make the directory, detect if it already exists, and respond accordingly.

I think in bash this would be written this way:

mkdir /usr/ui/ || cannot_patch 
aschipfl
  • 33,626
  • 12
  • 54
  • 99
Paul Hodges
  • 13,382
  • 1
  • 17
  • 36
  • I'm reading `if not errorlevel 1` as a lazy way of writing `if "!errorlevel!"=="0"`, but I usually don't mess with errorelevel beyond "0" and "not 0". – SomethingDark Nov 06 '20 at 17:13
1
for /f "tokens=*" %%a in ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') do (
    if not errorlevel 1 goto :cannot_patch
)

runs the command %adb% shell mkdir /usr/ui/ then searches that output for the string File exists. if not errorlevel 1 means "if %errorlevel% is not greater than or equal to 1," which is a weird and awkward way of just saying if "%errorlevel%"=="0", which would mean that the string was found. If it's found, the script then goes to the label :cannot_patch.

In bash, the way to process the output of a command is to use $() instead of for /f, so

$(${adb} shell mkdir /usr/ui/ | grep "File exists") && cannot_patch

This assumes that the variable adb is set and you've got a function in your shell script somewhere called cannot_patch.

Note that the only difference between my answer and Paul Hodge's answer is that I'm assuming that you still need ADB to be called.

SomethingDark
  • 13,229
  • 5
  • 50
  • 55
  • Thanks! I edited the question inserting a link to the complete batch script I am trying to covert. Yes, indeed ADB is needed as it connects to the router I am trying to patch. I will let you know asap what I will achieve using your suggestions! – umbe1987 Nov 06 '20 at 19:27
  • I actually solved differently but yours and others answers really helped me a lot. Sorry for replying so late. a link to what I did: https://github.com/umbe1987/R216-Z/blob/c49708deabba80b433c2a24680dd98adcda0eca1/R216-Z_patch/R216-Z_patch.sh#L105-L110 – umbe1987 Dec 03 '20 at 09:34
1

Sorry, but I have got bad news for you — the batch code you want to transcript is wrong:

for /f "tokens=*" %%a in ('%adb% shell mkdir /usr/ui/^|find /i "File exists"') do (
    if not errorlevel 1 goto :cannot_patch
)

This is not going to do what you probably think it does. The following steps actually happen:

  • It executes the command %adb% shell mkdir /usr/ui/ and pipes (|) its output into the command find /i "File exists", but all this happens in a new cmd.exe instance created by the for /F loop.
  • The for /F loop, well, loops over the lines of the output of the aforesaid commands after they have completed; the new cmd.exe instance is destroyed before starting to iterate.
  • BUT: The ErrorLevel value resulting from the %adb% …|find … command line is not available in the body of the for /F loop, because this executes in the cmd.exe instance the batch script itself runs in too. This means, that the condition if not errorlevel 1 in the loop body actually reacts on a preceding command, unintentionally.

Since, as I believe, you simply want to test whether the output of %adb% shell mkdir /usr/ui/ contains the sub-string File exists in a case-insensitive manner, you actually just need this:

rem /* Just using `echo` for demonstration purposes, to be replaced;
rem    moreover, you may also omit the `&&` or the `||` part: */
%adb% shell mkdir /usr/ui/ | find /i "File exists" && (
    echo ErrorLevel 0: sub-string found.
) || (
    echo ErrorLevel 1: sub-string NOT found!
)

Unfortunately, I cannot tell you exactly how to translate this, but find might probably have to be substituted by grep, and the operators && and || could be replaced by checking the exit code returned in $?, unless there are similar operators…

aschipfl
  • 33,626
  • 12
  • 54
  • 99