How to print what a bash script is actually running? - bash

I am trying to make a bash script which will:
Add all changes to git
Commit with a message I pass to the bash script
Push it to the repo
I am trying to do this with:
m=\"$*\"
git add -A
echo git commit -m $m
git push
However, I am getting errors saying error: pathspec 'Q2,' did not match any file(s) known to git. for everything word I pass to the script.
How can I see what bash it actually doing? When I put echo in front of the offending line (which I presume is the commit) I get a correctly form command. If I put it in to the terminal, I runs find.

To see what bash is doing, add -xv options to the shebang line:
#!/bin/bash -xv
The problem is probably the quoting. m=\"$*\" does not do what you want. $m is still split into several words if it contains whitespace, just the first word starts with a doublequote and the last word ends in a doublequote.
Rather, change the offending line to
git commit -m "$m"

Related

Unexpected question mark in Windows/bash

I'm trying to automatically "archive" some local branches on Windows using the MinGW Bash on Windows.
I copy and pasted the content of git branch on a txt file (branches_to_be_archived.txt) and then I wrote:
A script to archive a single branch
#!/bin/bash
BRANCH_NAME=$1
echo "Branch name is $BRANCH_NAME"
git tag archive/$BRANCH_NAME $BRANCH_NAME
git branch -D $BRANCH_NAME
A script to archive every branch from a file
#!/bin/bash
FILE=./branches_to_be_archived.txt
LINES=$(cat $FILE)
for LINE in $LINES
do
./archive_branch.sh.txt $LINE
done
I inserted some echo prints and they are ok but executing git commands will raise an error like this:
fatal: Failed to resolve '[actual name of the branch]?' as a valid ref.
With a trailing ? that is not shown anywhere.
Any way to avoid having this question mark at the end?
Indeed, it was a endline characters issue (Win CRLF and Linux LF).
I solved by using Notepad++ and setting EOL -> Linux (LF) but I think that dos2unix for branches list file works just fine

Basename returns file name with additional "\r\r\r" in the end?

I'm trying to write simple shell script that clones a github repo then CDs into the folder. I have
#!/bin/bash
# $1 is github repo url
url=$1
reponame=$(basename $url)
git clone $url
echo $reponame
cd $reponame
Now when I run
bash deploy_app.sh https://github.com/username/reponame
It echoes the right $reponame but the next line returns this "cd: $'reponame\r\r\r\r': No such file or directory"
I have no idea where the "\r\r\r\r" part is coming from. It's not in the repo name and it's not part of $reponame when I just echo it. Any idea why this happens??
The first thing I would be looking at is your script itself, by doing something like:
od -xcb deploy_app.sh
and looking for any carriage return characters (CR, ^M, \r, hex 0d, or octal 015) at the end of the cd line (or any line really).
That's because, if you have CR characters at the end, it will cause that problem (the ^M characters below were entered with CTRL-VCTRL-M):
pax:~> cd blah^M^M^M^M
-bash: cd: $'blah\r\r\r\r': No such file or directory
If there are CR characters in your file, you probably want to remove them, and check your line ending settings for git (assuming that file is in a repo) - this is a problem commonly caused by incorrect settings if you're updating your repo from both Windows and Unix.
If that file is not being "damaged" by git, you'll need to discover what else is putting those characters in.
And, as an aside, you wouldn't actually see whether the reponame had those characters with an echo, since echoing them would result in the actual characters, four "go to the start of the line" characters, then a "go to the next line" character. Hence there's no way to tell the difference just by looking, without pushing the output through a command like od.
I am not sure either, but try changing $reponame to "${reponame}" (with the double quotes) and see if it still happens.

git commit error: pathspec 'xxx' did not match any file(s) known to git

I wrote a bash script to update my files through git automatically, but I keep getting error for the git commit line, if my message contains error.
I have already wrapped my message with quote, what's wrong here?
My bash script:
##Handle local git update
remote='
cd express-demo-nonbare;
git pull origin master;
';
echo "Process for updating Git begin";
git add . ;
read -p "Message for this commit: " comment;
comment=\"${comment}\";
echo $comment;
git commit --message=$comment;
git push backup master;
Really you just need to quote the comment variable when supplying it to git commit. You can replace:
read -p "Message for this commit: " comment;
comment=\"${comment}\";
echo $comment;
git commit --message=$comment;
with
read -p "Message for this commit: " comment;
echo $comment;
git commit --message="$comment";
The quotes will not be part of the commit message, rather they are consumed by bash in ensuring that the entire content of the comment variable is submitted as part of a single --message=... argument to git, even if it contains whitespace characters.
You don't want to, as you put it:
wrap... my message with quote
(which is indeed what you are doing). Instead, you want to protect your message from having bash treat it as a list of words. To do that, you need quotes, but in a different position:
remote='
cd express-demo-nonbare;
git pull origin master;
'
echo "Process for updating Git begin"
git add .
read -p "Message for this commit: " comment
echo "$comment"
git commit --message="$comment"
git push backup master
I removed all non-essential semicolons as well (bash treats the end of a line as the end of the command, unless something like an unclosed parenthesis or brace prevents this).
It's not clear to me what your intent is in setting the variable remote to the literal string newlinecdspaceex...;newline, especially since $remote does not occur later in the script. Note, however, that since this string does include white-space, expanding it outside quotes, as in $remote (vs "$remote" which expands it inside quotes) can trigger further shell actions. For instance:
foo='this; that'
wc $foo
will have the wc program attempt to open files named this; and that, having split $foo at the white spaces into separate words that are then passed to wc. This splitting is actually based on $IFS:
IFS=+
foo='this+that'
wc $foo
tries to open files named this and that. Restoring IFS to its normal setting:
wc $foo
tries to open one file named this+that.
(I sometimes use wc as a program to help show what the actual arguments were, since it tries to open each one as a file name, and spits out the actual file name in any subsequent error message or count.)
Similarly, if the expansion of a variable produces shell glob metacharacters, these will be evaluated after the expansion:
foo='*'
wc $foo
will try to open and read every file and directory in the current directory. Again, double quotes will protect against this:
wc "$foo"
will only try to open and read one file, named *.

"git push origin master" gives weird behavior in bash script

I have this simple function in my bashrc file
function aupgrade {
cat ~/.bash_aliases > ~/bash/.bash_aliases
cd ~/bash
git add .
if [[ $1 == "" ]]; then
git commit -m "Update"
else
git commit -m "$1"
fi
git push origin master
cd - 1>/dev/null
}
This function has a purpose, this is the expected behavior:
First, replace the content of the .bash_aliases file in the bash repository with the stdout of cat ~/.bash_aliases
Second, go into the ~/bash directory which is a git respository
Third, stage all changes
Fourth, if when calling the aupgrade function the following argument is nothing, just commit with the "Update" message, but if the user wrote an argument, like aupgrade "New commit!", commit the changes with such argument as the message, git commit -m $1
Fifth, push the changes
Sixth, go back to the previous directory
BUT, it doesnt do that, instead it just does:
First, replace the content of the .bash_aliases file in the bash repository with the stdout of cat ~/.bash_aliases
Second, go into the ~/bash directory which is a git respository
Third, stage all changes
Fourth, commit with the "Update" message although there is an argument
Fifth, push the changes
Sixth, go back to the previous directory
This is weird. This looks product of the git push origin master line. It's not a conditional problem, because when I wrote another function like this but without the git push origin master line it worked as desired.
Why does this happen? Is there any solution?
This is what set -x shows to me, it's really weird
So I have tested your code, and the only problem I could find is that passing your first argument as a string with multiple exclamation marks inserts the same (or previous?) command unless you escape it:
It looks like this is exactly what happened for you, but you had "set -x" ran beforehand, thus it replaced the double exclamation marks with that command.
I'd also recommend storing function arguments in named local variables for cleaner code.

Shell script: escaping list of parameters including hash symbol (#)

I wrote a custom shell script for myself to make it easier for me to commit code to Github.
Recently, I wanted to start using Github's ability to automatically close issues by including their number in the commit message:
# Would automatically close #1 on push
git add .
git commit -m "Closes issue #1 ..."
git push
However, the way my script is set up, it grabs all the parameters using $* but that automatically removes anything after the # symbol, because that's a comment in shell scripts.
commit() {
# Print out commands for user to see
echo "=> git add ."
echo "=> git commit -m '$*'"
echo "=> git push --set-upstream origin $current_branch"
# Actually execute commands
git add .
git commit -m "$*"
git push --set-upstream origin $current_branch
}
Now I CAN do commit 'Closes issue #1 ...' with the wrapping quotes, but that's slightly annoying...I specifically setup my script so I could easily write: commit Whatever message I want to put in here...
I've looked over the man pages and done some SO searching, but I can't find anything about the specific issue of escaping # symbols as a parameter.
Is this even possible?
Anything after the # is interpreted by the shell as a comment, so it will not passed to the function. This happens before the function is executed. There is nothing the function can do to prevent this.
There are two canonical ways of doing this:
Just require quoting. Every single Unix tool that accepts command line parameters requires this.
Have the program read the commit from stdin instead with read -r input. You can then run just commit and type in the message.
These are both good solutions because they're simple, familiar, transparent, robust, idiomatic Unix that is straight forward to reason about. It's always better to work with Unix than against it.
However, if you instead prefer a complex, unfamiliar, opaque, fragile special case, you can kludge it with magic aliases and history:
commit() {
echo "You wrote: $(HISTTIMEFORMAT= history 1 | cut -d ' ' -f 2-)"
}
alias commit="commit # "
Here's an example of this:
$ commit This is text with #comments and mismatched 'quotes and * and $(expansions)
You wrote: commit This is text with #comments and mismatched 'quotes and * and $(expansions)
just played a little with it
the script
commit() {
echo "$*"
}
script's usage and output
➜ ~ commit "whatever you want #1 for some reason"
whatever you want #1 for some reason
➜ ~ commit 'whatever you want #1 for some reason'
whatever you want #1 for some reason
➜ ~ commit whatever you want \#1 for some reason
whatever you want #1 for some reason
➜ ~ commit whatever you want #1 for some reason
whatever you want
➜ ~
so if you don't want to quote the message you need to escape the hash with a \ (backslash), that is actually a regular escape character

Resources