0

here is my sample file:

Host dns2
        HostName 172.20.4.80
        User root
        Port 22

Host dns1
        HostName 172.20.4.75
        User root
        Port 22

Host dns3
        HostName 172.20.4.76
        User root
        Port 22

Host dns4
        HostName 172.20.4.77
        User root
        Port 22

Host dns5
        HostName 172.20.4.78
        User root
        Port 22

Host dns6
        HostName 172.20.4.79
        User root
        Port 22

i want to print only one block means for example

Host dns1
        HostName 172.20.4.75
        User root
        Port 22

output :

Host: dns2  HostName: 172.20.4.80   User: root  Port: 22

but in this example all blocks have 4 lines maybe they reach to 5 or more lines later so i want to print from Host to first blank line or remove from Host to first blank line

im really bad at regex and need this to complete my script

thank you

Arash Shams
  • 186
  • 1
  • 2
  • 12

5 Answers5

3

I think that you basically want this:

awk -v RS='' '/dns1/' file

Unset the record separator so that each block is treated as a record, then print whichever record matches the pattern.

Or to use a shell variable:

host=dns1
awk -v host="$host" -v RS='' '$0 ~ host' file

In both of these examples, I'm using the fact that the default action is { print }. As you will likely be changing the output by using { printf ... } you may want to consider adding an exit statement to avoid processing the rest of the file unnecessarily.

Tom Fenech
  • 65,210
  • 10
  • 85
  • 122
  • 1
    thank you it works but can you modify the command to show my desired output ? – Arash Shams Jan 22 '16 at 12:40
  • You can add a block like `{ printf "%s %s", $1, $2 }` to the end of the awk script - have a play around with it. – Tom Fenech Jan 22 '16 at 12:42
  • 1
    i used { print ($1~/^Host$/ ? "" : "\t") $1 ": " $2} but unfortunately not worked only print first line Host foo and ofcourse after using { printf "%s %s", $1, $2 } can you please help and give me a sed command to remove too ? – Arash Shams Jan 22 '16 at 12:48
  • You don't need to use sed as well. The example I gave you only prints the first two fields `$1` and `$2` - if you want more fields, then add some more `%s` to the format specifier and then add `$3`, $4`, etc. to the list of arguments. – Tom Fenech Jan 22 '16 at 13:43
  • 1
    for efficiency: `awk -v RS= '/dns1/ {print whatever; quit}' file` – glenn jackman Jan 22 '16 at 14:11
  • @glenn I guess you mean `exit` but yes, I edited, thanks. – Tom Fenech Jan 22 '16 at 14:19
2

a similar awk

$ awk -v RS= -v OFS=' ' '{for(i=1;i<NF;i+=2) $i=$i":"}1' hosts

Host: dns2 HostName: 172.20.4.80 User: root Port: 22
Host: dns1 HostName: 172.20.4.75 User: root Port: 22
Host: dns3 HostName: 172.20.4.76 User: root Port: 22
Host: dns4 HostName: 172.20.4.77 User: root Port: 22
Host: dns5 HostName: 172.20.4.78 User: root Port: 22
Host: dns6 HostName: 172.20.4.79 User: root Port: 22

will give you all records in the desired output format. You can filter either this output further or add a pattern such as

$ awk -v RS= -v OFS=' ' '{for(i=1;i<NF;i+=2) $i=$i":"} /dns2/' hosts

Host: dns2 HostName: 172.20.4.80 User: root Port: 22

if want to exit after processing the selected record, you need to slightly change the script

$ awk -v RS= -v OFS=' ' '/dns2/{for(i=1;i<NF;i+=2) $i=$i":"; print; exit}' hosts
Host: dns2 HostName: 172.20.4.80 User: root Port: 22

If you want to select everything except one record you can negate the pattern (and remove exit)

$ awk -v RS= -v OFS=' ' '!/dns2/{for(i=1;i<NF;i+=2) $i=$i":"; print}' hosts
Host: dns1 HostName: 172.20.4.75 User: root Port: 22
Host: dns3 HostName: 172.20.4.76 User: root Port: 22
Host: dns4 HostName: 172.20.4.77 User: root Port: 22
Host: dns5 HostName: 172.20.4.78 User: root Port: 22
Host: dns6 HostName: 172.20.4.79 User: root Port: 22

Note that sed inplace replacement needs an intermediary file. If you want to replace the original file with the formatted one sans one record, you can use this command pattern on the last awk statement

$ awk ... > temp && mv temp original

UPDATE: setting OFS will change all the separators between fields. You want to logically group them by name: value, so change the script as such

$ awk -v RS= '{for(i=1;i<NF;i++) $i=$i (i%2?":":"\t")}1' hosts
Host: dns2       HostName: 172.20.4.80   User: root      Port: 22
Host: dns1       HostName: 172.20.4.75   User: root      Port: 22
Host: dns3       HostName: 172.20.4.76   User: root      Port: 22
Host: dns4       HostName: 172.20.4.77   User: root      Port: 22
Host: dns5       HostName: 172.20.4.78   User: root      Port: 22
Host: dns6       HostName: 172.20.4.79   User: root      Port: 22

which sets a tab delimiter after even positioned fields.

karakfa
  • 62,998
  • 7
  • 34
  • 47
  • 1
    Thank you very much can please give a sed command to delete this block ? for example delete dns2 – Arash Shams Jan 22 '16 at 14:50
  • You don't need `sed`, with `awk` you can select the rest of the records easily. See the update. – karakfa Jan 22 '16 at 14:55
  • 1
    you mean i use awk select everything except that line and redirect to that file again ? – Arash Shams Jan 22 '16 at 14:57
  • 1
    if i want to append a tab between items what can i do ? Host: dns2 HostName: 172.20.4.80 User: root Port: 22 – Arash Shams Jan 22 '16 at 16:15
  • 1
    your replacement is not working because you send output to original and its not oldfile its a new file with Host: dns1 HostName: 172.20.4.75 User: root Port: 22 not Host dns1 HostName 172.20.4.75 User root Port 22 so still sed i think its the best solution – Arash Shams Jan 22 '16 at 18:41
  • There is nothing not to work! You really confused me!? The formatted output of the script does not exist anywhere to be overwritten! Obviously, you can save it first and then delete the unwanted line but why? You can easily create the desired filtered output in one step as in one of the scripts above. – karakfa Jan 22 '16 at 18:53
  • http://stackoverflow.com/questions/34960569/delete-a-block-of-text-until-the-first-blank-line-using-sed i refer you to new question thanks – Arash Shams Jan 23 '16 at 06:53
  • i modified your code to add colors for each group foo: bar for example foo is yellow and bar is green but not working awk -v green="\033[0;32m" -v reset="\033[0m" -v RS='' -v OFS=' ' '/'"$name"'/{for(i=1;i – Arash Shams Jan 23 '16 at 08:57
1

Not different than Tom Fenech approach, since it uses the record separator, but it plays with the field separator too to obtain the desired output:

awk -v RS='' -F'\n[\t ]*' -v OFS='  ' '/dns1/{$1=$1;print}' file

When you change the output field separator, you need to use $1=$1 (or $0=$0 or with any other field) to force awk to re-evaluate the record and to take in account the new field separator.

Notes: you can exit awk when a matching block is found with the exit command. This avoids to process all the end of the file. You can also only test the pattern /dns1/ with the first field.

awk -v RS='' -F'\n[\t ]*' -v OFS='  ' '$1~/dns1/{$1=$1;print;exit}' file

If you add semi-colons to the result, since you modify the fields, the $1=$1 trick becomes useless. You can write:

awk -v RS='' -F'\n[\t ]*' -v OFS='  ' '$1~/dns1/{for(i=1;i<=NF;i++){sub(" ", ": ", $i)};print;exit}' file
Casimir et Hippolyte
  • 83,228
  • 5
  • 85
  • 113
  • 1
    thank you very much its the closest answer till now , just 2 things first if i want to print all lines with my desired output what can i do? second can you please give me sed command to remove the block please ? – Arash Shams Jan 22 '16 at 14:21
  • 1
    FYI not putting a space between the `-v` and the variable name makes the script gawk-specific so you should state that or just put a space in there so it's not gawk-specific. – Ed Morton Jan 22 '16 at 14:58
1

To print the 3rd record:

$ awk -v RS= -F'\n[[:blank:]]+' -v OFS='\t' 'NR==3{$1=$1; gsub(/ +/,": "); print}' file
Host: dns3      HostName: 172.20.4.76   User: root      Port: 22

To print the records that contains dns4:

$ awk -v RS= -F'\n[[:blank:]]+' -v OFS='\t' '/dns4/{$1=$1; gsub(/ +/,": "); print}' file
Host: dns4      HostName: 172.20.4.77   User: root      Port: 22

To print all records except those that contain dns3, dns4, or dns5:

$ awk -v RS= -F'\n[[:blank:]]+' -v OFS='\t' '!/dns[345]/{$1=$1; gsub(/ +/,": "); print}' file
Host: dns2      HostName: 172.20.4.80   User: root      Port: 22
Host: dns1      HostName: 172.20.4.75   User: root      Port: 22
Host: dns6      HostName: 172.20.4.79   User: root      Port: 22
Ed Morton
  • 157,421
  • 15
  • 62
  • 152
0

This might work for you (GNU sed):

 sed -n '/Host dns1/{:a;N;/^\s*$/M!ba;s/\n\s*/  /g;s/\s*$//p}' file

This focuses in on the required string, then appends following lines until a blank one and finally manipulates the new string that is gathered into the required output.

potong
  • 47,186
  • 6
  • 43
  • 72