1

I'm currently using bash 4.1 and I'm using a function to perform a SVN cat on a repository file. After that, it iterates over each line to perform some transformations (mostly concatenations and such). If said file does not exist, the script should stop with an error message. The script is as follows:

function getFile {
    svnCat=`svn cat (file) 2>&1` 
    if [[ -n $(echo "$svnCat" | grep "W160013") ]]; then # W160013 is the returned value by SVN stderr case a file doesn't exist
        echo "File doesn't exist" >&2
        exit 1
    else
        echo "$svnCat" | while read -r; do
            #Do your job
        done
    fi
}

function processFile{
    while read -r do
        #do stuff
    done < <(getFile)

    #do even more stuff
}

However, in situations where a file does not exist, the error message is printed once but the script keeps executing. Is there a way to detect that the while looped failed and should stop the script completely?

Can't use set -e option since I require to delete some files that were created in the process.

Update: I've tried to add || exit after the done command as follows:

function processFile{
    while read -r do
        #do stuff
    done || exit 1 < <(getFile)

However, the script is waiting for user output and when I press enter, it executes the content in the while loop

Sock Em
  • 19
  • 2
  • `{ while read -r; do ...stuff...; done || exit 1; } < <(getFile)` – Charles Duffy Feb 19 '19 at 18:18
  • That said, this question would really be better written as "how can I detect the exit status of a process substitution?", and it's a good question (only very recent versions of bash *have* a good answer at all -- so the failure to specify which specific version this question is about is a Problem). – Charles Duffy Feb 19 '19 at 18:19
  • (Also, `set -e` wouldn't help even if you *could* use it, because `set -e` doesn't look inside process substitutions any more than anything else does). – Charles Duffy Feb 19 '19 at 18:19
  • BTW, in general, I would advise against trying to capture stderr and parse specific error codes. Better to do something like `if content=$(svn cat "$file"); then ...; else exit; fi` -- that way you don't need to know or care if svncat fails because of error W160013 or on account of some other error that may not have even been assigned yet. (`else exit` works because the default status used by `exit` or `return` is `$?`, so it passes the exit status of the `svn cat` command through so long as you don't do anything that would set a different exit status in the interim). – Charles Duffy Feb 19 '19 at 18:23
  • ...or even `content=$(svn cat "$file") || exit` as a one-liner, so your function only proceeds to continue to exit if `svn cat` has a nonzero status. – Charles Duffy Feb 19 '19 at 18:25
  • BTW, re: `set -e`, it's best avoided in general. See [the exercises in BashFAQ #105](http://mywiki.wooledge.org/BashFAQ/105#Exercises) -- if you can answer them all off the top of your head you might know what you're getting into enough to use `set -e` safely (on bash alone; there are also [widespread incompatibilities among and between other shells' implementations of `set -e`](https://www.in-ulm.de/~mascheck/various/set-e/)), but typically, knowing enough of how `set -e` works under the hood to answer those exercises correlates with not wanting to use it. :) – Charles Duffy Feb 19 '19 at 18:35

1 Answers1

1

Tracking exit status from a process substitution is tricky, and requires a very modern version of bash (off the top of my head, I want to say 4.3 or newer). Prior to that, the $! after the <(getFile) will not be correctly populated, and so the wait will fail (or, worse, refer to a previously-started subprocess).

#!/usr/bin/env bash

### If you *don't* want any transforms at this stage, eliminate getFile entirely
### ...and just use < <(svn cat "$1") in processFile; you can/should rely on svn cat itself
### ...to have a nonzero exit status in the event of *any* failure; if it fails to do so,
### ...file a bug upstream.
getFile() {
    local content
    content=$(svn cat "$1") || exit  # pass through exit status of failed svn cat
    while read -r line; do
      echo "Generating a transformed version of $line"
    done <<<"$content"
}

processFile() {
    local getFileFd getFilePid line

    # start a new process running getFile; record its pid to use to check exit status later
    exec {getFileFd}< <(getFile "$1"); getFilePid=$!

    # actual loop over received content
    while IFS= read -r line; do
      echo "Retrieved line $line from process $getFilePid"
    done <&"$getFileFd"

    # close the FIFO from the subprocess
    exec {getFileFd}<&-

    # then use wait to wait for it to exit and collect its exit status
    if wait "$getFilePid"; then
      echo "OK: getFile reports success" >&2
    else
      getFileRetval=$?
      echo "ERROR: getFile returned exit status $getFileRetval" >&2
    fi
}
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
  • `{getFileFd}<`?? What wondrous new magic is this, and where may I find the recipe? :) – Paul Hodges Feb 19 '19 at 18:53
  • @PaulHodges, automatic FD assignment was introduced in bash 4.1; it picks an unused file descriptor number and assigns it to the variable named (in this case) `getFileFd`, so you don't need to go tracking which numbers were already hardcoded elsewhere in your program. Not coincidentally, 4.1 is also the first version where `<&"$getFileFd"` (using an expansion to get a file descriptor number in a redirection) works. Search for `{varname}` in https://www.gnu.org/software/bash/manual/html_node/Redirections.html – Charles Duffy Feb 19 '19 at 18:53
  • Awesome. THIS is why I come here, lol. Even if you have read the whole docs over, you sometimes miss (or just gloss over) details like this until you see them in context. – Paul Hodges Feb 19 '19 at 19:01