1

I have the following file:

blue yellow red black yellow
blue red black yellow red black
yellow red red purple yellow

How do I append a unique ID to each occurrence of the word red so that the output is similar to this:

blue yellow red1376049638182 black yellow
blue red13760496381928 black yellow red1376049631827 black
yellow red1376049631988 red13760496371827 purple yellow

I tried the following command:

sed "s/red/red$(node -e 'console.log(new Date().getTime())'; sleep 0.001s)/g" file

but very soon realized that the ID (in this case a time) is only generated once and replaced globally in the entire file. Thus, the solution must somehow use a loop though I'm not sure how to filter out the occurrences of red and make sure each has a unique ID appended to it.

mart1n
  • 5,969
  • 5
  • 46
  • 83
  • Related: http://stackoverflow.com/questions/7835407/how-to-generate-random-number-with-find-and-sed – fedorqui Aug 09 '13 at 12:34
  • @fedorqui that works then you're replacing one time in multiple files (i.e. using `find`) but I want to search a single file with multiple occurrences of the same string. Simply said, what is the equivalent of `find` that searches through a file sequentially so that I can stop at every hit and fire off a replace? – mart1n Aug 09 '13 at 12:39
  • @fedorqui The related one would replace the *same* random string everywhere in the file. – devnull Aug 09 '13 at 12:53

2 Answers2

1

You can define a function to create a random number. I'm not a big fan of $RANDOM so I'd create one using /dev/urandom. If you want 16-digit random number, you can say:

function myrandom() {
  cat /dev/urandom | tr -dc '[:digit:]' | fold -w 16 | head -1
}

Now use sed to replace the desired string with the function and eval it later. Note that I've advised sed to take care of word boundaries while replacing the string:

sed 's/\bred\b/red$(myrandom)/g' inputfile | while read -r line; do eval echo "${line}"; done

A sample invocation on your given input would result in:

blue yellow red5004720098524945 black yellow
blue red7014391283176465 black yellow red9350810549791982 black
yellow red4276472966991005 red6986710362535116 purple yellow
devnull
  • 118,548
  • 33
  • 236
  • 227
  • This is great, very elegant!! I wish I knew about `eval` before! Thank you! – mart1n Aug 09 '13 at 13:08
  • One more question about this approach. What if the line contains some special characters such as `;` or `>`. Is these any way ignore these and tell `eval` to only execute subshell commands? – mart1n Aug 09 '13 at 13:11
  • Not really. An alternative would be to use your original approach with one substitution per iteration. – devnull Aug 09 '13 at 13:24
0

Another method:

#!/bin/bash

IN=in.txt
OUT=out.txt
KEY="red"

rm -f $OUT

while read LINE
do
  QLINE=
  NEWQLINE=" $LINE "
  echo $QLINE : $NEWQLINE
  while [ "$NEWQLINE" != "$QLINE" ]
  do
    QLINE="$NEWQLINE"
    ID=$(uuidgen)
    ID=${ID//-/}
    NEWQLINE="${QLINE/ $KEY / $KEY$ID }"
  done
  echo "${QLINE:1:-1}" >> $OUT
done < $IN
loentar
  • 5,111
  • 1
  • 24
  • 25