0

This script, which i source in my terminal, offers the function _cphetzner, that is used to create a git repository. This function uses another function _feed_variable that prompts user for missing variables.

My questions at this point are:

  1. Is this the right way to return from functions - by using return 1?
  2. If set -o errexit is uncommented, does the script abort on any return 1 (e.g. from function _feed_variable or only the one that return to the shell?
  3. If set -o errexit is uncommented, what is the right way to exit the function without closing the terminal?
  4. I feel like i am not designing the script in a way that it cleans up on errors. Any recommendations?

#!/usr/bin/env bash

# +---------------+
# | Bash settings |
# +---------------+
# abort on nonzero exitstatus
# set -o errexit
# abort on unbound variable
# set -o nounset
# don't hide errors within pipes
# set -o pipefail

# +----------------+
# | Bash Variables |
# +----------------+

# +----------------+
# | Script Content |
# +----------------+

function _feed_variable() {

  local prompt_msg=$1
  local var_to_set=$2

  echo "$prompt_msg"

  read answer

  case $answer in
    [Yy]*) 
      echo "Enter $2: "
      read new_value
      eval $var_to_set="'$new_value'"
      return 0
      ;;
    [Nn]*)
      echo "Clearing up and quitting ..."
      return 1
      ;; 
  esac
}

function _cphetzner() {
    pushd $PWD > /dev/null
    POSITIONAL_ARGS=()

    REPO_NAME=
    # TARGET_FOLDER=$PWD/$REPO_NAME
    TARGET_FOLDER=
    SOURCE_TEMPLATE=    
    
    while [[ $# -gt 0 ]]; do
        case $1 in
            -r|--repository)
                REPO_NAME="$2"
                shift
                shift 
                ;;
            -s|--sourcetemplate)
                SOURCE_TEMPLATE="$2"
                shift
                shift 
                ;;
            -t|--targetfolder)
                TARGET_FOLDER="$2"
                shift
                shift
                ;;
            -*|--*)
                echo "Unknown option $1"
                exit 1
                ;;
            *)
                POSITIONAL_ARGS+=("$1") # save positional arg
                    shift # past argument
                    ;;
        esac
    done

  #=====[ Print Info ]==================================

  echo "SOURCE_TEMPLATE is:\t" $SOURCE_TEMPLATE
  echo "TARGET_FOLDER is:\t" $TARGET_FOLDER
  echo "{POSITIONAL_ARGS is [@]}:\t" ${POSITIONAL_ARGS[@]}
  echo "REPO_NAME is:\t" $REPO_NAME

  set -- "${POSITIONAL_ARGS[@]}"

  if [[ -n $1 ]]; then
    echo "Last line of file specified as non-opt/last argument:"
      tail -1 "$1"
  fi

  #=====[ Create folder and initiate repository ]=======

  # Check if user input has repo name
  if [[ -z "$REPO_NAME" ]]; then
    _feed_variable "Do you want to feed me a repository name? [y/n]" "REPO_NAME" ||
    return 1
  fi

  # Check if repository in given path exists
  if [[ -z "$TARGET_FOLDER" ]]; then
    _feed_variable "Do you want to feed me a target name? [y/n]" "TARGET_FOLDER" ||
    return 1
  fi

  while [[ -d "${TARGET_FOLDER}/${REPO_NAME}" ]]
  do
    echo "Repository already exist in this folder" && 
    _feed_variable "Do you want to feed me another target name? [y/n]" "TARGET_FOLDER" ||
    return 1
  done

  # Create folder 
  mkdir -p $TARGET_FOLDER/$REPO_NAME 

  # # Check if template folder exists
  #   [[ -z $SOURCE_TEMPLATE ]] && echo "No template folder" ||  
  #       cp -a $SOURCE_TEMPLATE/ $TARGET_FOLDER/$REPO_NAME


  #====[ Initiate Repo ]================================
  
  cd $TARGET_FOLDER/$REPO_NAME
    git init
    git remote add origin hetzner:/yolo/$REPO_NAME.git

    git add .
    git commit -m "Initial commit" 

  echo -e "[DONE] Repo $REPO_NAME created successfully"
  echo -ne "Do you want to push repo to Hetzner now? [y/n]"
    
  read answer

    case $answer in
    [Yy]*) 
          echo "Pushing to Hetzner ..."
      git push -u -f origin master
      ;;
    [Nn]*) 
      ;;
    esac

    echo "Have a great day ..." 

  return 0

    popd > /dev/null
}


1 Answer 1

1

The shell considers an exit status of zero as truthy/succesful, and a nonzero status as falsy/failing. So, if the function succeeds, return 0 would likely be considered more right than return 1.

As for set -e/set -o errexit, it doesn't trigger on just any command/function, e.g. it ignores those that are used as conditions or where the exit status is otherwise ignored, like the non-rightmost parts of a pipeline. It doesn't matter what nesting level the failure happens, though. But it does have some complications that make it hard to use.

fail() { return 1; }
# these won't trigger set -e
fail | cat
fail || true
if fail; then echo x; fi
# so this prints
echo one

# these also won't trigger it
try() {
    fail
    echo two
}

try || true
( fail; echo three ) || true

See https://mywiki.wooledge.org/BashFAQ/105 for the oddities of set -e.

3
  • Thanks for the answer. Should return 1 for example be used in a function where the user hasn't entered an input like in my script? or you mean with falsy that something bash-wise is causing an error?
    – MonkaS
    Feb 4, 2023 at 9:23
  • @MonkaS, you'll have to decide what counts as something to return a failing status on. It depends on what you want to do: if the user has made an input error, you should probably print some message explaining that. But if you do that inside an inner function or in the main program is pretty much up to you.
    – ilkkachu
    Feb 4, 2023 at 10:26
  • When I say "falsy", I mean a value that is treated as "false" in a boolean context. The shell doesn't have a separate type for boolean values, so conditionals like if and || have to somehow interpret integers to decide which choice to make. Those integers as such aren't "true" or "false", just numbers.
    – ilkkachu
    Feb 4, 2023 at 10:30

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .