1

I have this function in a bash script:

ver_to_num() { 
  printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' ')
}

ver_to_num 3.18.1.1 works from the command line. But when used in a script I get:

printf: 3 18 1 1: invalid number

Any idea why I would get this error in this context?

8
  • 1
    I get 003018001001 (GNU bash 5.2.37 / GNU coreutils 9.6)
    – pmf
    Commented Apr 7 at 16:00
  • 4
    that's the error message you'd get if you tried printf "%03d" '3 18 1 1', which would happen if you did something like wrapping the "$(echo ... | tr ....)" in double quotes; is this an exact copy of your code?
    – markp-fuso
    Commented Apr 7 at 16:10
  • 4
    fwiw, you could eliminate the double subshell invocation ($(echo ... | tr ...)) with a simple parameter substitution printf "03d ..." ${1//./ }
    – markp-fuso
    Commented Apr 7 at 16:12
  • 2
    Please post the script that gets the error.
    – Barmar
    Commented Apr 7 at 16:18
  • 5
    I wonder if perhaps you ran IFS= earlier in your script? That would explain the behavior described, as your code depends on word-splitting on IFS to behave as-intended (something both answers work around). Commented Apr 7 at 16:53

2 Answers 2

6

Your code is leaving the output of echo exposed to the shell for interpretation. Don't do that, always quote strings/variabes until if/when you need to remove the quotes. Populate then use a quoted array of the values instead:

ver_to_num() {
  local -a ver
  IFS=. read -ra ver <<< "$1"
  printf '%03d' "${ver[@]}"
}

See https://mywiki.wooledge.org/Quotes and https://mywiki.wooledge.org/BashFAQ/050 for more information.

Unlike the currently accepted answer the above:

  1. Will work for any number of digits in the version string, not just 4.
  2. Won't corrupt variables in the calling code that have the same name(s) as variables used inside the function.
3
  • 1
    Could just make it %03d and let printf repeat the format string as many times as needed; right now this effectively hardcodes four items (as the only number it'll work correctly for) just as much as the OP's original code does. (Well, not quite -- as originally written this'll also work right for multiples of four, but there's no need for that constraint I can see) Commented Apr 7 at 16:52
  • @CharlesDuffy good point, updated now, thx.
    – Ed Morton
    Commented Apr 7 at 16:58
  • 2
    BTW, insofar as the direct question is about the CLI and their script differing, might point out that this happens in an interpreter where one changes IFS from its default value, and presumably their script did so but they never did the same in their interactive shell. (More than just presumably, given comments on the question). Commented Apr 7 at 21:51
3

try this:

This eliminates issues caused by strange whitespace or unexpected extra fields.

ver_to_num() {
  IFS='.' read -r a b c d <<< "$1"
  printf "%03d%03d%03d%03d" "$a" "$b" "$c" "$d"
}
0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.