hjkml
hjkml

Reputation: 253

How can a shell script get input from an external editor (like Git does)?

I'd like a shell script to pause, get input from an external editor, and then resume. Something like this pseudocode for a minimal example:

testScript(){
  content=""
  # set value of content using vim...
  echo "$content"
}

I don't want to use a package, just Bash.

Upvotes: 2

Views: 1049

Answers (2)

gniourf_gniourf
gniourf_gniourf

Reputation: 46843

#!/bin/bash

# This uses EDITOR as editor, or vi if EDITOR is null or unset
EDITOR=${EDITOR:-vi}

die() {
    (($#)) && printf >&2 '%s\n' "$@"
    exit 1
}

testScript(){
  local temp=$(mktemp) || die "Can't create temp file"
  local ret_code
  if "$EDITOR" -- "$temp" && [[ -s $temp ]]; then
      # slurp file content in variable content, preserving trailing blank lines
      IFS= read -r -d '' content < "$temp"
      ret_code=0
  else
      ret_code=1
  fi
  rm -f -- "$temp"
  return "$ret_code"
}

testScript || die "There was an error when querying user input"
printf '%s' "$content"

If you don't want to preserve trailing blank lines, replace

IFS= read -r -d '' content < "$temp"

with

content=$(< "$temp")

You could also add a trap so that the temp file is removed in case script is stopped between the temp file creation and removal.

We're trying to check that everything is going all right through the whole process:

  • the function testScript aborts early if we can't create a temp file;
  • when we edit the temp file, we check that the editor runs and ends fine, and then we also explicitly check that the user entered at least one character by checking that the file exists and is not empty with [[ -s $temp ]].
  • we slurp the file in variable content, using the builtin read. Used this way, we are not trimming any leading or trailing blanks, nor trailing newlines. The variable content contains the trailing newline! you can trim it with ${content%$'\n'}; another possibility is to not use read altogether but content=$(< "$temp").
  • we explicitly check that the function returns with no errors before proceeding.

Upvotes: 5

ASG
ASG

Reputation: 13

Piggy backing off of Etan Reisner's suggestion you could just set a /tmp file to be used for the editing. Then read in the contents. Unless that file is going to be used to aggregate some data during other portions of your script you probably want to clear the contents when you're done setting the $content var.

testScript() {
  content=""
  contentFile=/tmp/input
  vim $contentFile
  content=`cat $contentFile`
  cat /dev/null > $contentFile
}

testScript
echo $content

Upvotes: 0

Related Questions