0

1. Summary

I want to print variables from loop.

If I place echo $i after command from loop:

    Travis CI build passed.

elif I place echo $i before command from loop:

    I get exit code 1.

I don't find:

  1. why is this happening,
  2. how I can print variable before command.

2. What the script should do

I use HTMLTidy for validate my HTML.

I want, that HTMLTidy validate all HTML in folder output and subfolders of this folder.

See simply configuration of my project.

Working equivalent Windows batch script:

@echo off
FOR /R %%i IN (*.html) DO echo %%i & tidy -mq %%i

3. Steps to reproduce

I print in console:

cd output && bash ../tidy.sh

4. exit code 0

If tidy.sh:

shopt -s globstar
for i in **/*.html; do
    tidy -mq $i
    echo $i
done

Travis CI build passed:

$ cd output && bash ../tidy.sh
line 8 column 9 - Warning: trimming empty <span>
SashaInFolder.html
line 8 column 9 - Warning: trimming empty <div>
subfolder/SashaInSubFolder.html
The command "cd output && bash ../tidy.sh" exited with 0.
Done. Your build exited with 0.

5. exit code 1

Elif:

shopt -s globstar
for i in **/*.html; do
    echo $i
    tidy -mq $i
done

Travis CI build failed:

$ cd output && bash ../tidy.sh
SashaInFolder.html
line 8 column 9 - Warning: trimming empty <span>
subfolder/SashaInSubFolder.html
line 8 column 9 - Warning: trimming empty <div>
The command "cd output && bash ../tidy.sh" exited with 1.
Done. Your build exited with 1.

6. Not helped

  1. I try printf instead of echo → I get same behavior.
  2. I can't find answer to my question in Google.
  • It would appear tidy is the one exiting with 1. You could look into using tidy -mq "$i" || echo "tidy failed with $? on $i", in place of your current tidy command. It might help to explicitly exit 0 reaching the end of your script. – SYN Mar 04 '18 at 09:13

1 Answers1

2

The exit status of a for loop compound command is that of the last command executed in it¹.

for cmd in true false; do
  "$cmd"
done

Returns false (a non-zero exit status) because false was the last command run.

echo will return true as long as it successfully manages to write what we're telling it to.

If you want to return false/failure if any of the tidy command fails, you'd need to either record the failure, or exit upon the first failure:

#! /bin/bash -
shopt -s globstar
ok=true
for i in ./**/*.html; do
  if tidy -mq "$i"; then
    printf '%s\n' "$i"
  else
    ok=false
  fi
done
"$ok"

or:

#! /bin/bash -
shopt -s globstar
for i in ./**/*.html; do
  tidy -mq "$i" || exit # if tidy fails
  printf '%s\n' "$i"
done

That one could still return false/failure if printf fails (for instance, when stdout is to a file on a filesystem that is full).

If you want to ignore any error and your script to return true/success in any case, just add a true or exit 0 at the end of your script.


¹ at least for the body part. For for i in $(exit 42); do :; done, most shells return 0 (AT&T ksh being the exception). They all return 0 for for i in; do :; done < "$(echo /dev/null; exit 42)".

  • Regarding the paragraph about ignoring errors: Using exit will be more efficient than true, since exit is builtin to the shell, but true will need to spawn a process. Even quicker would be a simple no-op (:) ! – user1404316 Mar 06 '18 at 18:08
  • @user1404316, true is a built-in in all modern Bourne-like shells including bash, just like : and exit (well those are special builtins, but that makes no difference in that regard). And true and false are the commands to express boolean values there. – Stéphane Chazelas Mar 06 '18 at 19:30
  • @StéphaneChazelas : I just now double-checked and get conflicting indications in bash version 4.4.12. man builtins does not include true in the synopsis at the beginning of the page, or anywhere else. OTOH, type true does indicate that it is a shell builtin. Maybe a documentation bug. man bash also does not document true. – user1404316 Mar 06 '18 at 19:43
  • @user1404316, true/false builtins were added in bash-2.0 (96) and zsh-2.0 (91). ksh always had them, though initially (including in ksh86) true was defined as a builtin alias for ":" and false for "let 0". ash originally had true but not false (added later in the 90s on the various BSDs). – Stéphane Chazelas Mar 06 '18 at 20:44