Finding My Most Commonly Used Commands On Linux

28th November 2020 - 11 minutes read time

I'm a proponent of automation, so when I find myself running the same commands over and over I always look for a way of wrapping that in an alias or script.

I spend a lot of my day to day job in the command line and I realised today that I must have typed 'git status' for the millionth time and wondered what my most commonly used commands were. So I found a stack overflow post showing my most used commands in a nice little bash one liner.

history | awk '{CMD[$2]++;count++;}END { for (a in CMD)print CMD[a] " " CMD[a]/count*100 "% " a;}' | grep -v "./" | column -c3 -s " " -t | sort -nr | nl |  head -n10

This command extracts information from the bash history and shows me the root command I have run. This showed me the following on my current system.

  1. 1 6865 68.3901% git
  2. 2 761 7.58119% cd
  3. 3 441 4.39331% ll
  4. 4 425 4.23391% vagrant
  5. 5 275 2.73959% composer
  6. 6 149 1.48436% ssh
  7. 7 146 1.45447% exit
  8. 8 128 1.27515% rm
  9. 9 73 0.727237% sudo
  10. 10 72 0.717274% mv

It looks like I've used git in nearly 70% of the commands I have run. This is interesting yet meaningless without seeing what git commands I'm actually running. I decided to drill down into this data a little to find out what commands I run the most, and perhaps in a particular order, in an attempt to try and automate some steps.

To see what the actual commands I have run recently I created a little PHP script that takes the history command as input and finds the most commonly run commands. As the output of the history command has lines starting with numbers these need to be removed from every line first. After that it is just a case of building up an array of commands as the keys of an indexed array and then keeping count of the number of times that command appears in the history.

  1. $stdin = fopen('php://stdin', 'r');
  2.  
  3. $commands = [];
  4.  
  5.  
  6. while (false !== ($line = fgets($stdin))) {
  7. if (strlen($line) > 300) {
  8. // Prevent long commands from being counted.
  9. continue;
  10. }
  11. $command = preg_replace('/(\d+\*?\s{1,2})/', '', trim($line));
  12. if (!isset($commands[$command])) {
  13. $commands[$command] = 1;
  14. }
  15. else {
  16. $commands[$command]++;
  17. }
  18. }
  19.  
  20. arsort($commands);
  21.  
  22. print_r(array_slice($commands, 0, 25));

To run this I just piped the output of the history command into the script. The use php://stdin he output of running this command is as follows.

  1. $ history | php history-report.php
  2. Array
  3. (
  4. [git status] => 1527
  5. [ll] => 468
  6. [cd ..] => 314
  7. [git log] => 250
  8. [exit] => 158
  9. [vagrant up] => 140
  10. [cd vm] => 137
  11. [git checkout stage] => 136
  12. [vagrant halt] => 125
  13. [git pull origin stage] => 111
  14. [vagrant ssh] => 106
  15. [git checkout master] => 92
  16. [git checkout prod] => 87
  17. [git diff] => 78
  18. [git checkout dev] => 78
  19. [git pull origin prod] => 73
  20. [git pull origin master] => 70
  21. [git checkout integration] => 69
  22. [git merge stage] => 66
  23. [git reset HEAD --hard] => 60
  24. [git pull origin dev] => 57
  25. [git merge dev] => 57
  26. [git checkout preprod] => 55
  27. [git pull origin integration] => 55
  28. [git commit] => 49
  29. )

Looks like I run 'git status' a lot more than other git commands. Looking up the aliases I have installed I realised I could shorten this using the Oh My Zsh alias 'gst', which I now need to remember to use. I'm sure I just type 'git status' using muscle memory and have just gotten used to typing it. Other than git I tend to use vagrant a lot but also see a few file system navigation commands in there.

Digging a little deeper I decided to see what commands I ran in order to give a better picture of the types of things I tend to repeat. This means tweaking the above script a little so that I find out what commands I have run one after the other.

  1. $stdin = fopen('php://stdin', 'r');
  2.  
  3. $commands = [];
  4.  
  5. $adjacentCommands = [];
  6.  
  7. // Extract the commands.
  8. while (false !== ($line = fgets($stdin))) {
  9. if (strlen($line) > 300) {
  10. continue;
  11. }
  12. $command = preg_replace('/(\d+\*?\s{1,2})/', '', trim($line));
  13. $commands[] = $command;
  14. }
  15.  
  16. // Find concurrent commands.
  17. foreach ($commands as $id => $command) {
  18. for ($i = 2; $i <= 5; $i++) {
  19. if (isset($commands[$id - $i])) {
  20. $key = implode(' && ', array_slice($commands, $id - $i, $i));
  21. if (!isset($adjacentCommands[$i][$key])) {
  22. $adjacentCommands[$i][$key] = 1;
  23. }
  24. else {
  25. $adjacentCommands[$i][$key]++;
  26. }
  27. }
  28. }
  29. }
  30.  
  31. // Print report.
  32. foreach ($adjacentCommands as $number => $adjacentCommand) {
  33. echo 'NUMNER:' . $number . PHP_EOL;
  34. arsort($adjacentCommand);
  35. print_r(array_slice($adjacentCommand, 0, 10));
  36. }

After playing with the number of commands run in sequence I found that 5 seems to be a good number, any more than this and it produces a lot of command sequences that I have run only once, which doesn't make a lot of sense to automate. I have used '&&' to string the commands together as this means that command sequence can actually be used as they are if I needed to.

Although there s quite a bit of output here I'll need to remove some of it as there are some command sequences that stray into client work, which I can't repeat here. Here is the output I can show.

  1. $ history | php history-report.php
  2. Number: 2
  3. Array
  4. (
  5. [cd .. && ll] => 107
  6. [git checkout stage && git pull origin stage] => 100
  7. [git status && git log] => 75
  8. [cd vm && vagrant up] => 72
  9. [vagrant halt && cd ..] => 67
  10. [ll && cd ..] => 55
  11. [vagrant up && vagrant ssh] => 54
  12. [git checkout integration && git pull origin integration] => 53
  13. [git status && git diff] => 53
  14. [git reset HEAD --hard && git status] => 50
  15. )
  16.  
  17. Number: 3
  18. Array
  19. (
  20. [ll && cd .. && ll] => 31
  21. [cd vm && vagrant up && vagrant ssh] => 29
  22. [git status && git reset HEAD --hard && git status] => 28
  23. [exit && vagrant halt && cd ..] => 22
  24. [git status && git add composer.json && git add composer.lock] => 19
  25. [cd .. && ll && cd ..] => 19
  26. [vagrant halt && cd .. && ll] => 18
  27. [vagrant up && vagrant ssh && vagrant halt] => 16
  28. [vagrant ssh && vagrant halt && cd ..] => 16
  29. [ll && cd var && ll] => 16
  30. )
  31.  
  32. Number: 4
  33. Array
  34. (
  35. [cd vm && vagrant up && vagrant ssh && vagrant halt] => 10
  36. [vagrant halt && cd .. && ll && cd ..] => 9
  37. [vagrant up && vagrant ssh && vagrant halt && cd ..] => 8
  38. [cd .. && ll && cd .. && ll] => 8
  39. [cd vm && ll && cd var && ll] => 7
  40. [cd vm && vagrant up && vagrant ssh && cd ..] => 7
  41. [git status && git add composer.json && git add composer.lock && git add vendor/] => 6
  42. [git status && git add composer.json && git add composer.lock && git status] => 6
  43. [git checkout prod && git pull origin prod && cd vm && vagrant up] => 6
  44. [ll && cd vm && ll && cd var] => 5
  45. )
  46.  
  47. Number: 5
  48. Array
  49. (
  50. [cd vm && vagrant up && vagrant ssh && vagrant halt && cd ..] => 6
  51. [ll && cd vm && ll && cd var && ll] => 5
  52. [git status && git reset HEAD && git status && git reset HEAD --hard && git status] => 4
  53. [vagrant halt && cd .. && ll && cd .. && ll] => 4
  54. [cd .. && ll && cd .. && cd www && ll] => 3
  55. [exit && vagrant halt && cd .. && ll && cd ..] => 3
  56. [cd someproject && git checkout prod && git pull origin prod && cd vm && vagrant up] => 3
  57. [cd vm && vagrant up && vagrant ssh && git status && cd ..] => 3
  58. )

Clearly, I'm an idiot. The data is right there. How many times have I run 'cd something' followed by 'll'? Well, the number is actually there, so at least 107 times. Using this I can also see that I tend to use 'git status' as a crutch to make sure of the status of my git repo before and after I run some sort of destructive command.

Pessimism aside, I can see some time savings to be made here. I tend to run commands to checkout and then update git branches quite a lot so automating this would be the first step in saving me some typing. Aside from that I could also save some typing by running a directory list after every directory change. As it happens I've already written about listing the directory contents after changing directory in the past, so I will be adding this to my bash profile as soon as I can.

What are your results? How many times have you run the same sequence of commands over and over again? Post a comment below and show me that I'm not alone in my madness!

Add new comment

The content of this field is kept private and will not be shown publicly.