5

I would like to convert this:

foo^bar
ba^rfoo
oofrab
raboof^

To this:

FOObar
BArfoo
oofrab
RABOOF

Anything before the "^" (or another special character if it makes it easier) gets capitalized

Also, the removal of the "^" is also not required if it makes it easier.

4
  • What is the practical application and why do you list those languages/tools in the tags? Is this an exercise for people to choose whichever language/tool they like? May 11, 2015 at 3:43
  • It's kind of a long story of the purpose of this, but ultimately I am creating images out of words, and I am seeking a shorthand way to capitalizing things quickly. And for the different list of tools, whatever is easier for people to do. It helps me learn what tool is right for different jobs
    – TuxForLife
    May 11, 2015 at 3:47
  • What is with this case: foo^bar^foo
    – A.B.
    May 12, 2015 at 17:03
  • In my case, there's a 99.9% chance of that happening, so it's no biggie if it doesn't work properly in that scenario!
    – TuxForLife
    May 12, 2015 at 17:26

8 Answers 8

8

Using GNU sed (Ubuntu's default) (thanks to pabouk for the -r option suggestion):

< inputfile sed -r 's/^(.*)\^/\U\1\E/' > out

Using perl (thanks to Oli for the shortened regex):

< inputfile perl -pe 's/^(.*)\^/\U\1\E/' > out

Command #1 breakdown:

  • < inputfile: redirects the content of inputfile to stdin
  • -r: allows the use of extended regexes
  • > out: redirects the content of stdout to out

Command #2 breakdown:

  • < inputfile: redirects the content of inputfile to stdin
  • > out: redirects the content of stdout to out

Regex breakdown:

  • s: performs a substitution
  • /: starts the regex
  • ^: matches the start of the line
  • (: starts the first capturing group
  • .*: matches any number of characters
  • ): stops the first capturing group
  • \^: matches a ^ character
  • /: stops the regex / starts the replacement
  • \U: starts converting to uppercase
  • \1: replaces with the first capturing group
  • \E: stops converting to uppercase
  • /: stops the replacement
11
  • 1
    Hello kos, almost there, however it's doing the opposite. I am getting fooBAR instead of FOObar. echo foo^bar | sed 's/^\(.*\)\^\(.*\)$/\1\U\2/'
    – TuxForLife
    May 11, 2015 at 3:43
  • 1
    @TuxForLife You can drop to chat and ping me with "@kos"
    – kos
    May 11, 2015 at 3:59
  • 2
    The second command is missing a command! I think you forgot a perl. Plus you don't need to replace the second capture with the second capture. Just don't capture it. s/^(.*)\^/\U\1/ will do the job.
    – Oli
    May 11, 2015 at 7:49
  • 1
    With GNU sed the second variant of the regex (extended regex) can be used with option -r: sed -r 's/^(.*)\^/\U\1/' May 11, 2015 at 9:00
  • 2
    What non GNU sed version support \U?
    – cuonglm
    May 11, 2015 at 16:57
5

Using vim:

vim -es '+g/\^/normal gUf^' +wq foo
  • The -es turns on the venerable ex mode, and silences vim (mostly).
  • + is used to provide vim commands as command-line arguments.
  • g/\^/ - run the command on all lines matching /\^/
  • normal - run the rest of the command as a normal mode action.
  • gUf^ - convert to uppercase (gU) until ^ (f^). While using a range g/.../ in this case), the cursor is placed at the start of each line before executing the command.
  • Then save and quit (wq).

f looks for the first ^, so lines with multiple ^ would have only the field converted. There's no simple motion for finding the last ^. You can try going to the end of the line and searching backwards ($F^), but that would fail if ^ is the last character. So, you would need to do it in two steps:

vim -es '+g/\^./norm $F^gU0' '+g/\^$/norm gU$' +wq foo
6
  • muru, only first instance gets capitalized, e.g.,` FOO^bar^baz` May 11, 2015 at 19:31
  • @Serg yes. That's what f does - move to first instance of the character.
    – muru
    May 11, 2015 at 19:32
  • Is there a way to make all instances capitalized ? I really like that vim writes back to that same file , so this would be ideal if it capitalized all instances May 11, 2015 at 19:38
  • @Serg not in one pass. There's no simple motion that takes one to the last occurrence of a character (not counting plugins). You'd have to do: vim '+g/\^./norm $F^gU0' '+g/\^$/norm gU$' +wq foo - the first command for lines that don't end in ^, and the second command for lines that end in ^.
    – muru
    May 11, 2015 at 19:47
  • That works ! Can you add that to your answer ? I'll upvote May 11, 2015 at 21:21
3

EDIT

So after about hour and half , I came up with this:

awk -F' ' '{ gsub(/\^/,"@ "); for (i=1;i<=NF;i++){ if($i ~ /\@/) $i = toupper($i);};gsub("@ ","");gsub("@",""); print   }  ' removecharsfile 

Basic ideas:

  • get rid of ^ and replace it with @ plus space
  • Treat space as field delimiter; now we have fields to play with
  • for loop to step through each field in each line, and check if there is @ character.
  • if there is @ character, convert that field to upper. Why $i = toupper($i) ? Because otherwise it doesn't get stored anywhere
  • after the loop is done, get rid of @+space, and any @ at the end of fields.
  • print everything

Perhaps better approach to writing all this on one line is to put it in a file ( neatly organized bellow ), and run that with awk like so awk -f awkscript theinputfile

# awk script to capitalize
# whatever comes before caret(^)


{
  gsub (/\^/, "@ ");
  for (i = 1; i <= NF; i++)
    {
      if ($i ~ /\@/)
        $i = toupper ($i);
    };
  gsub ("@ ", "");
  gsub ("@", "");
  print

}

And here it is in action:

enter image description here

ORIGINAL POST

I'll contribute my own code version with awk:

awk -F'^' '{print toupper($1)$2}' thefile

And of course you can redirect output with > output.txt And here it is in action:

enter image description here

22
  • Nice, but it will break with more than 2 fields: echo foo^bar^test | awk -F'^' '{print toupper($1)$2}' outputs FOObar
    – kos
    May 11, 2015 at 4:18
  • 1
    If you take of the last "^" after "raboof", then raboof still gets capitalized
    – TuxForLife
    May 11, 2015 at 4:20
  • Not sure if that's important tough, @TuxForLife is it fine to always expect 2 fields?
    – kos
    May 11, 2015 at 4:25
  • 1
    Sometimes a "^" won't be provided on a line, so nothing should happen in that case. But that's my own fault for not providing that in my example!
    – TuxForLife
    May 11, 2015 at 4:27
  • @TuxForLife I'm gonna try to improve the answer to include no "^" case May 11, 2015 at 4:40
3

I would set ^ as field separator in awk in upper case the first field:

$ awk 'BEGIN{FS="^"; OFS=""} NF>1{$1=toupper($1)}1' file
FOObar
BArfoo
oofrab
RABOOF

By saying OFS="" we set the output field separator to the empty string, so that the ^ is removed. If this is not needed, awk -F"^" '{$1=toupper($1)}1' file alone would make it; it converts all ^ into spaces.

Note we use NF>1 in order to perform the upper case in case there is at least one ^.

5
  • see, you are doing same mistakes I've done in the original post. This will works with two fields, but what if file contains something like foo^bar^baz ? only field $1 gets capitalized. Also text entries with no ^ still get capitalized. May 11, 2015 at 19:30
  • @Serg you are right regarding capitalizing everything if no ^ is present. I just fiexed it, thanks. Regarding multiple ones, I understand the question as "upper case up to the first one".
    – fedorqui
    May 11, 2015 at 21:47
  • OP said "Anything before the "^"", but he did not specify how many fields there are supposed to be, so you have to consider multiple possibilities. Don't get me wrong - I'm just trying to help you consider the problem from different angle May 12, 2015 at 1:16
  • @Serg yep, I know and I appreciate it. But this wouldn't be that complicated either, you just need to loop: awk 'BEGIN{FS="^"; OFS=""} NF>1{for (i=0;i<NF;i++) $i=toupper($i)}1' file.
    – fedorqui
    May 12, 2015 at 7:37
  • @Serg by the way, from the comments in your answer I see you are learning awk by yourself. This idiomatic awk helped me a lot a while ago.
    – fedorqui
    May 12, 2015 at 7:50
2

Bash can also do this, so I'll throw a bash answer into the mix.

#bash
while IFS= read -r line; do
    if [[ $line = *^* ]]; then
        tmp=${line%%^*} 
        line=${tmp^^}${line#*^}
    fi
    printf '%s\n' "$line"
done < inputfile > outptufile

This is just iterating the inputfile line-by-line (BashFAQ 1), and using parameter expansions to do the splitting and uppercasing (BashFAQ 73).

2

Through python3,

Without using re module,

with open(file) as f:
    for line in f:
        if '^' in line:
            m = line.strip().split('^')
            print(m[0].upper() + m[1])
        else:
            print(line, end="") 

With using re module.

import re
with open(file) as f:
    for line in f:
        print(re.sub(r'(.*)\^', lambda m: m.group(1).upper(), line.strip()))

Replace file in the above scripts with the actual file path. And run the script using python3 command.

1

An other awk version:

awk '{ a=$_; ismatch=sub(/\^.*/, "", a); b=gensub(/.*\^(.*)/, "\\1", "", $_); if(ismatch==1) { print toupper(a) b} else { print b} }' testdata

human readable ;)

awk '{
  a=$_;
  ismatch=sub(/\^.*/, "", a);
  b=gensub(/.*\^(.*)/, "\\1", "", $_);
  if(ismatch==1) {
    print toupper(a) b
  }
  else {
     print b
  }
}' testdata
3
  • For me entries with multiple ^ , like foo^bar^baz, get middle part removed and get only last one left - i.e., FOObaz May 11, 2015 at 19:36
  • @Serg Hmmm you're right. I will change this tomorrow.
    – A.B.
    May 11, 2015 at 19:41
  • Ping me when you do, alright ? May 11, 2015 at 19:43
1

Another python approach:

#!/usr/bin/env python2
with open('/path/to/file.txt') as f:
    for line in f:
        if '^' in line:
            index = line.find('^')
            print line[:index].upper() + line[index+1:].rstrip()
        else:
            print line.rstrip()

Output :

FOObar
BArfoo
oofrab
RABOOF
  • index = line.find('^') contains the index of the character ^

  • line[:index].upper() prints the characters before index (^) in uppercase (upper())

  • line[index+1:] prints the characters after ^ literally

  • rstrip() will remove the trailing newlines added by print by default.

EDIT :

Now if you have a file like this (multiple ^) :

foo^bar^spam
ba^rfoo^egg
oofrab
raboof^spamegg

and you want to make it like:

FOOBARspam
BARFOOegg
oofrab
RABOOFspamegg

In this case you can use:

#!/usr/bin/env python2
with open('/path/to/file.txt') as f:
    for line in f:
        if '^' in line:
            index = line.rfind('^')
            print line[:index].upper().replace('^', '') + line[index+1:].rstrip()
        else:
            print line.rstrip()

Output :

FOOBARspam
BARFOOegg
oofrab
RABOOFspamegg

Only replacements are rfind('^') instead of find('^'), which will find the index of rightmost ^ and replace('^', '') will replace all ^s with blanks.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .