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!”.
blair@Squawk 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.
blair@Squawk 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 blair@Squawk MINGW64 ~/test $
OK, this is an easy fix, I just need to run chmod and set the execute bit to on, right?
blair@Squawk 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 blair@Squawk MINGW64 ~/test $ chmod 744 foo blair@Squawk 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 blair@Squawk 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:
blair@Squawk MINGW64 ~/test $ chmod u+x foo blair@Squawk 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:
blair@Squawk MINGW64 ~/test$ cat foo #!/bin/sh echo Hello World! blair@Squawk 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)