Question

I have the following function defined in bash:

function print_revision {
    printf "Revision %s (%s)\n%s\n\n" "$1" "$(date --utc +%d.%m.%Y,\ %H:%M\ UTC)" \
    "----------------------------------------"
}

I also have a variable, $CHANGES with some text in it. What I would need to do is prepend the string printed in print_revision to the $CHANGES variable.

I've tried the following but it doesn't seem to work:

CHANGES="$(print_revision $CURRENT_SHORT_REVISION)"$CHANGES

It prepends everything from print_revision but it goes through the last two newline characters, concatenating the contents of $CHANGES right after the ----- part.

How can I achieve what I need?

Was it helpful?

Solution 3

The simplest fix is to just print the current value of $CHANGES inside the command substitution:

CHANGES="$(print_revision $CURRENT_SHORT_REVISION; echo "$CHANGES")"

OTHER TIPS

Don't use command substitution here.

printf -v var_name format_string arg1 arg2 ...

...stores the output of the printf directly into var_name, without any subshell involved (more efficient!), and no command substitution (no stripping of trailing newlines!)

Thus:

print_revision_to() {
    printf -v "$1" \
      "Revision %s (%s)\n%s\n\n" \
      "$2" \
      "$(date --utc +%d.%m.%Y,\ %H:%M\ UTC)" \
      "----------------------------------------"
}

# ...thus, to put a header for revision 13 into "$changes", you'd run...
print_revision_to changes 13

...or, if you want to do this all at once...

prepend_revision_header_to() {
    printf -v "$1" \
      "Revision %s (%s)\n%s\n\n%s" \
      "$2" \
      "$(date --utc +%d.%m.%Y,\ %H:%M\ UTC)" \
      "----------------------------------------"
      "${!1}"
}

# ...thus, to modify the existing value of "$changes" by putting a header
# at the beginning, you'd run...
prepend_revision_header_to changes 13

Command substitution strips off newlines at the end of the string. The typical use case is that the output ends with a newline and is either going to be printed with another newline or concatenated with another string to make up a single line (e.g. echo $(echo part1)$(echo part2)).

If you can't or don't want to change the print_revision function, print a non-newline character after it, then strip it off.

new_revision=$(print_revision $CURRENT_SHORT_REVISION; echo a); new_revision=${new_revision%a}
CHANGES=$new_revision$CHANGES

Alternatively, put as many newlines as you want in the concatenation.

CHANGES="$(print_revision $CURRENT_SHORT_REVISION)

$CHANGES"
$ man bash
...
Command Substitution
...
Bash performs the expansion by executing command and replacing the com‐
mand substitution with the standard output of  the  command,  with  any
trailing newlines deleted.

Hence the use of $(...) is removing the trailing newlines.

If you want to keep a separate print_revision function then one approach would be to avoid trailing newlines in its output by appending a sentinel in print_revision, then stripping that off when you use it:

function print_revision {
    printf "Revision %s (%s)\n%s\n\n___" ...
}

REVISION="$(print_revision $CURRENT_SHORT_REVISION)"
CHANGES="${REVISION%___}$CHANGES"

One possibility would be to pass the existing value (if any) into the function:

prepend_revision() {
  printf "Revision %s (%s)\n%s\n\n%s" \
         "$1" "$(date --utc +%d.%m.%Y,\ %H:%M\ UTC)" \
         "----------------------------------------" "$2"
}

CHANGES=$(prepend_revision "$CURRENT_SHORT_REVISION" "$CHANGES")

Note that if you call prepend_revision with only one argument, the output is exactly the same as the existing print_revision function, so it can be used either to create or prepend.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top