So I have a large file like the following:
RESOURCETAGMAPPINGLIST arn:aws:ec2:us-east-1:XXXXXX:instance/i-XXXXXXXXXXXXXXXXX
TAGS app-name appname1
RESOURCETAGMAPPINGLIST arn:aws:ec2:us-east-1:XXXXXX:instance/i-XXXXXXXXXXXXXXXXX
TAGS app-name appname2
RESOURCETAGMAPPINGLIST arn:aws:ec2:us-east-1:XXXXXX:instance/i-XXXXXXXXXXXXXXXXX
TAGS app-name appname1
..
I only want to modify the line with RESOURCETAGMAPPINGLIST
and print the the other lines w/out modification. Then I want to print only specific fields on the matching like, like below:
arn ec2 us-east-1 XXXXXX
TAGS app-name appname1
arn ec2 us-east-1 XXXXXX
TAGS app-name appname2
arn ec2 us-east-1 XXXXXX
TAGS app-name appname1
..
I was trying using awk gsub command, but really could not get the -F:
part to work out. Any help would be greatly appreciated and it does not matter if it's awk, sed or perl.
CodePudding user response:
With awk
. I used as input field separators at least one space (
) or (|
) one colon (:
).
If a row contains string RESOURCETAGMAPPINGLIST
then print columns 2, 4, 5, 6 and stop processing this row and continue with next row. If a row does not contain RESOURCETAGMAPPINGLIST
then print complete row unchanged.
awk -F ' |:' '/RESOURCETAGMAPPINGLIST/{print $2,$4,$5,$6; next} {print}' file
Output:
arn ec2 us-east-1 XXXXXX TAGS app-name appname1 arn ec2 us-east-1 XXXXXX TAGS app-name appname2 arn ec2 us-east-1 XXXXXX TAGS app-name appname1
See: The Stack Overflow Regular Expressions FAQ
CodePudding user response:
One awk
idea using the default field delimiter and split()
:
awk '
/RESOURCETAGMAPPINGLIST/ { split($2,a,":") # split 2nd field on ":" delimiter, storing results in array a[]
print a[1],a[3],a[4],a[5]
next # skip to next line of inpu
}
1 # print current line
' sample.dat
# or as a one-liner sans comments:
awk '/RESOURCETAGMAPPINGLIST/ {split($2,a,":"); print a[1],a[3],a[4],a[5]; next} 1' sample.dat
This generates:
arn ec2 us-east-1 XXXXXX
TAGS app-name appname1
arn ec2 us-east-1 XXXXXX
TAGS app-name appname2
arn ec2 us-east-1 XXXXXX
TAGS app-name appname1
CodePudding user response:
Here is one way to do it:
#!/usr/bin/perl
use v5.30; # Perl v5.10 or above is required to use 'say' instead of 'print'
use warnings;
my $file = "datafile.txt"; #declare file name
open( my $fh, "<", $file ) or die( "Can't open `$file`: $!\n" );
#open the file with file handle $fh
while (my $line = <$fh>){ #read the file line by line
chomp $line; #remove line breaks
if ($line =~ m/^RESOURCETAGMAPPINGLIST/){ #if line start with
my @fields = split(/\t|:/, $line); #split in tab or : characters; each field becomes an element of @fields array
say (join ' ', (@fields[1,2,3,4])); #join the relevant fields with a single space and print
}else{
say $line; #if line does not start with RESOURCETAGMAPPINGLIST, simply print it
}
}
#when script finishes, Perl will automatically close the input file.
CodePudding user response:
I assume that one keeps the fields 1,3,4,5 from the colon-separated part.
perl -wple'
s{^RESOURCETAGMAPPINGLIST\s (. )}
{ join " ", ( $1 =~ /([^:] )/g )[0,2..4] }e' file
With -p
the variable $_
, which has the line to process, is printed after the processing.
So if that keyword isn't matched the $_
remains unchanged. If it is matched it gets replaced with the return of the code in the replacement side. (With /e
modifier the replacement side is evaluated as code.)
Or: test for the word, and either split the line and join the needed parts or print it as is
perl -wnlE'say
/^RESOURCETAGMAPPINGLIST/ ? join " ", (split /\s |:/)[1,3..5] : $_' file
or first split and then test and print accordingly
perl -wnE'
@f = split /\s |:/;
say $f[0] eq "RESOURCETAGMAPPINGLIST" ? "@f[1,3,4,5]" : "@f"' file
This retains only single spaces (until it is clarified whether it should preserve spaces).
All are broken into lines so that it is easier to read. They can be copy-pasted as they are (in bash), or brought onto one line. Or, this can be written far more nicely in a file, but the question seems to be asking for a command-line program.