-2

I try to uncomment specific lines from a file with patterns in oracle linux 8.6 using bash. There are leading white spaces on certain lines where the comments are not removed. I tried to uncomment the commented lines with sed and grep for matching the patterns. I need to exactly match two numbers from the output. There is one word per column(total 2 columns) in the file each with numbers.

Example: column1:pd19_ORA column2:svg38.

I need to uncomment lines inplace with exact match of 19 and 38 without 190,1900 or 019 etc.. excluding lines such as,

   #pd19_ORA svg37 
#pd199_ORA svg388 

Code:

sed -n '/\<19\>/,+1p' cmfile|grep '38'|sed -i '/38/s/^#//g' cmfile

Contents of file:

    #pd19_ORA svg38
#pd19_ORA sil38
#pd29_ORA sil37
    

First line is still commented after using sed with inplace but comment of second line is removed.

Output:

    #pd19_ORA svg38
pd19_ORA sil38
#pd29_ORA sil37

How to remove the first line comment which has leading white spaces without removing the leading space?

Expected output:

    pd19_ORA svg38
pd19_ORA sil38
#pd29_ORA sil37
8
  • Sorry, I'm not quite understanding. You've asked how to remove a comment character from a line with leading whitespace - that's ok. But why is the second line uncommented when the third is not? Commented Dec 30, 2024 at 13:09
  • Or is it that you're actually trying to remove comments from lines containing 19 while still preserving leading whitespace? Commented Dec 30, 2024 at 13:11
  • @ChrisDavies the third line would not match the grep '38'. Commented Dec 30, 2024 at 13:17
  • @EduardoTrápani yes, that too. It's a bit confusing Commented Dec 30, 2024 at 13:19
  • It doesn't make sense to pipe to sed -i. When sed is operating in inplace mode, it ignores its standard input. Commented Dec 30, 2024 at 18:18

3 Answers 3

2

You can try awk and command like this can do the work. If you need to have the specific strings 19 and 38 on specific places you should mention it in question:

awk '/19/ && /38/ {sub(/#/,"")}1' 

This command search for line with 19 and 38 and remove the # symbol. And then print the line (independently if it's match and edited or not)

awk '/19/ && /38/ {sub(/#/,"")}1' input_file
    pd19_ORA svg38
pd19_ORA sil38
#pd29_ORA sil37

If you want to be sure # is the first nonwhite space symbol in the line you can modify the script like this:

 awk '/19/ && /38/ && $1 ~ "^#" {sub(/#/,"")}1' input_file

Here is changed script which search for specific strings, congaing desired numbers:

awk '$1~/pd19_ORA$/ && $2~/[a-z]{3}38$/ && $1 ~ "^#" {sub(/#/,"")}1' 
12
  • 1
    @RomeoNinov, beware gawk -i inplace is unsafe. Commented Dec 30, 2024 at 15:56
  • 3
    ITYM sub() rather than gsub() here. Commented Dec 30, 2024 at 17:13
  • 3
    gsub() replaces all occurrences of # while here, you likely want to replace only the first occurrence on each line. Commented Dec 30, 2024 at 17:18
  • 1
    "This command search for line with 19 and 38" - it would also match on the single instance of 1938, or substrings of numbers such as 219 or 5387 Commented Dec 31, 2024 at 9:49
  • 2
    @Kishan then you need to tell us what operating systems this needs to work on in the question. Commented Dec 31, 2024 at 13:06
1

I think you're looking to remove a leading # character from all lines containing the number 19, while maintaining any leading whitespace

sed -E '/[^0-9]19([^0-9]|$)/s/^([[:space:]]*)#/\1/' cmfile

This searches for lines containing 19 (but without digits either side such as 019 or 193) since I couldn't get \<19\> to match any of your lines here.

In the case where you want lines containing 19 and 38 you simply need to extend the initial line matching:

sed -E '/[^0-9]19[^0-9](.*[^0-9]|)38([^0-9]|$)/s/^([[:space:]]*)#/\1/' cmfile

In both cases, for all such matched lines we capture any leading space preceding a # character so that we can add it back into the output.

Finally, as yet another variation, sed -i ... can be used to update the file seemingly in place

9
  • i need to filter two patterns one with 19 and other with 38 in single command and then uncomment the line. Commented Dec 30, 2024 at 15:45
  • What do you mean "filter two patterns"? You want a 19 and a 38 on the same line? Commented Dec 30, 2024 at 15:52
  • there are two strings each line, one with pd<number>_ORA and second word with string<number>. I want to grep or filter (with any alterative) numbers from both strings with 19 and 38 to make sure that i dont edit any other lines which has 19 with 37 etc.. Commented Dec 30, 2024 at 17:09
  • 1
    So you want lines with both 19 and 38 in them? Commented Dec 30, 2024 at 17:27
  • Can i also edit the file inplace on the same sed command in one go without two separate commands? Commented Dec 31, 2024 at 7:15
1

Assuming you want to uncomment the lines that contain 38 and that follow a line that contain a word-delimited¹ 19 as your attempt suggests you might want to do:

perl -pi -e 's/^\s*\K#// if $flag && /38/; $flag = /\b19\b/' your-file

In your:

sed -n '/\<19\>/,+1p' cmfile|grep '38'|sed -i '/38/s/^#//g' cmfile

The sed -i '/38/s/^#//g' cmfile command doesn't read its input, it only edits cmfile in place, removing a leading # on any line that contains 38 (btw, the g is superfluous as there can only be one substitution).

So the sed -n '/\<19\>/,+1p' cmfile|grep '38' is pointless as nothing is reading its output.

Also note that all of -i, +1, \<\> are non-standard GNU extensions², while the perl command will work on any system³.

Edit

With your updated requirements where you want to uncomment the lines where the first field contains 19 and the second contain 38 (themselves not surrounded by other digits):

perl -api -e 's/^\s*\K#// if $F[0] =~ /(?<!\d)19(?!\d)/ &&
                             $F[1] =~ /(?<!\d)38(?!\d)/' your-file

Here using the -awk mode there the fields are made available in the @F array or, if there may be whitespace on either side of the # which would result in the first field becoming the second field, do the full line matching with a regexp:

perl -pi -e 's/^\s*\K#(?=\s*\S*(?<!\d)19(?!\d)\S*\s+\S*(?<!\d)38(?!\d))//' your-file

Where we have (see perldoc perlrun, perldoc perlop and perldoc perlre for details):

  • -p: sed mode, where the -expression is evaluated for each line of input.
  • -i: in-place editing
  • s/regexp/replacement/: substitute the first match of the regular expression with replacement.
  • \s: any whitespace character, \S for non-whitespace.
  • <atom>*: the previous atom repeated any number of times including 0, as many as possible
  • \K: Keep what's on the left, or IOW resets the start of the match that will end up being substituted.
  • (?=...): positive look-ahead, or IOW "provided what follows matches ..."
  • (?<!...): negative look-behind, or IOW "provided what precedes does not match ..."
  • (?!...): negative look-ahead
  • \d: any decimal digit character. Without -C (for Unicode mode), that's limited to 0123456789.

¹ In any case, the 19 in pd19_ORA is not word-delimited as both d and _ are word characters; if you wanted to match on 19 that is neither preceded nor followed by a decimal digit, you'd use (?<!\d)19(?!\d) instead of \b19\b (the perl equivalent of ex' \<19\>).

² Well, strictly speaking, \<\> originated in ex/vi, not GNU sed and -i was copied by GNU sed from perl, and ,+1 from ed.

³ Though you'll need perl 5.10.0 (from 2007) or newer for \K (to Keep what's on the left of it). On systems will an older version of perl, you can replace s/^\s*\K#// with s/^(\s*)#/$1/.

2
  • is there any other alternatives other than perl? Commented Dec 31, 2024 at 7:17
  • 2
    @Kishan there are plenty alternatives to perl, but perl is the most obvious one for text processing. That's one of the things it is best at. Commented Dec 31, 2024 at 15:48

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.