I have a POSIX-compatible install script which determines the latest version of some software by grepping info from a json blob retrieved from a software registry:

meta=$(wget -qO - https://registry.npmjs.org/after-dark/latest)
grep --version | grep "BSD grep" 1>/dev/null && vers=$(echo "$meta" | grep -oE '"version":(\d*?,|.*?[^\\]",)' | tr -d 'version": ,')
grep --version | grep "GNU grep" 1>/dev/null && vers=$(echo "$meta" | grep -oP '"version":(\d*?,|.*?[^\\]",)' | tr -d 'version": ,')

I need to make this software compatible with BusyBox, which, like BSD grep, uses -E for extended regex support. It doesn't, however, support --version flag.

If I run busybox grep I see the version of busybox. Should I just use that to add compatibility? Is there a better way I could be doing this to remove the code smell without installing dependencies.

  • You could also more generally try to detect the distribution and then use the correct syntax for the grep binary: unix.stackexchange.com/questions/6345/… — also try to use absolute paths to /bin/grep. – slhck Aug 7 at 13:50
  • 1
    IMHO you already answered your question, - use busybox's grep. It would be consistent across different platforms. The only thing is that busybox isn't installed by default on all systems. Another approach is to use awk for parsing, it comes practically with all Unix based systems by default, even on stripped down platforms, just use only compatible awk's commands that exist in all its flavors (classic awk,nawk,gawk...), it would be much more portable. – Alex Aug 7 at 14:44
  • Thanks, Alex. This answer on SO provides some history regarding the addition of PCRE to grep which postdates POSIX and, therefore, your suggestion to use awk for better portability will be taken into consideration. It seems the root of the problem, however, may either be a bug or undocumented feature in BSD grep when using extended regex as suggested here. – Josh Habdas Aug 11 at 10:52
up vote 0 down vote accepted

Couldn't wait any longer so I put some fresh eyeballs on this and came up with the following solution which tested well using GNU, BSD and BusyBox grep, and improves support for semver:

egrep -o "\"version\".*[^,]*," <<< "$meta" | cut -d ',' -f1 | cut -d ':' -f2 | tr -d '" '

It works by calling ERE grep with a regex (with a hat tip) and uses cut to slice off the trailing comma, grab everything on the right-hand side of the :, trim the string and remove any double-quotation marks present.

Given $meta of {"version":"5.5.0-beta4",} will return exactly 5.5.0-beta4.
Given $meta of "version" : 1, will return exactly 1.

Assumes version field is not the last field in the JSON blob which is a trade-off for readability.

Applied to the original question results in:

vers=$(echo "$meta" | egrep -o "\"version\".*[^,]*," | cut -d ',' -f1 | cut -d ':' -f2 | tr -d '" ')

Eliminating the need to detect the version of grep and removing the code smell.

UPDATE:

I was eventually led to a more elegant solution using sed in a follow-up question on SO.

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.