Stanley Shyiko2020-06-02T07:20:52+00:00http://shyiko.comStanley Shyikostanley.shyiko@gmail.comDon't let `cd` slow you down2014-10-10T00:00:00+00:00http://shyiko.com/2014/10/10/commacd<p>I spend quite a bit of time moving around in the terminal. Naturally, tools like <a href="https://github.com/clvv/fasd" target="_blank">fasd<a></a>, <a href="https://github.com/vigneshwaranr/bd" target="_blank">bd</a>, <a href="https://github.com/junegunn/fzf" target="_blank">fzf</a>, <a href="https://github.com/shyiko/dotfiles">etc</a> are at the top of my ‘day-to-day usage list’. Unfortunately, <a hred="http://ohmyz.sh/" target="_blank">zsh</a> is nowhere among them. As much as I would love to use it, the majority of Linux distributions still don’t have it preinstalled. For me it’s a deal breaker. I honestly don’t want to get <a href="https://github.com/Russell91/sshrc">homesick</a> every time I <code class="language-plaintext highlighter-rouge">ssh</code> into some remote machine I have no control over. The obvious shell of choice, given the circumstances, is Bash. Up until recently, there was one thing driving me nuts, though. Namely, absent zsh-style <code class="language-plaintext highlighter-rouge">cd</code>. But, with <a href="https://github.com/shyiko/commacd" target="_blank">commacd</a> in the game, it’s no longer a problem.</a></p>
<p>So, what is <code class="language-plaintext highlighter-rouge">commacd</code> exactly? Well, it’s basically just a bunch of aliases (<code class="language-plaintext highlighter-rouge">,</code>, <code class="language-plaintext highlighter-rouge">,,</code>, and (surprise, surprise!) <code class="language-plaintext highlighter-rouge">,,,</code>), designed to make navigation faster. The best way to explain each one of them is probably to go through some examples.</p>
<p>Let’s start with <code class="language-plaintext highlighter-rouge">,</code>. Think of it as a <code class="language-plaintext highlighter-rouge">cd</code> that matches directories by prefixes:</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh">~<span class="nv">$ </span>, des
<span class="o">=></span> <span class="nb">cd </span>Desktop
<span class="c"># move through multiple directories</span>
~<span class="nv">$ </span>, /u/l/ce
<span class="o">=></span> <span class="nb">cd</span> /usr/local/Cellar
<span class="c"># allow me to choose directory in case of ambiguous pattern (= multiple choices)</span>
~<span class="nv">$ </span>, d
<span class="o">=></span> 1 Desktop
2 Downloads
: <<span class="nb">type </span>index of the directory to <span class="nb">cd </span>into>
<span class="c"># given two directories jdk7 and jdk8 on the Desktop, cd into jdk8 without hitting interactive mode (the one shown above)</span>
~/github<span class="nv">$ </span>, ~/d/j<span class="k">*</span>8
<span class="o">=></span> <span class="nb">cd</span> ~/Desktop/jdk8
<span class="c"># cd into directory having 'esk' somewhere in its name</span>
~/github<span class="nv">$ </span>, ~/<span class="k">*</span>esk
<span class="o">=></span> <span class="nb">cd</span> ~/Desktop</code></pre></figure>
<p>If you often find yourself using <code class="language-plaintext highlighter-rouge">cd ..</code>, <code class="language-plaintext highlighter-rouge">cd ../..</code>, … (in one from or another), you might be interested in <code class="language-plaintext highlighter-rouge">,,</code>. It has a number of different strategies that can make navigation backward a whole lot easier:</p>
<ul>
<li>(no arguments) look for the project (checkout) directory (the one with .git/.hg/.svn in it) and then <code class="language-plaintext highlighter-rouge">cd</code> into it;</li>
<li>(one argument) <code class="language-plaintext highlighter-rouge">cd</code> into the closest parent having its name begin with whatever the value you passed in;</li>
<li>(two arguments) replace all occurrences of the first value with the second one (in the current path).</li>
</ul>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># go all the way up to the project root (in this case, the one that has .git in it)</span>
~/github/lorem/src/public<span class="nv">$ </span>,,
<span class="o">=></span> <span class="nb">cd</span> ~/github/lorem
<span class="c"># cd into to the first parent directory named g*</span>
~/github/vimium/src/public<span class="nv">$ </span>,, g
<span class="o">=></span> <span class="nb">cd</span> ~/github
<span class="c"># substitute jekyll with ghost</span>
~/github/jekyll/test<span class="nv">$ </span>,, jekyll ghost
<span class="o">=></span> <span class="nb">cd</span> ~/github/ghost/test</code></pre></figure>
<p>The last one is <code class="language-plaintext highlighter-rouge">,,,</code> (my favorite). Think of it as <code class="language-plaintext highlighter-rouge">,</code> but from the first (nearest) parent directory having non-empty expansion. It allows quickly jump between multiple directories sharing the same ancestor (not necessarily an immediate one).</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="c"># jump to some other project (in this case, located in ~/github)</span>
~/github/rook/src/public<span class="nv">$ </span>,,, m<span class="k">*</span>binlog
<span class="o">=></span> <span class="nb">cd</span> ~/github/mysql-binlog-connector-java</code></pre></figure>
<p>Note that all three commands automatically emit <code class="language-plaintext highlighter-rouge">pwd</code> before the completion (just like <code class="language-plaintext highlighter-rouge">cd -</code>), which makes it possible to combine them with other tools (without actually changing the working directory):</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span><span class="nb">tail</span> <span class="nt">-f</span> <span class="sb">`</span>, /v/l/t/l<span class="sb">`</span>/catalina.out
<span class="o">=></span> <span class="nb">tail</span> <span class="nt">-f</span> /var/lib/tomcat7/logs/catalina.out
/opt/nginx<span class="nv">$ </span><span class="sb">`</span>,,, ap/b<span class="sb">`</span>/cassandra-cli
<span class="o">=></span> /opt/apache-cassandra-1.2.1/bin/cassandra-cli</code></pre></figure>
<p>You can also hit <code class="language-plaintext highlighter-rouge"><Tab></code> whenever you want to check where are you about to jump to. Like this:</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh"><span class="nv">$ </span>, /v/l<Tab>
<span class="o">=></span> <span class="o">(</span>shows 3 possible completions<span class="o">)</span> /var/lib/ /var/lock/ /var/log/
<span class="nv">$ </span>, /u/l/ce<Tab>
<span class="o">=></span> , /usr/local/Cellar/</code></pre></figure>
<p>To shrink path back use <code class="language-plaintext highlighter-rouge"><Control>-</code>.</p>
<p>And now, the final part - installation. <code class="language-plaintext highlighter-rouge">commacd</code> is written in pure Bash (no dependencies on Python/Ruby/Perl, no nothing). Getting it into your shell is as easy as:</p>
<figure class="highlight"><pre><code class="language-sh" data-lang="sh">curl https://raw.githubusercontent.com/shyiko/commacd/master/commacd.bash <span class="nt">-o</span> ~/.commacd.bash <span class="o">&&</span> <span class="nb">echo</span> <span class="s2">"source ~/.commacd.bash"</span> <span class="o">>></span> ~/.bashrc</code></pre></figure>
<p>After that, open a new terminal session and <code class="language-plaintext highlighter-rouge">,</code> ahead!</p>
<hr />
<p>Want to help out? Report issues, suggest improvements/features or, even better, send in a pull request (see <a href="https://github.com/shyiko/commacd">GitHub page</a>). Every bit is helpful.</p>