Tag Archives: bash

Two New Commands

I enjoy learning new things. When I do, I’ll try to write them down in case others may perhaps also find them useful. If nothing else, the notes may help me when I’m wondering, “How did this get solved last time?”

My team at work is in the process of switch from Subversion to Git and GitHub. Git is new ground for many of us, as is the bash prompt (On Windows, Git includes a full-fledged bash prompt). Doing some troubleshooting today, I learned some new new things about both tools.

Pretty git logs

The first was a new Git command:
 git log --graph --oneline

This creates a wonderful ASCII-art timeline, showing all your commits and showing how files have been merged into the project.

For a project of any complexity, this simple visualization is wonderful for understanding the flow of changes and merges by multiple developers. Check out this snippet from PhantomCSS for example:

[email protected] MINGW64 ~/test
$ git log --graph --oneline
* 20db940 0.10.4
* a482cc3 Merge pull request #127 from raveclassic/master
|\
| * bbbaa22 fixed TypeError when calling phantomcss.compareAll() #68
* | e44196e Merge pull request #125 from shadowfiles/extension-settings
|\ \
| * | 28a397f Added option to specify the suffix names of screenshot types.
| |/
* | d8479cf Merge pull request #124 from renanborgez/patch-1
|\ \
| |/
|/|
| * 4b0913f Update README.md
|/
* 84945e5 0.10.2

 

Commit 4b0913f (near the bottom) updates the README.md and is then meged back in commit d8479cf.  Meanwhile, a separate developer is adding new functionality in commit 28a397f, merges two pull requests in commits e44196e and bbbaa22 before everything comes back together in a482cc3

It’s a bit cryptic at first, but much easier to fathom than the straight up git log.

The same format also appears, in a non-text based format, in the gitk tool.

A new (to me) bash command

The other new command was from bash. I’ve known for a while that your command line history is stored in ~/.bash_history, but I didn’t know what it was good for aside from presumably powering the up-arrow / down arrow line-at-a-time history.

Turns out that the history command also displays your command line history, and it’s waaaaay more convenient than typing
more < ~/.bash_history
As a bonus, if you have multiple terminals open, this command is guaranteed to show the history for the current window rather than the one most recently closed.

Problem: chmod is ignored in the Git Bash prompt

So here’s a strange one that had me baffled for a bit – the chmod command is pretty much a null operation from the Git Bash prompt (MingW64). This initially showed up on a script for launching a Docker container, but as nearly as I can tell, it happens for any shell script.

So, we have a simple script that prints out “Hello World!”.

[email protected] MINGW64 ~/test
$ cat foo
echo Hello World!

Simple enough. Now the thing is, I want to make this script executable. Now this particular Bash implementation will let me run ./foo and it’ll execute, but my real use case (running a Docker container) is going to have a somewhat longer name. Just as a matter of convenience, I’d like to to type just the first few characters, press tab, and have the filename expanded. And besides, your executable files should always be marked as executable.

[email protected] MINGW64 ~/test
$ ls -l
total 2
-rwxr-xr-x 1 blair 197121 28 Oct 18 00:20 bar*
-rw-r--r-- 1 blair 197121 18 Oct 18 00:10 foo

[email protected] MINGW64 ~/test
$

OK, this is an easy fix, I just need to run chmod and set the execute bit to on, right?

[email protected] MINGW64 ~/test
$ ls -l
total 2
-rwxr-xr-x 1 blair 197121 28 Oct 18 00:20 bar*
-rw-r--r-- 1 blair 197121 18 Oct 18 00:10 foo

[email protected] MINGW64 ~/test
$ chmod 744 foo
[email protected] MINGW64 ~/test
$ ls -l
total 2
-rwxr-xr-x 1 blair 197121 28 Oct 18 00:20 bar*
-rw-r--r-- 1 blair 197121 18 Oct 18 00:10 foo

The execute bit didn’t change. Maybe I need to use the u+x syntax instead?

$ chmod u+x foo
[email protected] MINGW64 ~/test
$ ls -l
total 2
-rwxr-xr-x 1 blair 197121 28 Oct 18 00:20 bar*
-rw-r--r-- 1 blair 197121 18 Oct 18 00:10 foo

Still no luck. So why is bar marked as executable? What’s the difference between these two scripts? The answer turns out to be one line of code:

[email protected] MINGW64 ~/test
$ chmod u+x foo
[email protected] MINGW64 ~/test
$ cat bar
#!/bin/sh
echo Hello World!

Do you see that first line, where it says “#!/bin/sh”. That’s how Bash knows what interpreter to pass the script to. It also turns out, in this particular implementation, that’s how Bash knows the file contains an executable script instead of just text.

So we modify foo, and get this result:

[email protected] MINGW64 ~/test$ cat foo
#!/bin/sh
echo Hello World!
[email protected] MINGW64 ~/test
$ ls -l
total 2
-rwxr-xr-x 1 blair 197121 28 Oct 18 00:20 bar*
-rwxr--r-- 1 blair 197121 18 Oct 18 00:10 foo*

(Image credit: Screenshot by ThatBlairGuy)