0

This is probably a simple one for a bash scripter, which I am not.

I'm running a cron job that downloads some data, and then depending on that data, may or may not modify a second file. After the job, I want to git commit one or both files. For the conditional commit, I tried this in a .sh script:

# attempt to capture whether MyNotes.txt was changed
# by counting lines in git status output
mywc=(git status -s MyNotes.txt | wc -l)
echo $mywc found!
if [ $mywc = 1 ]; then
   echo Add file for commit
else
   echo Nothing to add
fi

I'm pretty much getting nowhere; this thing seems to fail on the first line with syntax error near unexpected token '|'. If I run git status -s MyNotes.txt | wc -l on the command line, I get the numeric output I expect.

  1. What am I doing wrong and how can I make this work?
  2. If there's a more elegant way to determine whether a file changed, feel free to share.

Also, for my edification, how could I get this to work without the interim mywc variable? I.e., if I wanted to just do the command within the if, something like this:

if [[ $(git status -s MyNotes.txt | wc -l) = 1 ]]; then
...

Thanks!

Marc
  • 11,403
  • 2
  • 35
  • 45
  • 1
    `mywc=$(git status -s MyNotes.txt | wc -l)` (put $ before open paren) ? – CryptoFool Oct 10 '22 at 01:48
  • Ugh... I literally just tried that before I saw your comment, and yes, that seems to work. I've been banging my head against the wall on this, and figured it was something dumb I was doing. Thank you, @CryptoFool. But I think my `IF` statement is still failing. I'm looking into that now. – Marc Oct 10 '22 at 01:49
  • Note that your script so far does not actually commit anything. In general the phrase "commit a file" might not be what you mean; every commit contains _all_ your files. – matt Oct 10 '22 at 02:21
  • @matt, I updated the question title to what I think is more correct verbiage. – Marc Oct 10 '22 at 02:26
  • 1
    Since `git add .` would embrace this file if it has indeed changed, why not just say that? What's the need for the conditional? – matt Oct 10 '22 at 02:27
  • You're right. No need for conditional after all. – Marc Oct 10 '22 at 02:29
  • 1
    Since the (correct) answer did not explain, **why** you got a syntax error: If you write `a=(foo bar baz)`, you are storing into `a` a 3-element array with these values. The symbol `|` denotes a pipe for bash, and a pipe does not make sense inside an array denotation. Hence the syntax error. To store a pipe symbol into an array (in case you need this one day), you would have to quote it: `a=(foo bar '|' baz)`. – user1934428 Oct 10 '22 at 05:36

3 Answers3

3

What am I doing wrong and how can I make this work?

put a dollar before parenthesis.

foo=$(command)

The thing you are using looks like a bash array

declare -a letters=(a b c d)

If there's a more elegant way to determine whether a file changed, feel free to share.

Consider this:

$ git diff -s --exit-code README.md || echo has changed
has changed
$ git checkout  README.md
Updated 1 path from the index
$ git diff -s --exit-code README.md || echo has changed

The OR (||) runs if the first command exits with a non-zero code. Same thing essentially:

$ false || echo false exits with 1 
false exits with 1
$ true  || echo will not trigger

An aspect of bash that people overlook is that [[, ]], [ and ] are separate commands. They have return codes too. With this knowledge, you can leverage the return codes with if and any other command.

$ if true; then echo yes; else echo no; fi
yes
$ if false; then echo yes; else echo no; fi
no

So for detecting changes in a tracked file:

$ if git diff -s --exit-code README.md; then echo same as in index; else echo changed; fi 
same as in index

$ echo 123 >> README.md 
$ if git diff -s --exit-code README.md; then echo same as in index; else echo changed; fi 
changed

With all of that said...

Just add the file. You don't need to check anything. If it hasn't changed, nothing will happen.

$ echo foo >> myfile
$ git add myfile
$ git commit -m 'maybe changed' myfile
[master b561cc1] maybe changed
1 file changed, 1 insertion(+), 1 deletion(-)

$ git add myfile
$ git commit -m 'maybe changed' myfile
no changes added to commit (use "git add" and/or "git commit -a")

if you need to avoid a non-zero exit code (such as with set -e), just put a || true after the command that you want to ignore the exit status of:

$ cat foo.sh

  #!/bin/basho
  
  set -e
  
  echo foo >> myfile
  git add myfile
  git commit -m 'maybe changed' myfile
  
  git add myfile
  git commit -m 'maybe changed' myfile > /dev/null || true
  
  echo no error here. it\'s fine..
  
  false
  
  echo fill never reach this.

Try running that script and see what happens

1

I search for a way for checking if file changed.

git diff --exit-code -s <path>

Now the bash scripter knows that every command returns a status code which can be checked with $?. In case everything went smoothly, 0 is returned. In that case we get 0 if file is not changed.

Every bash scripter knows too that you can use that with && and || operators (because of lazy evaluation) to write such construct:

git diff --exit-code -s <path> && echo "should add file"

About your edification, what you wrote is perfectly fine!

kosciej16
  • 6,294
  • 1
  • 18
  • 29
1

As CryptoFool pointed out in a comment, I failed to include a $ in my variable assignment. Simple fix in the first line of my script:

mywc=$(git status -s MyNotes.txt | wc -l)

As matt pointed out in a subsequent comment, doing a git add on a file that hasn't changed has no effect. It won't stage the file for commit. So instead of doing conditional logic to determine whether to git add myfile.txt, I'll just blindly execute git add myfile.txt, which will either stage the file if there are changes, or do nothing if there are no changes. Therefore, my entire script can be replaced with one line:

git add MyNotes.txt
Marc
  • 11,403
  • 2
  • 35
  • 45