Ask Ubuntu is a question and answer site for Ubuntu users and developers. It's 100% free.

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have to sort all of the words from a file given as parameter in a shell script. Here is the one-liner:

tr  [:space:] '\n' <$1  | sort -nrk2,2 | uniq -c |sed 's/^ \+//g'

Basically, if I have something like this in my file:

bla bla bla bla hu hu hu

It will output

4 bla
3 hu

I want them to be something like

bla 4
hu 3
share|improve this question
    
Also I believe you should replace sort -nrk2,2 with just sort -nr. In this case k2,2 just doesn't do anything since you trimmed the whitespace beforehand (i.e. there is just one field), but normally that wouldn't necessarily produce a file sorted the way uniq -c requires. – kos Jan 16 at 17:02
up vote 4 down vote accepted

You could replace your sed command with a simple awk command that swaps the field order

... | awk '{print $2,$1}'
share|improve this answer
    
What about if i have, let's say ,2 words with exactly the same number of occurences in a text , but i want to print only the first in alphabetically order ( let's say my output in this case is steel 4 and driver 4 ,but i will print only driver 4)? – gigiman Jan 16 at 16:32
1  
@gigiman you could either add an additional sort -uk1,1 in the pipeline, or modify the awk to build an array of the first result for each count, and output the array at the end of processing e.g. awk '!a[$1]{a[$1]=$2} END{for (i in a) print a[i],i}' – steeldriver Jan 16 at 16:44
    
Wow , thanks so much steeldriver you're really a life saver ! – gigiman Jan 16 at 16:50

There are many, many ways of doing this. Steeldriver already gave you the classic awk approach. Here are some other choices:

  1. Use sed to capture two groups of non-whitespace (\S) characters and then switch them around:

    ... | sed -E 's/\s*(\S+)\s+(\S+)/\2 \1/'
    
  2. Use perl. Its -a switch makes it work like awk. It will automatically split each input line on whitespace and save each field as an element of the array @F. Therefore, the 1st field will be $F[0], the second $F[1] etc.:

    ... | perl -lane 'print "$F[1] $F[0]"'
    
  3. Use Perl for the whole thing:

    perl -lane '$k{$_}++ for @F; }{ print "$_ $k{$_}" for keys(%k)' "$1"
    

    Here, perl reads the input file line by line and applies the script to each line. $k{$_}++ for @F saves each word (each field from @F) as a key in the hash %k and increments the associated value by one each time the word is seen. Then, after the file has been processed (that's what the }{ means), it will print the word ($_) and the number of times it was seen ($k{$_}) for each key stored in the hash.

  4. Use awk for the whole thing:

    awk '{for(i=1;i<=NF;i++){a[$i]++}}END{for(i in a){print i,a[i]}}' "$1"
    

    The first for loop iretares over each field and adds one to the value associated with that field in the array a. Then, at the end of the file, it loops over each element in a and prints the element (the word) and the associated value (the number of times the word was seen).

  5. Use the shell with your original pipeline:

    ... | while read a b; do echo "$b $a"; done
    
share|improve this answer
    
+1 nice roundup! these kinds of questions always make me want to quote the barometer question – steeldriver Jan 16 at 17:05
    
@steeldriver heh, indeed. – terdon Jan 16 at 17:11
    
@terdon thanks alot , now i have a much more ample vision about formatting in bash :) – gigiman Jan 16 at 18:03
    
Come on Guru..i want some free points too ;) – heemayl Jan 17 at 4:11

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.