0

I was working it with awk, and keying on $1 $2 fields, but awk would act differently depending on whether the FieldSeperator ($match) starts the line, or had a blank before it, or didn't have a blank before it.

I'm thinking that sed is my best choice for this particular task, but need some help. I thought that I would be able to get this with a few simple searches, and although I've seen plenty of examples to replace the entire line, I haven't been able to find what I need to do below. I would appreciate if someone is able to help me out.

I want to specify a variable ($match) that is set to the match I want to find (password=). This will actually be in a batch script where I will loop through various patterns.

I want to print:

  • only lines containing the match. Ignore lines that do not have the match.
  • print up to and including the 1st match value on the line
  • if there is any value after the match, replace it with masked
  • if there is nothing after the match, print line as is (or replace with empty)
  • case insensitive search for the match.

infile.txt

dummy line  
mypassword=123  
PASSWORD=1234  
secret password=2345  2ndpassword=99485  
password=  
nothing to see move along
end of file

Desired Output

mypassword=masked  
PASSWORD=masked  
secret password=masked  
password= 
2
  • If you say "if there is nothing after the match", does that include whitespace-only after the match, or only lines that end immediately after the =?
    – AdminBee
    Apr 26 at 13:17
  • "awk would act differently depending on whether the FieldSeperator ($match) starts the line, or had a blank before it, or didn't have a blank before it." you can write an FS expression that handles this.
    – roaima
    Apr 27 at 9:56

5 Answers 5

2

How about

$ sed -nE '/(password|PASSWORD)=/{s/=.+/=masked/;p;}' infile.txt
mypassword=masked
PASSWORD=masked
secret password=masked
password=
  • match password= or PASSWORD=
  • substitute any non-empty sequence of characters after =
  • print the result

With GNU sed, you can simplify the case-insensitive match using the I modifier:

sed -nE '/password=/I{s/=.+/=masked/;p;}' infile.txt
1
  • 1
    This would change file=password= into file=masked, which may be unintentional.
    – Kusalananda
    Apr 26 at 6:46
1

In "defense" of awk, and assuming your awk understands the IGNORECASE internal flag and POSIX character classes (this will probably limit it to GNU Awk), the following should do:

awk -v pat="$match" 'BEGIN{IGNORECASE=1;w=length(pat)} i=index($0,pat) {printf "%s%s%s",substr($0,1,i+w-1),substr($0,i+w)~/^[[:space:]]*$/ ? "" : "masked",ORS}' infile.txt

This will import the shell variable $match into the awk variable pat. In the BEGIN section, the IGNORECASE flag will be set to instruct awk to ignore case in matches.

It will only process lines where the index() function can successfully locate the search pattern (I chose to do a literal string match instead of a RegEx match to guard against characters with special meaning in your $match variable - use the match() function if a RegEx search is wanted instead). In this case it will print the line from the beginning up to and including the pattern, followed by the word masked unless the remainder of the line only consists of whitespace.


A more portable variant will use the tolower() function to transform the line to lower-case before performing the index() search (so, make sure the $match variable contains an all-lowercase search string), and doesn't make use of POSIX character classes:

awk -v pat="$match" 'BEGIN{w=length(pat)} i=index(tolower($0),pat) {printf "%s%s%s",substr($0,1,i+w-1),substr($0,i+w)~/^[ \t]*$/ ? "" : "masked",ORS}' infile.txt

This should work with all variants of awk.

0
0

Using sed

$ sed -n '/password=/I{/=[[:alnum:]]/{s/\([^=]*=\).*/\1masked/};p}' input_file
mypassword=masked
PASSWORD=masked
secret password=masked
password=
0

I wasn't able to do it with sed, but perl -p works fine.

grep -i "$MATCH" "file.txt" | perl -p -e "s/(?<=$MATCH\S).*/masked/i" 
0

Using GNU sed with its /I modifier for case insensitivity.

##> ensure $match is pluggable
## on the kha of a s/// and/or
## /../
match='password='
match_lhs=$(printf '%s\n' "$match" | sed -e 's:[*[$.\^/]:\\&:g')

sed -n "
  /$match_lhs\$/Ip
  s/\($match_lhs\)..*/\1MASKED/Ip
" infile.txt

mypassword=MASKED
PASSWORD=MASKED
secret password=MASKED
password=

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.