13

I want to delete the 5th word of each line in a file.

The current content of the file:

File is not updated or and will be removed  
System will shut down f within 10 seconds  
Please save your work 55 or copy to other location  
Kindly cooperate with us D  

Expected output:

File is not updated and will be removed  
System will shut down within 10 seconds  
Please save your work or copy to other location  
Kindly cooperate with us

9 Answers 9

31

How about cut :

$ cut -d' ' -f1-4,6- file.txt 
File is not updated and will be removed  
System will shut down within 10 seconds  
Please save your work or copy to other location  
Kindly cooperate with us
  • -d' ' sets the delimiter as space

  • -f1-4,6- selects the first to 4th field (word), leaving the 5th one and then continue printing from 6th to the rest.

11

A solution with cut:

cut -d ' ' -f1-4 -f6- FILE
3
  • Multiple -f is not supported in my cut (GNU) at least..
    – heemayl
    Jul 14, 2015 at 15:54
  • Supported in BSD cut but I like your response better than mine.
    – fd0
    Jul 14, 2015 at 15:56
  • 1
    If it's GNU cut, you get the --complement flag to simplify things: cut --complement -d ' ' -f5. Remember to redirect the output to a new file, then mv it over the original. Jul 15, 2015 at 10:30
6

awk: remove the 5th field

awk '{for (i=5; i<NF; i++) $i = $(i+1); NF--};1' file

If you want to save the file in-place: https://stackoverflow.com/q/16529716/7552

You could just erase the contents of the 5th field, but that leaves 2 consecutive output field separators:

awk '{$5 = ""};1' file
2
  • the caveat here is that changing any field's value in awk has the side effect of rewriting the whole "$0" with only 1 separator between each fields. should be taken into account if you wanted to keep any alignment (unless gnu awk has an option avoiding this? regular awk/nawk will recompute $0) Jul 15, 2015 at 0:57
  • In both case you reformat the line with a single separator. If there is 2 space or space +tab in a separator, the result is a single space in place. This is hoppefully OK for most of the text. Jul 16, 2015 at 9:02
4

With POSIX sed:

sed -e 's/[^[:alnum:]_][[:alnum:]_][[:alnum:]_]*//4' <file
6
  • why limit the class to :alnum: and _ and not anything else then :blank: or :space: ? Jul 16, 2015 at 9:03
  • @NeronLeVelu: That's depend on how you define what make a word.
    – cuonglm
    Jul 16, 2015 at 9:22
  • @mikeserv; Nice catch! I updated my answer.
    – cuonglm
    Jul 16, 2015 at 16:46
  • What's the \( capture group \) for?
    – mikeserv
    Jul 16, 2015 at 16:55
  • @mikeserv: my mis-typing, I have just tried some ways to retain the delimiter.
    – cuonglm
    Jul 16, 2015 at 17:00
2

glenn offered a solution that is equivalent to

awk '{$5=""; print}' file

As he and others have pointed out, this

  1. strips leading and trailing whitespace from every line,
  2. compresses each string of whitespace (spaces and/or tabs) into a single space, and
  3. leaves two spaces between the fourth and six words.

A hack to fix the third problem is

awk '{$5=""; print}' file | sed 's/  / /'

This will still leave one or more added space(s) at the end of any line that had five or fewer words going in.  If you can identify a word that will never appear in the input,

awk '{$5="unicorn"; print}' file | sed 's/ *unicorn//'

will handle even that (but it still leaves problems 1 and 2).

2
 sed 's/^\(\([[:blank:]]*[^[:blank:]]\{1,\}\)\{4\}\)[[:blank:]]*[^[:blank:]]*/\1/' YourFile > Output.txt
  • posix sed based on space/tab separator (meta class [:blank:]])
  • keep the following space after 5th word but removing the one before

A more robust (sed take the longest pattern possible and pattern with * could miss separation or word in first version) but a bit longer version

sed 's/^\([[:blank:]]*\([^[:blank:]]\{1,\}[[:blank:]]\{1,\}\)\{4\}\[^[:blank:]]\{1,\}/\1/' YourFile > Output.txt
5
  • 1
    sed 's/[^[:blank:]]*//5'
    – mikeserv
    Jul 16, 2015 at 9:09
  • @mikeserv, this will keep both surrounding separator, sed 's/[[:blank:]*[^[:blank:]]*//5' is better. Very good point. I suspected that sed take each single char as a entity but it take greatest unbreaked pattern as entity Jul 16, 2015 at 11:08
  • sed 's/[[:blank:]][^[:blank:]]*//4' will remove the 5th field entirely.
    – mikeserv
    Jul 16, 2015 at 13:20
  • @mikeserv Assuming there is not starting space on the line (like in the sample) Jul 17, 2015 at 7:25
  • In this case, yes, I think you're right. Usually such a thing would be a null field and the behavior would be correct. In this case you should do as @cuonglm did and ensure you reference a word each time like sed 's/[[:blank:]][^[:blank:]][^[:blank:]]*//4', or, w/ GNU/BSD/toybox seds: sed -E 's/[[:blank:]][^[:blank:]]+//4'.
    – mikeserv
    Jul 17, 2015 at 7:41
1

Perl.

perl -ne 'print $_ =~ /^(\w+ +\w+ +\w+ +\w+ +)\w+ (.*)/,"\n"' file
1

Another possibility, assuming GNU cut:

cut -d' ' -f5 --complement file.txt
-1

Using Perl > 5.10 (and successfully outputting all lines :0) ):-

perl -nE '/^((\w+ +){4})\w+ *(.*)/; say $1.$3' file
0

You must log in to answer this question.

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