I want to delete the last column of a txt file, while I do not know what the column number is. How could I do this?



1223 1234 1323 ... 2222 123
1233 1234 1233 ... 3444 125
0000 5553 3455 ... 2334 222

And I want my output to be:

1223 1234 1323 ... 2222
1233 1234 1233 ... 3444
0000 5553 3455 ... 2334
  • There are many ways to do this..please add an example and your expected output from it..
    – heemayl
    Oct 7, 2015 at 6:07
  • @heemayl ok I did
    – zara
    Oct 7, 2015 at 6:11
  • Thanks..are the columns tab separated or space separated?
    – heemayl
    Oct 7, 2015 at 6:12
  • @heemayl space is deliminator
    – zara
    Oct 7, 2015 at 6:14
  • cut sounds like the tool for the job.
    – Vorac
    Jun 29, 2020 at 2:57

9 Answers 9


With awk:

awk 'NF{NF-=1};1' <in >out


awk 'NF{NF--};1' <in >out


awk 'NF{--NF};1' <in >out

Although this looks like voodoo, it works. There are three parts to each of these awk commands.

The first is NF, which is a precondition for the second part. NF is a variable containing the number of fields in a line. In AWK, things are true if they're not 0 or empty string "". Hence, the second part (where NF is decremented) only happens if NF is not 0.

The second part (either NF-=1 NF-- or --NF) is just subtracting one from the NF variable. This prevent the last field from being printed, because when you change a field (removing the last field in this case), awk re-construct $0, concatenate all fields separated by space by default. $0 didn't contain the last field anymore.

The final part is 1. It's not magical, it's just used as a expression that means true. If an awk expression evaluates to true without any associated action, awk default action is print $0.

  • @JJoao: Ah, thanks, forgot about --. A note , currently, you need ;1 for POSIX compliant.
    – cuonglm
    Oct 7, 2015 at 10:04
  • 1
    My initial instinct would be to use a for loop, but this is much more concise and clever. Oct 8, 2015 at 13:22
  • 10
    It's worth noting that if you're using a non-default delimiter, you'll need to make some changes. Assuming , is your delimiter: awk -F',' 'BEGIN { OFS = FS }; NF { NF -= 1 }; 1' < in > out
    – Mr. Llama
    Mar 28, 2018 at 16:04
  • 2
    The effect of decrementing NF is undefined behavior by POSIX - you will get different output depending on which awk you're running. Some awks will remove the last field as you want, some will do nothing at all, and others could report a syntax error or so anything else.
    – Ed Morton
    Oct 29, 2018 at 11:38

Using Perl:

perl -lane '$,=" ";pop(@F);print(@F)' in

Using rev + cut:

rev in | cut -d ' ' -f 2- | rev

Using grep with PCRE:

$ grep -Po '.*(?=\s+[^\s]+$)' file.txt 
1223 1234 1323 ... 2222
1233 1234 1233 ... 3444
0000 5553 3455 ... 2334

Using GNU sed:

$ sed -r 's/(.*)\s+[^\s]+$/\1/' file.txt 
1223 1234 1323 ... 2222
1233 1234 1233 ... 3444
0000 5553 3455 ... 2334
  • 1
    @ramin Sure..could you please ask it as a new question (this is how this site works) :)
    – heemayl
    Oct 7, 2015 at 6:26
  • @ramin Does it give you any time restriction or any warning?
    – heemayl
    Oct 7, 2015 at 6:29
  • it says this is out of standard question!
    – zara
    Oct 7, 2015 at 6:29
  • @ramin Ok..let me contact an admin, may be they can help you with it..btw did you check any old QA regarding your question? its a possibility that the question is already asked and answered..
    – heemayl
    Oct 7, 2015 at 6:31
  • 3
    Don't ask super basic questions like "how can i rename a file name in Linux". Use Google. Oct 7, 2015 at 14:46

Using GNU sed:

sed -r 's/\s+\S+$//' input.txt

More generally, this one works with the BSD sed in OSX, as well as GNU sed:

sed 's/[[:space:]]\{1,\}[^[:space:]]\{1,\}$//' input.txt

If the delimiter is always a single char (so two or more consecutive delimiters designate empty fields), you could head just the first line from your input file, count the delimiters (n delimiters means number of fields is n+1) then use cut to print from the 1st field up to the nth field (second to last one), e.g. with tab-delimited input:

n=$(head -n 1 infile | tr -dc \\t | tr \\t \\n | wc -l)
cut -f1-$n infile > outfile

or e.g. with a csv file:

n=$(head -n 1 infile | tr -dc , | tr , \\n | wc -l)
cut -d, -f1-$n infile > outfile

I'll run some benchmarks later if I have the time but with huge input I think this solution should be faster than other solutions that use regex as this one does minimal processing on the first line to get the no. of fields and then uses cut which is optimized for this job.


Portably you can use either of these:

sed 's/[[:space:]]*[^[:space:]]*$//' file

awk '{sub(/[[:space:]]*[^[:space:]]*$/,"")}1' file

For people who have a similar problem but with different field separators this awk method will preserve the field separator correctly:

$ cat file 
$ awk -F'.' 'sub(FS $NF,x)' file

Using vim:

Open file in vim

vim <filename> 

Go to first row, just in case the cursor is placed anywhere else.


Create a macro named "q" qq, that goes to the back of the current line $, then goes back to the last space F (capital F, followed by literal SPACE) then delete from current position through end of line D go down to the next line j and stop macro recording with q.

qq$F Djq

Now we can repeat our macro with @q for each line.
We can also press @@ to repeat last macro or even easier:


to repeat the macro 99 times.
Note: The number must not exactly match the lines.


My solution with awk:

$ awk '{ for (i=1; i<NF; i++) if (i != NF - 1){printf("%s ", $i)} else {printf("%s\n", $i)} }' in > out

Or use different non-default delimiter:

$ awk -F, 'BEGIN { OFS = FS } { for (i=1; i<NF; i++) if (i != NF - 1){printf("%s%s", $i, OFS )} else {printf("%s\n", $i)} }' in > out

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .