I want to compare one file with other files in the same directory.

file1.txt contains:

move 34#123#    
get 11#278    
put 33#29#567#23

file1.txt should compare with files 1.txt and 2.txt.

1.txt contains:

move 11    
put 34    
run 13

2.txt contains:

get 14    
move 66

I need the results to be like this:

move 2    
get 1    
put 1

How can I accomplish this?

closed as unclear what you're asking by Kusalananda, GAD3R, thrig, Tomasz, countermode Feb 18 '17 at 21:12

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • You want the count of the number of move and get and put lines? – slm Apr 21 '14 at 6:43

Here is an awk solution:

$ awk 'FNR==NR{a[$1];next}($1 in a){++a[$1]}
    END{for(i in a){print i" "a[i]}}' file1.txt 1.txt 2.txt
put 1
get 1
move 2


  • FNR==NR{a[$1];next}: when processing file1.txt, we mark the occurence of $1 in associative array a.
  • ($1 in a){++a[$1]}: when processing 1.txt and 2.txt, we check if $1 is existed in associative array a, if yes, increasing the count by 1.
  • Finally, loop through associative array a, print the key (first field in files) and its value (number of ocurrences in 1.txt and 2.txt).

Here is another solution in perl, with the same logic:

$ perl -alne '++$i;                  
    if ($. == $i) {
        close ARGV if eof;
    ++$h{$F[0]} if defined $h{$F[0]};
    for (keys %h) {
        print $_." ".$h{$_};
}' file1.txt 1.txt 2.txt
move 2
get 1
put 1

Showing an alternative that uses a variety of tools, grep, awk, sort, & uniq. Granted it makes use of more tools but I find it easier to understand what's going on vs. AWK.

$ for i in 1.txt 2.txt; do grep -f <(awk '{print $1}' $i) file1.txt; done | \
    awk '{print $1}' | sort | uniq -c


$ for i in 1.txt 2.txt; do grep -f <(awk '{print $1}' $i) file1.txt; done | \
    awk '{print $1}' | sort | uniq -c
      1 get
      2 move
      1 put

How it works

Here's the example unrolled a bit.

$ for i in 1.txt 2.txt; do 
    grep -f <(awk '{print $1}' $i) file1.txt
  done | \
    awk '{print $1}' | sort | uniq -c

The for loop goes through the 2 files that we're matching against, 1.txt and 2.txt. Each time through the loop we're using the first column from one of these files as a static set of strings to grep looking for these in our target file, file1.txt. This is the line that does that:

$ grep -f <(awk '{print $1}' $i) file1.txt

After running that using each file from the for loop we take all that output and select only the first column of it:


We then use sort and uniq to tally up how many of each type we saw.


Here's another way with join:

join -1 1 -2 2 -o 0 2.1 <(sort -k1,1 file1.txt) <(cut -d ' ' -f1 1.txt 2.txt | sort | uniq -c)

note that the output will be sorted:

get 1
move 2
put 1

If you want to preserve the order from file1.txt e.g.

move 2
get 1
put 1

you could run:

join -j2 -o 1.1 0 2.1 <(nl -ba -nrz file1.txt | sort -k2,2) \
<(cut -d ' ' -f1 1.txt 2.txt | sort | uniq -c) | sort -k1 | cut -d ' ' -f2-

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