3

I have a folder with a bunch of files. In some of those files there is a line like this

id = "8c09e1ce-56b6-4aa5-8307-8998f507b594"

All those ids are unique.

I want to write a command that will generate new unique values for all those ids

I have a sed command that will match the original line 's/id = \"[a-zA-Z0-9-]\{36\}\"//g' but I don't know how I can dynamically get the substitution working.

There are a few StackOverflow questions that are similar (like this one) but I couldn't get the solution to work reliably across OSX and Linux which is one of my requirements. They all have uuidgen installed though.

Something concise and readable would obviously be preferred.

Krzysztof Kozmic
  • 27,267
  • 12
  • 73
  • 115

2 Answers2

3

For in-placing editing with sed's -i option, there is no cross-platform syntax that works without creating a backup file, as explained in this answer of mine (you must use either -i '' (BSD/macOS) or just -i (GNU), but with creation of a backup file something like -i.bak does work with both implementations).

That said, awk is the better tool for this task anyway, because Awk scripts allow invoking shell commands and external utilities on demand:

find . -type f -name '*.extension' | while IFS= read -r fpath; do
  awk '
    BEGIN { FS=OFS=" = " } 
    $2 ~ /^\"[a-zA-Z0-9-]+\"$/ { "uuidgen" | getline uuid; $2 = "\"" uuid "\"" }
    1
  ' "$fpath" > "$fpath.tmp" && mv "$fpath.tmp" "$fpath"
done

This assumes that filenames have no embedded newlines, which is rarely a real-world concern.

  • The command uses only POSIX-compliant shell features, utilities and options, with the exception of:

    • the uuidgen utility, which, however, is available on both Linux and macOS, as well as FreeBSD and its variants.
  • Sadly, Mawk (which is Ubuntu's default Awk) doesn't support duplication expressions such as {36}, which is why the less specific + is used above; if you know that you'll be using only either BSD or GNU Awk, however, you can still use {36}.

  • The command supports replacing multiple UUIDs in each input file.

On a side note: GNU Awk v4.1+ allows in-place updating with -i inplace, similar to Sed's -i option, which would allow for a single-command find solution with -exec, but neither BSD Awk nor Mawk support it.

Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
-2

So I figured it out (by cheating).

There's only one id per file so I decided to iterate over the files and run sed for each file separately, like this:

for i in `find . -name "*.extension" -type f`; do
    uuid=$(uuidgen)
    sed -i '' -e "s/id = \"[a-zA-Z0-9-]\{36\}\"/id = \"${uuid}\"/g" $i
done

The question still stands though for a general case, if that can be done in one go.

Krzysztof Kozmic
  • 27,267
  • 12
  • 73
  • 115
  • 2
    Please [do not use `for` to parse command output](http://mywiki.wooledge.org/DontReadLinesWithFor). Also, your `sed -i ''` only works with _BSD_ Sed, not with _GNU_ Sed. Please double-quote `$i` to ensure that its value is used as-is. The `g` option in the Sed script's `s` function call is not needed, because there's at most 1 UUID per input line. – mklement0 Dec 02 '16 at 05:10