10

I want to print all columns from nth to last column of a line

Input String in file

vddp vddpi vss cb0 cb1 cb2 cb3 ct0 ct1 ct2 ct3  

Command

cat <file> | awk ' { for (i=3; i<=NF; i++)   print $i }'

Current Output

cb0
cb1
cb2
cb3
ct0
ct1
ct2
ct3

Desired Output

cb0 cb1 cb2 cb3 ct0 ct1 ct2 ct3

I am trying the awk iteration, but cannot get desired output

11 Answers 11

12

Doesn't answer your question using the awk approach requested, but I'll just point out that cut can be employed for this task.

cut -f4- -d' ' input
  • 7
    cut expects one and only one SPC between fields and doesn't ignore leading and trailing ones, while awk ignores leading SPC+TABs (sometimes other [:blank:], sometime including newline, sometimes all of ASCII [:space:]) and treats sequences of one or more whitespace as delimiter. – Stéphane Chazelas Jan 12 at 8:12
11
awk -v n=4 '{ for (i=n; i<=NF; i++) printf "%s%s", $i, (i<NF ? OFS : ORS)}' input

This will take n as the value of n and loop through that number through the last field NF, for each iteration it will print the current value, if that is not the last value in the line it will print OFS after it (space), if it is the last value on the line it will print ORS after it (newline).

$ echo 'vddp vddpi vss cb0 cb1 cb2 cb3 ct0 ct1 ct2 ct3' |
> awk -v n=4 '{ for (i=n; i<=NF; i++) printf "%s%s", $i, (i<NF ? OFS : ORS)}'
cb0 cb1 cb2 cb3 ct0 ct1 ct2 ct3
8

command

awk '{$1=$2=$3="";print $0}' filename| sed -r "s/^\s+//g"

command

cb0 cb1 cb2 cb3 ct0 ct1 ct2 ct3
  • 10
    awk '{$1=$2=$3=""; $0=$0; $1=$1; print}' file – RudiC Jan 12 at 12:28
  • @RudiC you should make that an answer -- it's much better than your answer above ;-) – mosvy Jan 12 at 16:44
  • @mosvy : yes, thank you - but it is not mine; don't want to steal an idea. – RudiC Jan 12 at 19:05
6

If you want to preserve the spacing in between the fields that are going to be preserved, you might as well do:

<file awk '{sub(/^[[:blank:]]*([^[:blank:]]+[[:blank:]]+){3}/, ""); print}'

(though note that not all awk implementations support {3} even though it's POSIX).

That is use a regular expression that matches the first 3 fields.

Or simply use sed to do that:

<file sed 's/^[[:blank:]]*\([^[:blank:]]\{1,\}[[:blank:]]\{1,\}\)\{3\}//'

Those assume that all the lines in the input have at least 4 fields.

On a a b c d <tab>e f <nl> input, that will give d <tab>e f <nl> output.

  • ...and quietly lurking in the forest was the right answer :-). The awk command should be using sub() instead of gsub() but no real harm done other than to make some of us pause to think about why it's using gsub()! – Ed Morton Jan 13 at 3:52
  • 1
    @EdMorton Thanks. good point about gsub. I've replaced it with sub. – Stéphane Chazelas Jan 13 at 7:14
3

An improvement on the $1=$2=$3="" trick which works with any FS or OFS, provided that normalizing the output line to $4 OFS $5 OFS ... is acceptable:

awk 'BEGIN{p=length(OFS)*3+1} {$1=$2=$3="";print substr($0,p)}'

Assigning a value to any field (eg. $1="" or $1=$1) will cause $0 (the whole record/line) to be rebuilt (the first time it is used) by joining the $1, $2, ... fields with the OFS variable.

So $1 = $2 = $3 = "" will have the effect of setting $0 to OFS OFS OFS $4 OFS $5 ..., and the substr will get rid of leading OFSs (notice that indexes start from 1 in awk).

  • Collapses blank space between fields. – Isaac Jan 13 at 16:50
3

with awk that supports regex in field separator, you can do:

awk -F'^[[:blank:]]*([^ \t]*[ \t]+){3}' '{ print $2 }' infile
cb0 cb1 cb2 cb3 ct0 ct1 ct2 ct3

replace number 3 in {3} with number of columns you need ignore from the beginning; and space/tab with field separator which your columns delimited with other than whitespaces; so printing $2 would be the rest of columns remaining to the end; this solution will preserve multiple whitespaces between fields if any in result.

notes: to avoid printing empty line if there was a line with equal or less than 3 fields (that indicates the # (number) of columns you need cut out) , add NF>1 to the command.

awk -F'^[[:blank:]]*([^ \t]*[ \t]+){3}' 'NF>1{ print $2 }' infile

[[:blank:]]* is used to avoid erroneous output when FS is whitespaces; if FS is something else (e.x: comma ,), then the command would be even simpler:

awk -F'^([^,]*,){3}' 'NF>1{ print $2 }' infile
  • Every awk supports a regexp for FS (it's mandated by POSIX and even non-POSIX awks support it). Only gawk supports \s shorthand for [[:space:]] though so you should change that or just say its gawk-only. If you keep it then it doesn't make sense to use \s in one place and [ \t] in another so you can replace the [ \t] with \\s and [^ \t] with \\S. – Ed Morton Jan 13 at 3:57
  • thanks I have edited with your suggestion but [[:blank:]] instead of [[:space:]] to match also tab as well as space, but I kept using [ \t] amd [^ \t] since they are representing field seperator and it's to understand where to modify if one's field seperator is something other than whitespaces and only need change ` \t` part there – αғsнιη Jan 13 at 4:26
  • You're welcome. If the OP changes to use something other than the default FS then you'd have to get rid of the leading [[:blank:]]* too. – Ed Morton Jan 13 at 4:29
  • 1
    @EdMorton thanks, see update please. – αғsнιη Jan 13 at 5:16
2

Tried in Python 2:

#!/usr/bin/python
k=open('filename','r')
for i in k:
    k=i.strip().split(' ')[3:]
    print " ".join(k)

output

cb0 cb1 cb2 cb3 ct0 ct1 ct2 ct3
1

How about

awk 'sub ("^" $1 FS $2 FS $3 FS, _)' file
cb0 cb1 cb2 cb3 ct0 ct1 ct2 ct3  
  • Doesn't work with FS=' ' (the default) when the fields are separated by multiple spaces or tabs or combinations thereof. Also fails with FS='*' or FS='.'. – mosvy Jan 12 at 16:19
  • 1
    also since first parameter of sub is regex so it will fail if one of columns 1 or 2 or 3 was containing characters other than alpha/num/alnum; for example when a line starts with [0-9] – αғsнιη Jan 12 at 16:40
1

print insert a newline by default. Use printf instead of print

echo "vddp vddpi vss cb0 cb1 cb2 cb3 ct0 ct1 ct2 ct3" | awk ' { for (i=4; i<=NF; i++) printf "%s " ,$i }'

For multiple lines:

cat filename.txt | awk ' { for (i=4; i<=NF; i++) 
if (i==NF) 
printf "%s\n", $i
else
printf "%s ", $i
}'
  • Didn't thought about multiple lines. Added the code for multiple line. – koolwithk Jan 13 at 15:33
0

Perl solution:

space-delimited:

perl -lane 'print "@F[3..$#F]"' file

tab-delimited:

perl -F'\t' -lane 'print join "\t", @F[3..$#F]' file

Here, the command-line flags are:
-e: tells perl interpreter to use the inline code rather than code in file.
-n: loops over the input one line at a time, assigning the input line to $_.
-l: strip the input line separator (new line in UNIX) before the code in the one-liner, and add the output line separator after the print.
-a: split each line in array @F, on whitespace by default.
-F: split on the provided pattern, rather than on whitespace.

@F[3..$#F]: fields with indexes from 3 to $#F (the last element of @F), indexes are zero-based.

  • 1
    if you're to use perl instead of awk, you can use split with a limit and easily preserve the original separators: perl -nle 'print((split " ", $_, 4)[3])' or perl -pe '$_ = (split " ", $_, 4)[3]' – mosvy Jan 12 at 17:14
0

Retain white space after the field(s) removed:

$ sed -E 's/^[[:blank:]]*([^[:blank:]]+[[:blank:]]*){3}//' file
$ awk '{sub(/^[[:blank:]]*([^[:blank:]]+[[:blank:]]*){3}/,"")};1' file
$ grep -oP '^[[:blank:]]*([^[:blank:]]+[[:blank:]]*){3}\K.*' file

Description

There is a way to match fields with a regex: [[:blank:]]*[^[:blank:]]+.
Match an optional stream of blanks (space or tab) followed by an stream of non-blanks. If that pattern is repeated 3 times, 3 fields (with the leading white space) are matched. Then, those could be removed with any tool that is able to process regexes (with variations). Note that there may be white space before and/or after each field, that white space needs to be also matched. Matching any leading white space and the delimiting trailing white space will make the regex a bit longer: ^[[:blank:]]*([^[:blank:]]+[[:blank:]]*){n} where n is an integer that points to the last field matched.

Alternative with GNU awk:

$ awk -vrem=3 '{ 
                 n=split($0,f,FS,seps);
                 for(i=rem+1;i<=n;i++){
                                        printf("%s%s",$i,seps[i])
                                      };
                 print ""
               }
              '  file

White space collapsed

$ awk '{$1=$2=$3=""; $0=$0; $1=$1 } 1 ' file      # thanks RudiC

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.