Alias in bash_profile executes by itself - bash

I have set up an alias in ~/.bash_profile as follows:
alias lcmt="git show $(git log --oneline | awk '{print $1;}' | head -n 1)"
However, whenever I open a terminal window, I see:
fatal: Not a git repository (or any of the parent directories): .git
I have been able to narrow it down to that particular alias because when I comment it out, there's no error message. Why does it evaluate by itself on OS X? Can I prevent it from doing so?

The $(...) inside a double-quoted expression gets executed at the time of the assignment, the creation of the alias. You can avoid that by escaping the $ of the $(...). And you want to do the same thing for the $1 inside the awk command:
alias lcmt="git show \$(git log --oneline | awk '{print \$1}' | head -n 1)"

Shell functions are better than aliases in a number of ways, including that there's no quoting weirdness like there is with aliases. Defining a shell function to do this is easy:
lcmd() { git show $(git log --oneline | awk '{print $1;}' | head -n 1); }
I'd make two other recommendations, though: put double-quotes around the $( ) expression, and have awk take care of stopping after the first line:
lcmd() { git show "$(git log --oneline | awk '{print $1; exit}')"; }

Related

How to figure out, why is my shell crash?

When I enter this command:
$ grep -n 'some search' $file | awk '{print 1}' | sed 's/://' | xargs -I{} vim +"{}" $file
It will open, but after quitting vim, the shell crash. It does not react to any input neither for Ctr-C. I have no idea why, how to find out? I suspect there is some infinite loop, because after reboot, there is a lot clearing in terminal. But really have no clue of the reason.
PS:
alias grep: alias grep='grep --color=auto -P'
alias sed: alias sed='sed -E'
No more aliases.
vi changes the terminal settings.
When you onlu want to go to the first match, you can use the linenumber with
vi +$(grep -n 'some search' .bashrc | cut -d: -f1 | head -1) .bashrc
This is still to complicated, you can jump to the match with
vi '/+some search/' "$file"
When you want to go to the second match, just use n.

sed: Argument list too long when running sed -n

I am running this command from Why is my git repository so big? on a very big git repository as https://github.com/python/cpython
git rev-list --all --objects | sed -n $(git rev-list --objects --all | cut -f1 -d' ' | git cat-file --batch-check | grep blob | sort -n -k 3 | tail -n800 | while read hash type size; do size_in_kibibytes=$(echo $size | awk '{ foo = $1 / 1024 ; print foo "KiB" }'); echo -n "-e s/$hash/$size_in_kibibytes/p "; done) | sort -n -k1;
It works fine if I replace tail -n800 by tail -n40:
1160.94KiB Lib/ensurepip/_bundled/pip-8.0.2-py2.py3-none-any.whl
1169.59KiB Lib/ensurepip/_bundled/pip-8.1.1-py2.py3-none-any.whl
1170.86KiB Lib/ensurepip/_bundled/pip-8.1.2-py2.py3-none-any.whl
1225.24KiB Lib/ensurepip/_bundled/pip-9.0.0-py2.py3-none-any.whl
...
I found this question Bash : sed -n arguments saying I could use awk instead of sed.
Do you know how do fix this sed: Argument list too long when tail is -n800 instead of -n40?
It seems you have used this anwer in the linked question: Some scripts I use:.... There is a telling comment in that answer:
This function is great, but it's unimaginably slow. It can't even finish on my computer if I remove the 40 line limit. FYI, I just added an answer with a more efficient version of this function. Check it out if you want to use this logic on a big repository, or if you want to see the sizes summed per file or per folder. – piojo Jul 28 '17 at 7:59
And luckily piojo has written another answer addressing this. Just use his code.
As an alternative, check if git sizer would work on your repository: that would help isolating what takes place in your repository.
If not, you have other commands in "How to find/identify large commits in git history?", which do loop around each objects and avoid the sed -nxx part
The alternative would be to redirect your result/command to a file, then sed on that file, as in here.

Bash alias with an argument that works with pipe and globs

I have a convenience function in my bashrc file that looks like following:
function get_pattern()
{
grep "P1" $1 | grep -v "BUT_NOT_THIS" | awk -F":" '{print $(1)}' | sort -u
}
alias gp='get_pattern'
This works fine if I run it on individual file like gp file_1.c. However I am unable to run it like find . -name "*.c" -type f | xargs gp. I also fail to run it like gp *.c. How do I code get_pattern so that I can have all these conveniences.
NOTE: I have simplified the function for easier understanding. Not expecting smart grep/awk/sed/sort hacks or tweaks. The question is I have an alias that takes filenames as arguments. Want it to work with pipes, and preferably with globs.
As pointed out by the experts, aliases are not suited for your requirement.
In your function, you are only passing the first argument to grep, as in grep "P1" $1. Change it to use all arguments, this way:
function get_pattern() {
grep "P1" "$#" | grep -v "BUT_NOT_THIS" | awk -F":" '{print $(1)}' | sort -u
}
Note:
When you invoke your function as get_pattern *.c and there are matching files, the function doesn't see *.c, it sees the list of matching files. The glob expansion is done by the shell while invoking the function, but not inside the function.
In the present format, the function doesn't read from stdin. So, piping the results of another command into your function may not work. To make it accept stdin, you need to change the first grep to:
grep "P1" - "$#"
That would mess up the invocation when you intend the function to only read the files. So, it would be better to rewrite the function this way:
function get_pattern() {
if (($# > 0)); then
# arguments passed, use them as file names to grep from
grep_args=("$#")
else
# no arguments passed, grep from stdin
grep_args=(-)
fi
grep "P1" "${grep_args[#]}" | grep -v "BUT_NOT_THIS" | awk -F":" '{print $(1)}' | sort -u
}
I'm just going to focus on the issue of piping data to xargs. That doesn't work because xargs doesn't know anything about the alias. However you can effectively pass the function definition in to make it work (non-portable solution which works in bash and possibly some other shells, but don't expect it to work everywhere) with export -f:
$ foo() { echo foo: $#; }
$ echo bar baz | xargs bash -c 'foo $#' _
_: foo: command not found
$ export -f foo
$ echo bar baz | xargs bash -c 'foo $#' _
foo: bar baz
I am not aware of any way to do this with an alias, but I'm also not aware of any reason to ever use an alias. Stop using aliases.

How to use awk inside git --msg-filter

i am trying to clean up some git history. For instance, trim all lines in my commits messages. I need to be able to do something like:
git filter-branch -f --msg-filter 'cat | awk '{$1=$1;print}'' HEAD
this, of course, will fail because of my bad usage of Apostrophes.
It does not work either if I try to escape then or use double apostrophes.
As an example of what I need to process take this:
Add cool service to application
Related: ISSUE-3
This is the result of appending related issue identifier at end of commit and remove it from my summary line, note the space(s) at beggining of commit summary. It is mostly those commit summary what i want to trim with awk.
Can anybody help me with my limited bash skills?
Thanks in advance
In the following command :
git filter-branch -f --msg-filter 'cat | awk '{$1=$1;print}'' HEAD
the expression between the innermost single quotes is not escaped and $1 is replaced by value
git filter-branch -f --msg-filter 'cat | awk '\''{$1=$1;print}'\' HEAD
may be valid. Try also to add echo command at the beginning of the line
echo git filter-branch -f --msg-filter 'cat | awk '{$1=$1;print}'' HEAD
echo git filter-branch -f --msg-filter 'cat | awk '\''{$1=$1;print}'\' HEAD
or clearer adding printf "'%s'\n"
printf "'%s'\n" git filter-branch -f --msg-filter 'cat | awk '\''{$1=$1;print}'\' HEAD

Prepending branch name to git commit

I've been reading and trying to figure out how to get this to work. I want to prepend the branch name to the commit message so I can just use git commit -m "message" and get a commit named branch message. The closest I got was to use the following code in .git/hooks/commit-msg but I get sed: 1: ".git/COMMIT_EDITMSG": invalid command code . using OSX 10.8.5.
I read it has something to do with OSX sed having different behaviours but I can't find a solution that will work. I probably just don't know enough about OSX/Linux.
ticket=$(git symbolic-ref HEAD | awk -F'/' '{print $3}')
if [ -n "$ticket" ]; then
sed -i "1i $ticket " $1
fi
Yea, OS/X is different. I tested this and it works ok, but maybe has some additional minor tweaks for you to deal with. Note that the -i flag on OS X requires a filename extension to save the backup file under, and to avoid sed insisting that the text used to add with 1i must be escaped with \ followed by another line, I used 1s instead.
ticket=$(git symbolic-ref HEAD | awk -F'/' '{print $3}')
if [ -n "$ticket" ]; then
sed -i '.bak' "1s/^/$ticket /" $1
fi

Resources