2

I have the following string: abcd=efgh and I would like to uppercase all characters before the = to get ABCD=efgh.

After reading a bunch of posts, I tried the following:

echo "abcd=efgh" | sed -r 's/^(.*)=/\U\1\E=/'

But this returns: UabcdE=efgh

So with the command, I can capture the right group but the \U doesn't uppercase the capture group. Any idea? Thanks!

astiegler
  • 315
  • 3
  • 15
  • 1
    Doesn't have to be `sed` necessarily, it's just what I found when I was searching for an answer – astiegler Jun 03 '22 at 18:57
  • 2
    This can be easily adapted to your question: [bash script to convert values before an equal sign to lowercase?](https://stackoverflow.com/q/63197734/3776858) – Cyrus Jun 03 '22 at 20:15

4 Answers4

6

As you discovvered, you're running into the difference between GNU sed and BSD sed; the latter is what ships on macOS. If you install GNU sed (e.g. the Homebrew gnu-sed package) and use that (probably installed as gsed), then your original attempt will work.

A more portable solution would be to use perl. Your original sed expression works unchanged:

$ echo "abcd=efgh"  |  perl -pe 's/^(.*)=/\U\1\E=/'       
ABCD=efgh

Although I would simplify the expression a bit and replace the echo | with a here-string:

$ perl -pe 's/^[^=]*/\U$&/' <<<'abcd=efgh'
ABCD=efgh

There are other tools you could use, but they don't drop right in with the same syntax as sed like perl does. For example, you could use shell builtins to split the string and then run tr on the half you want to capitalize:

IFS== read left right <<<abcd=efgh
printf '%s=%s\n' "$(tr a-z A-Z <<<"$left")" "$right"

If you're using zsh, you can use its built-in uppercase expansion instead of tr:

IFS== read left right <<<abcd=efgh
printf '%s=%s\n' "${(U)left}" "$right"

Newer bash versions can do it, too, but not the one that ships in /bin/ on macOS. You have to install a newer one (for example with Homebrew again), and then this works:

IFS== read left right <<<abcd=efgh
printf '%s=%s\n' "${left^^}" "$right"
Mark Reed
  • 91,912
  • 16
  • 138
  • 175
  • I wonder if `declare -u left` would work on the ancient bash version that comes on macs. – Shawn Jun 03 '22 at 20:14
  • 1
    Answering my own question, no. It was added at the same time as the `^^` and `,,` expansions. – Shawn Jun 03 '22 at 20:24
  • `declare -u` does have the advantage of working in both (a new-enough) bash and zsh. Add ksh to the mix if you use `typeset` instead of `declare`, which works in all three. – Mark Reed Jun 03 '22 at 20:52
4

Plenty of solutions. Here one using awk:

awk -F= '{print toupper($1) "=" $2}' <<< 'abcd=efgh'
ABCD=efgh
Diego Torres Milano
  • 65,697
  • 9
  • 111
  • 134
  • 2
    If you're using = as the input field separator, you might as well use it as the output one, too. I'd write it like this: `awk -v{,O}FS== '{$1 = toupper($1)}1'` – Mark Reed Jun 03 '22 at 19:33
  • 2
    @MarkReed nice brevity with `-v{,O}FS=` - I haven't seen that before. – Ed Morton Jun 04 '22 at 01:32
2

This might work for you (GNU sed):

sed 'h;y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/;G;s/=.*=/=/' file

Make a copy of the current line.

Translate all lowercase alpha to uppercase alpha.

Append the copy of the original line.

Remove the portion of the two lines from = to = and replace it by =.

Alternative:

sed 's/.*=/\U&/' file
potong
  • 55,640
  • 6
  • 51
  • 83
0

Using sed, the alternative \u option should work.

$ sed ':a;s/[a-z]\+=/\u&/;ta;' input_file
ABCD=efgh
HatLess
  • 10,622
  • 5
  • 14
  • 32