0

I am working on a Automator service and in my situation I have stdin as B-Funny Flash Nonfiction 202105131635 and I want to get to B-Funny Flash Nonfiction 202105131636 incriminating the "5" by 1 to "6".

I'd think I'd first want to separate the text from the number before doing the add 1 then rejoin them?

Would egrep or sed or awk be best?

Tips?

2 Answers2

2

Bash has simple integer arithmetic built in.

str='B-Funny Flash Nonfiction 202105131635'
# parse into prefix and number
num=${str##*[!0-9]}
prefix=${str%$num}
echo "$prefix$((num+1))"

The parameter expansion ${var#pat} produces the value of the variable var with any prefix matching pat removed; % does the same for suffixes, and doubling the operator changes to matching the longest possible pattern match instead of the shortest. The pattern *[!0-9] matches a string which ends on a character which isn't a number; in this context, it retrieves the prefix, i.e. everything up to just before the first digit. (If your prefix could contain numbers, too, this needs tweaking. Probably switch to removing all digits from the end, then extracting the removed numbers; but I guess this will require an unattractive temporary variable.)

Finally, the secret sauce which evaluates an arithmetic expression is the $((...)) arithmetic context.

For more involved number crunching, try bc or Awk. In fact, this could be a one-liner in Awk:

awk '{ $NF +=1 }1' <<<"$str"

The here string passes the value as standard input to Awk, which increments the last field $NF. The final 1 is a common Awk shorthand for "print all input lines to output".

tripleee
  • 175,061
  • 34
  • 275
  • 318
  • Thanks, this little awk oneliner is exactly what I've been looking for. For some reason, I thought it would require something a bit more fancy, like changing the num string to be used as a number. Guess not. – Will Simpson Jun 01 '21 at 21:44
  • Awk conveniently converts strings to numbers and vice versa as needed; it's fabulous when it works as intended, but also the source of pesky bugs when it happens when you don't want it to. – tripleee Jun 02 '21 at 04:01
0

I don't know the bash tools well enough to give a cool one-line answer, so here is a python script instead.

Usage

  • Save the code in a file increment.py;
  • Make the file executable with chmod +x increment.py;
  • Run the script with ./increment.py blablabla 123.

Code

#!/usr/bin/env python3

import sys

def print_help(argv0):
    print('increment numbers by 1')
    print('example usage:')
    print('  {} B-Funny Flash Nonfiction 202105131635'.format(argv0))
    print('  B-Funny Flash Nonfiction 202105131636')

def main(argv):
    if len(argv) < 2:
        print_help(argv[0])
    else:
        for s in argv[1:]:
            if s.isnumeric():
                print(int(s) + 1, end=' ')
            else:
                print(s, end=' ')
        print()

if __name__=='__main__':
    main(sys.argv)

Explanation

In a python program called from the command-line, the command-line arguments are stored in the array sys.argv.

The first element of the array, with index 0, is the name that was used to call the program, most likely "./increment.py" in our case.

The remaining elements are the parameters that were passed to the program; the words "B-Funny", "Flash", "Nonfiction", "202105131635" in our case.

The for-loop for s in argv[1:]: iterates on the elements of argv, but starting with the element 1 (thus ignoring the element 0). Each of these elements is a string; the method .isnumeric is used to check whether this string represents a number or not. Refer to the documentation on .isnumeric.

If the string is not numeric, we print is as-is. If the string is numeric, we compute the number it represents by calling int(s), then we add 1, and we print the result.

Apart from that, the line if len(argv): checks whether argv contains at least two elements; if it doesn't, that means it only contains its element 0, which is "./increment.py"; in this case, instead of printing the arguments, the script calls the function print_help which explains how to use the program.

Finally, the bit about if __name__ == '__main__': is a python idiom to check whether the file increment.py was run as a program or as a module imported by another file. Refer to this question.

Stef
  • 13,242
  • 2
  • 17
  • 28
  • Thanks for this. It works great! I was hoping for some cool one line bash command. Bash is a language I'm learning and have some basic familiarity with. Python, at this level, is a bit beyond my understanding. I can make this work but I'd be blind to how it does its magic. – Will Simpson Jun 01 '21 at 17:16
  • @WillSimpson I added a short explanation. – Stef Jun 01 '21 at 21:51