3

I have a text file in which I must cut the fields 3,4,5 and 8:

219 432 4567 Harrison     Joel     M 4540 Accountant      09-12-1985
219 433 4587 Mitchell     Barbara  C 4541 Admin Asst      12-14-1995
219 433 3589 Olson        Timothy  H 4544 Supervisor      06-30-1983
219 433 4591 Moore        Sarah    H 4500 Dept Manager    08-01-1978
219 431 4527 Polk         John     S 4520 Accountant      09-22-1998
219 432 4567 Harrison     Joel     M 4540 Accountant      09-12-1985
219 432 1557 Harrison     James    M 4544 Supervisor      01-07-2000

Since the delimiter by default is tab the command to extract the fields would be:

cut -f 3,4,5,8 filename

The thing is that the output is the same as the original file content. What is happening here? Why doesn't this work?

  • I cannot reproduce this on Ubuntu 12.04, cut version 8.13. What are you running/what is the version of your cut? Do you maybe have an alias for cut that sets -d? – Anthon Oct 2 '13 at 5:45
  • Please verify in an editor or using od -a that there really is a tab character separating your fields. – Dubu Oct 2 '13 at 7:32
5

Not all those spaces between the columns look to be tabs, so cut won't be able to do what you want. I'd suggest using awk instead. It's more flexible than cut when parsing columns of data such as what you're trying to accomplish:

$ awk '{print $3,$4,$5,$8}' data.txt

Example

$ awk '{print $3,$4,$5,$8}' data.txt 
4567 Harrison Joel Accountant
4587 Mitchell Barbara Admin
3589 Olson Timothy Supervisor
4591 Moore Sarah Dept
4527 Polk John Accountant
4567 Harrison Joel Accountant
1557 Harrison James Supervisor

You can also space the output by using the column command:

$ awk '{print $3,$4,$5,$8}' data.txt |column -t
4567  Harrison  Joel     Accountant
4587  Mitchell  Barbara  Admin
3589  Olson     Timothy  Supervisor
4591  Moore     Sarah    Dept
4527  Polk      John     Accountant
4567  Harrison  Joel     Accountant
1557  Harrison  James    Supervisor

You can also do everything using just awk and printf:

$ awk '{printf "%s\t%-20s\t%s\n",$3,$4" "$5,$8}' data.txt 
4567    Harrison Joel           Accountant
4587    Mitchell Barbara        Admin
3589    Olson Timothy           Supervisor
4591    Moore Sarah             Dept
4527    Polk John               Accountant
4567    Harrison Joel           Accountant
1557    Harrison James          Supervisor

cut revisited

The above methods do an OK job, but they don't handle any of the lines where there are spaces within the value for a particular column. For example the line with "Dept Manager" get's chopped to just Dept.

If the data can be guaranteed to be structures as shown we could use cut but instead of splitting on a delimiter, we could just display using the actual positions of the characters.

Example

This will cut the text from the data.txt file and print whatever is at positions 9 through 13, and 14 through 35, etc.

$ cut -c 9-13,14-35,43-58 data.txt 
4567 Harrison     Joel     Accountant      
4587 Mitchell     Barbara  Admin Asst      
3589 Olson        Timothy  Supervisor      
4591 Moore        Sarah    Dept Manager    
4527 Polk         John     Accountant      
4567 Harrison     Joel     Accountant      
1557 Harrison     James    Supervisor      

awk revisited

Awk can also be made to pull text out based on it's position rather than by a delimiter. It's more verbose though, but here's how, just for completeness.

$ awk '{
    printf "%s\t%-20s\t%s\n",substr($0,9,5),substr($0,14,22),substr($0,43,16)
  }' data.txt
4567    Harrison     Joel       Accountant      
4587    Mitchell     Barbara    Admin Asst      
3589    Olson        Timothy    Supervisor      
4591    Moore        Sarah      Dept Manager    
4527    Polk         John       Accountant      
4567    Harrison     Joel       Accountant      
1557    Harrison     James      Supervisor      

awk FIELDWIDTHS

If you're using a variant of GNU awk you can use the variable FIELDWIDTHS to specify the static size of each field. This works out to be much cleaner than the substr method, if you have access to it. Also you can effectively glue together fields that would otherwise be parsed as separate fields.

$ awk 'BEGIN { FIELDWIDTHS="4 4 5 24 5 16 11" }{ print $3,$4,$5,$6 }' data.txt 
4567  Harrison     Joel     M  4540  Accountant      
4587  Mitchell     Barbara  C  4541  Admin Asst      
3589  Olson        Timothy  H  4544  Supervisor      
4591  Moore        Sarah    H  4500  Dept Manager    
4527  Polk         John     S  4520  Accountant      
4567  Harrison     Joel     M  4540  Accountant      
1557  Harrison     James    M  4544  Supervisor      
  • 1
    Your examples also document the drawback of using awk like this: The "Admin Asst" becomes "Admin" and the "Dept Manager" becomes "Dept", because they are split on the whitespace. Extracting the date would totally go awry on the second and fourth rows. So beware, this is not the solution if your fields contain whitespace. – Dubu Oct 2 '13 at 7:23
  • @Dubu - Agreed. There's a reason that CSVs exist. You can't use any character as a delimiter (spaces) and also have that same character contained within the strings you're trying to delimit! – slm Oct 2 '13 at 7:46
  • 1
    @Dubu - see updates for another alternative. – slm Oct 2 '13 at 7:59
  • See also the special FIELDWIDTHS variable as an alternative to using FS in the GNU implementation of awk. – Stéphane Chazelas Oct 2 '13 at 12:11
  • @StephaneChazelas - THANKS. Never noticed that variable before. – slm Oct 2 '13 at 13:02
1

My guess is that I don't think those are tabs. The reason I don't think they are tabs is because when I copypaste the file and manually tabulate the fields, then the cut -f 3,4,5,8 filename seems to work fine. You might be better off doing cat filename | awk '{print $3, $4, $5, $8}' if you don't want to retabulate fields and values.

  • There is no need to use cat, awk can read input files perfectly well. – Sam Brightman Sep 16 '16 at 12:52

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.