LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - General (http://www.linuxquestions.org/questions/linux-general-1/)
-   -   Help with sed and awk to change L-case letters to U-case for specific lines in a file (http://www.linuxquestions.org/questions/linux-general-1/help-with-sed-and-awk-to-change-l-case-letters-to-u-case-for-specific-lines-in-a-file-945767/)

rootaccess 05-18-2012 12:52 PM

Help with sed and awk to change L-case letters to U-case for specific lines in a file
 
Hello folks. The objective is to rename all lowercase letters to uppercase letters but ONLY in certain lines of a file. Basically, Ive made some notes with commands written in them. To better read my documents, I'd like to capitalize my notes and leave the commands in the lowercase letters they already are written in.

To change all lowercase letters to uppercase letters in a document, I can use this sed command:

Code:

sed 's/\(.*\)/\U\1/' testfile
That, of course will change every letter. So what I did was I went over the document which contained 262 lines and only documented which lines I wanted to capitalize (Im sure there's no way to do this automatically unless there is a syntax with sed or awk or another program that can automatically parse commands in a file but I never heard of any).

So I made a file with just numbers in them representing each line of the document I'd like to edit like so:

17,21,25, etc

Then I realized that I don't know of a sed command to just parse those lines individually. I am aware of

Code:

sed '1,3s/\(.*\)/\U\1/' file
which will capitalize every lower case letter from lines 1-3 but I'd like to do that with just individual lines like 17,21,25,33 etc. Since I couldn't figure that one out, I know I can do

Code:

sed -e '17s/\(.*\)/\U\1/' -e '21s/\(.*\)/\U\1/' file
And that works nicely for editing those 2 lines but I have 50 lines to edit (or it can be 500 lines) and that can get old. So I'd like to be able to find a quicker way to edit my lines.

The only other thing I could think of is make a sed script and write out the numbers of the lines I want to edit in lines like so:

17
21
25

Then I was going to use
Code:

awk '{print $1 "s/\(.*\)/\U\1/"}' sedscript
Basically that would add the code
Code:

s/\(.*\)/\U\1/
to be placed right after the number with no spaces so I can use that as a sedscript but it isn't printing out like I had expected. The command line shows this output:

s/\(.*\)/\U/(strange box right here). It is missing a lot of code.
By now I could have done this all by hand but the idea is to learn it for future reference.

My sedscript already contains
#!/bin/sed -f
If I want to edit my lines, the sedscript would have to contain lines like so:
Code:

17s/\(.*\)/\U\1/
Code:

21s/\(.*\)/\U\1/
Then If I run that script against my document, it will do the job. The problem is getting the awk statement correctly to display that code. If someone could help me with that and/or the other sed command to group the editing (above the awk command) or if there is an easier way to do what I am looking for, I'd appreciate it.

Thanks,
Shawn

grail 05-18-2012 01:18 PM

There is probably a quicker way in sed, but as you have the numbers in a file, simply loop through the file in a bash script and perform the sed on the corresponding lines.
Code:

for n in $(< file_with_numbers)
do
    sed "$n s/.*/\U&/" file
done


PTrenholme 05-18-2012 01:58 PM

There are, of course, parsers that you could use to automatically select the lines you wish to convert to upper case. The simplest is the one you're already using - the regular expression. For more complicated things, you can use, e.g., bison to create C code that you can compile. And, of course, awk is a complete programming language.

rootaccess 05-18-2012 02:05 PM

Hi grail,

That code does not seem to work for me. I believe it is because it needs single quotes around the syntax like below. Even though the for loop is a script, it is running the sed command on the command line and not in a script and the actual syntax on the command line is:

Code:

sed '17s/.*/\U&/' file
or multiple instances of sed

Code:

sed -e '17s/.*/\U&/' -e '21s/.*/\U&/' file
and I try to put single quotes around the sed command in your script on the sed line between double quotes and it still isnt working.

It just goes through the file doing nothing.

I then add single quotes in the script around between the double quotes and I get this:
I am getting a line of this for each line
Code:

sed: -e expression #1, char 1: unknown command: `''
==================================================================
SOLVED!
EDIT:
Ok the original command did not work because it was in a script and sed usually only filters, doesnt change anything permament unless you redirect to a new file or use the -i flag. I used the -i flag and I did the syntax on the command line since I was trying everything under the sun

Code:

for n in $(< seducase);do sed -i "$n s/.*/\U&/" sed;done

pan64 05-18-2012 02:20 PM

awk ' /pattern/ { print toupper ($0); next } { print } ' file
this /pattern/ { ... } can be repeated as many time you need to cover your cases. { print } will print the not covered lines

rootaccess 05-18-2012 02:25 PM

Quote:

Originally Posted by pan64 (Post 4682104)
awk ' /pattern/ { print toupper ($0); next } { print } ' file
this /pattern/ { ... } can be repeated as many time you need to cover your cases. { print } will print the not covered lines

Can you show me an example of what you are doing? This looks very useful. Are you adding a column to a file from another file? That is what I wanted to do. If anyone knows how to do that and show an example, please.

Thanks!

pan64 05-18-2012 02:34 PM

pattern will give you a search regexp on which lines should be affected
Code:

awk ' /pattern1/ { print toupper($0); next } # will look for pattern and makes it upcase and prints
      /pattern2/ { print toupper($0); next } # will look for another pattern and does the same
... { print }                                # this will print not affected lines unchanged
' testfile > resultfile

this will not add column, it will generate a new file, in which all the lines selected by patterns are converted to upcase.

rootaccess 05-18-2012 02:51 PM

Quote:

Originally Posted by pan64 (Post 4682118)
pattern will give you a search regexp on which lines should be affected
Code:

awk ' /pattern1/ { print toupper($0); next } # will look for pattern and makes it upcase and prints
      /pattern2/ { print toupper($0); next } # will look for another pattern and does the same
... { print }                                # this will print not affected lines unchanged
' testfile > resultfile

this will not add column, it will generate a new file, in which all the lines selected by patterns are converted to upcase.

I'm a bit confused. A simple example would help tremendously. Maybe output a couple lines in a file that is lowercase and only make a select few uppercase? Maybe you can show with the awk command and then cat out the files and paste them here? Would really help.

Thanks

pan64 05-18-2012 03:05 PM

just back to your original problem: you have a document with more than 200 lines and you want to convert some lines to upcase. Is that right? How can you select the lines?
For example (not an efficient one, but just for you): line 17, 21 and 25 should be converted.
Code:

awk ' NR == 17 { print toupper($0); next } # will look for line number 17 and makes it upcase and prints
      NR == 21 { print toupper($0); next } #
      NR == 25 { print toupper($0); next } #
      { print }                                # this will print not affected lines unchanged
' yourtestfile > yourresultfile

this is not efficient, because here we look for line numbers, so we will need a lot of lines.
/pattern/ may give you a much better criteria.

rootaccess 05-18-2012 03:18 PM

Quote:

Originally Posted by pan64 (Post 4682139)
just back to your original problem: you have a document with more than 200 lines and you want to convert some lines to upcase. Is that right? How can you select the lines?
For example (not an efficient one, but just for you): line 17, 21 and 25 should be converted.
Code:

awk ' NR == 17 { print toupper($0); next } # will look for line number 17 and makes it upcase and prints
      NR == 21 { print toupper($0); next } #
      NR == 25 { print toupper($0); next } #
      { print }                                # this will print not affected lines unchanged
' yourtestfile > yourresultfile

this is not efficient, because here we look for line numbers, so we will need a lot of lines.
/pattern/ may give you a much better criteria.

Ok I totally get it. WOW what a life saver. What I did was make a new file and in the /pattern/ I simply just put the word replace in the slashes since I only wanted to replace that line with all uppercase and it worked. Great stuff. Beautiful. Wondrous. Fantastique.

You're right about my way not being efficient but basically in my case, I have documents that are basically notes to all the commands I wrote down and for my notes, I only capitalized the first letter and the line below is the actual command. So it isnt very clear sometimes when scrolling down many lines. Then again in my case, unfortunately, most of the lines would have to be written down by hand and then added to the forloop script that grail created. Otherwise I may end up capitalizing letters from my commands.

rootaccess 05-18-2012 03:47 PM

Even though I have found several solutions, the quickest one (the pattern one is great but lets face it, I won't always be able to tell which exact lines will be affect and some lines I may noy want capitalized) happens to be a simple sed script

Code:

#!/bin/sed -f

s/.*/\U&/

Now what I do is, since Im eventually going to have to make a file with all the lines I want edited of a file, I might as well just load the script in vi, hit "yy" to copy the code above and then hold "p" to get that pasted down as many times as I need. Now just write the number right before the sed command in the script, so lets say I wanted lines 17,21 and 25 to be changed (and a lot more I'm sure), I just enter them so the lines look like this:

Code:

17s/.*/\U&/
21s/.*/\U&/
25s/.*/\U&/

Then if I pasted too many of these
Code:

s/.*/\U&/
, I can just hold dd until they are gone. Now ZZ and (my scripts are all in a folder which is in my $PATH) and I run it simply:
Code:

sedscript file_to_be_edited
Thanks for all the help!
Shawn

rootaccess 05-18-2012 04:22 PM

If anyone knows the syntax with awk to extract columns and paste them in another file wherever I need, I'd greatly appreciate it.

Thanks

PTrenholme 05-21-2012 02:50 PM

Here's a simple program to do that. Note that this program uses the standard field separators. You could change that by setting FS in the BEGIN section or on the command line.

Code:

#!/bin/awk -f
function usage()
{
  print "Usage: replace_column -v from=number -v with=number from_file with_file out_file" > "/dev/stderr"
  print "" > "/dev/stderr"
  print "Note: This program requires that the from_file and with_file have the identical number of rows." > "/dev/stderr"
  exit(1)
}
BEGIN {
  if ((ARGC < 2) || (ARGC > 3) || (! from) ||( ! with)) usage()
  if (ARGC == 3) {out=ARGV[3];--ARGC} else {out="/sev/stdout"}
  with_file = ARGV[2]
  --ARGC
  errors=0
}
{
  if (! getline with_line < with_file) {
    if (! errors) print "Error: " FILENAME " is longer than " with_file ". No further replacements." > "/dev/stderr"
    ++errors
  }
  else {
    split(with_line, column)
  }
  if (! errors) $from = column[with]
  print > out
}

Note: Untested code.

Note2: You could get much more sophisticated by, for example, only replacing only when a line satisfies some criteria, or matches something read in the "with_file."


All times are GMT -5. The time now is 07:33 PM.