Saurav Saha
Saurav Saha

Reputation: 997

what does this command mean in bash

I have come across a bash script which contains the commands:

cd .git/hooks
[ `eval ls -l | grep -io 'Post-Commit' | wc -l` -eq 1 ] && echo 'post-commit file check... Pass' || echo 'post-commit file check... Fail'

I believe it is to check for a file named post-commit in the .git/hooks folder. But even if I create the file post-commit inside the directory I get the output as: post-commit file check... Fail.

Am I doing something wrong here? I create my file using:

cat > post-commit

and then I write into it and make it into an executable using chmod +x post-commit

Any help will be really appreciated

Upvotes: 0

Views: 1031

Answers (1)

torek
torek

Reputation: 487755

Although this script seems to be aimed at Git hooks, it has no dependency on Git in any way, so I dropped the and tags. It also has nothing to do with Linux so I dropped the tag.

The eval here is entirely unnecessary.1 We can drop it:

[ `ls -l | grep -io 'Post-Commit' | wc -l` -eq 1 ] &&
  echo 'post-commit file check... Pass' ||
  echo 'post-commit file check... Fail'

(I broke this into more lines for readability).

As Losko notes in a comment, [ is nominally an external command. In practice it's actually built into most shells, including bash, so that there's no fork-and-exec.

The backquoted section, however, does require a fork-and-exec. Text of the form `command` is more or less equivalent to code of the form $(command). The $(...) form is generally superior because it provides sane nesting, so if there's a choice of which to use, one should usually prefer the $(...) form. In any case, it just means: run the command shown and insert its output here. So we're going to run:

ls -l | grep -io 'Post-Commit' | wc -l

as metallic notes in another comment.

The ls -l does the obvious: a long listing.

The grep -io has two flags: -i, meaning ignore case, and -o, meaning show only the matched part as output. So this is going to look for ls -l output in which the line contains POST-COMMIT, post-commit, pOSt-COMmit, and so on.

The wc -l does the obvious: count lines.

The end result is that we count the number of files that have this string in their file name. A file named this-GETS-RUN-post-COMMIThaste-er for instance gets counted.

The final result of this count gets compared for numeric equality to 1. If the number is one, the && part of the clause is run; if not, the || part is run.

Overall, this script fails to do anything useful, because it's too sloppy. It allows mixed-case file names; Git does not. (However, on file systems where the OS allows case-insensitive matching of file names, Git's use of strictly lowercase results in accessing mixed-case or uppercase names after all.) If we find two or more files that match, it complains, yet files whose names simply have a sub-match are probably not an issue here.


1An eval in a shell command makes the shell do various expansions first, then run the command. Where it's useful—e.g., eval $cmd—it's also generally pretty dangerous: you must exercise extremely strict control over what goes in $cmd. Where there are no expansions, as in this case, it's useless. Avoid it as much as possible.

Upvotes: 1

Related Questions