2

How to put into array all words appearing between brackets in text file and replace it with random one from that array?

cat math.txt
First: {736|172|201|109} {+|-|*|%|/} {21|62|9|1|0}
Second: John had {22|12|15} apples and lost {2|4|3}

I need output like:

First: 172-9
Second: John had 15 apples and lost 4
Marcin Wojcik
  • 57
  • 1
  • 3
  • In my opinion it will be a pain to do this in Bash. What about Perl or Python? This here http://stackoverflow.com/questions/2388488/select-a-random-item-from-an-array may be your starting point. – Tony Stark Jun 29 '15 at 19:13
  • You want a language that can do regex substitution with a custom repl function. Bash or sed is not one of those; I don't claim to be an awk expert, but from what I know this is hard in awk too. This is a breeze in Python. – 4ae1e1 Jun 29 '15 at 19:29

3 Answers3

2

This is trivial in awk:

$ cat tst.awk
BEGIN{ srand() }
{
    for (i=1; i<=NF; i++) {
        if ( match($i,/{[^}]+}/) ) {
            n = split(substr($i,RSTART+1,RLENGTH-2),arr,/\|/)
            idx = int(rand() * (n-1)) + 1
            $i  = arr[idx]
        }
        printf "%s%s", $i, (i<NF?OFS:ORS)
   }
}

$ awk -f tst.awk file
First: 172 - 9
Second: John had 22 apples and lost 2
$ awk -f tst.awk file
First: 201 - 9
Second: John had 12 apples and lost 2
$ awk -f tst.awk file
First: 201 + 62
Second: John had 12 apples and lost 2
$ awk -f tst.awk file
First: 201 + 1
Second: John had 12 apples and lost 4

Just check the math on the line where idx is set - I think it's right but didn't put much thought into it.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
1
$ awk 'BEGIN{srand()} {for (i=1;i<=NF;i++) {if (substr($i,1,1)=="{") {split(substr($i,2,length($i)-2),a,"|"); j=1+int(rand()*length(a)); $i=a[j]}}; print}' math.txt
First: 172 + 1
Second: John had 12 apples and lost 3

How it works

  • BEGIN{srand()}

    This initializes the random number generator.

  • for (i=1;i<=NF;i++) {if (substr($i,1,1)=="{") {split(substr($i,2,length($i)-2),a,"|"); j=1+int(rand()*length(a)); $i=a[j]}

    This loops through each field. If any field starts with {, then substr is used to remove the first and last characters of the field and the remainder is split with | as the divider into array a. Then, a random index j into array a is chosen. Lastly, the field is replaced with a[j].

  • print

    The line, as revised above, is printed.

The same code as above, but reformatted over multiple lines, is:

awk 'BEGIN{srand()}
{
    for (i=1;i<=NF;i++) {
        if (substr($i,1,1)=="{") {
            split(substr($i,2,length($i)-2),a,"|")
            j=1+int(rand()*length(a))
            $i=a[j]
        }
    }
    print
}' math.txt

Revised Problem with Spaces

Suppose that match.txt now looks like:

$ cat math.txt 
First: {736|172|201|109} {+|-|*|%|/} {21|62|9|1|0}
Second: John had {22|12|15} apples and lost {2|4|3}
Third: John had {22 22|12 12|15 15} apples and lost {2 2|4 4|3 3}

The last line has spaces inside the {...}. This changes how awk divides up the fields. For this situation, we can use:

$ awk -F'[{}]' 'BEGIN{srand()} {for (i=2;i<=NF;i+=2) {n=split($i,a,"|"); j=1+int(n*rand()); $i=a[j]}; print}' math.txt
First:  736   +   62 
Second: John had  12  apples and lost  3 
Third: John had  15 15  apples and lost  2 2

How it works:

  • -F'[{}]'

    This tells awk to use either } or { as field separators.

  • BEGIN{srand()}

    This initializes the random number generator

  • {for (i=2;i<=NF;i+=2) {n=split($i,a,"|"); j=1+int(n*rand()); $i=a[j]}

    With our new definition for the field separator, the even numbered fields are the ones inside braces. Thus, we split these fields on | and randomly select one piece and assign the field to that piece: $i=a[j].

  • print

    Having modified the line as above, we now print it.

John1024
  • 109,961
  • 14
  • 137
  • 171
  • what about if i would like to change one of the lines to look like this: Second: John had {22 22|12 12|15 15} apples and lost {2 2|4 4|3 3} how to solve this because your solution doesnt work – Marcin Wojcik Jul 11 '15 at 20:55
  • @MarcinWojcik That changes things quite a bit. I just added a new solution that will handle those spaces. – John1024 Jul 11 '15 at 23:13
1

You can try this awk:

awk -F'[{}]' 'function rand2(n) { 
   srand();
   return 1 + int(rand() * n);
}
{
   for (i=1; i<=NF; i++)
      if (i%2)
         printf $i OFS;
      else {
         split($i, arr, "|");
         printf arr[rand2(length(arr))]
      };
      printf ORS
}' math.txt

First:  736  -  62
Second: John had  22 apples and lost  2

More runs of above may produce:

First:  172  *  9
Second: John had  12 apples and lost  4

First:  109  /  0
Second: John had  15 apples and lost  3

First:  201  %  1
Second: John had  12 apples and lost  3

...
...
anubhava
  • 761,203
  • 64
  • 569
  • 643