Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am trying to create an alias to use in my Cygwin shell to determine if the Mercurial-managed directory I'm currently in has outstanding changes. The command "hg st -m" lists all modified files. Unfortunately it doesn't set an exit value based on whether there are changes or not. So I have tried capturing the output and seeing if it's not empty, piping it through "wc -l" and checking if that's -gt 0, but I seem to have a larger problem in that lines are clearly not executing in the order I would expect them to.

Here is what I've boiled it down to:

alias hgfoo='
pwd
echo L1
localChanges="NOT SET YET"
hasLocal="NOT SET YET"
echo "Before: A${localChanges}Z A${hasLocal}Z"
localChanges=`hg st -m`
echo L2
echo "localChanges: A${localChanges}Z"
echo L3
if [ -n "${localChanges}" ] ; then
    echo L3
    hasLocal="YES"
else
    echo L4
    hasLocal="NO"
fi
echo L5
echo "RESULTS: A${hasLocal}Z"
echo L6
'

I put in the "Ln" echo statements for debugging. When I run it by typing in "hgfoo" in a directory that has changes, I see the following output:

/d/hg/succession > hgfoo
/d/hg/succession
L1
Before: ANOT SET YETZ ANOT SET YETZ
L2
localChanges: AL2
localChanges: ANOT SET YETZ
L3
L3
L5
RESULTS: AYESZ
L6
M succession-lib\.actionScriptProperties
M succession-lib\src\main\flex\com\workscape\SuccessionConstants.as
M succession-lib\src\main\flex\com\workscape\succession\nomination\tabmodules\BenchStrengthTab.mxml
M succession-lib\src\main\flex\com\workscape\succession\nomination\tabmodules\BenchStrengthTabMediator.as
M succession-lib\src\main\flex\com\workscape\succession\puremvc\controller\StartupSuccession.asZ
L3
L3
L5
RESULTS: AYESZ
L6

When I run it in a directory without changes, I see:

/d/hg/employee-profile > hgfoo
/d/hg/employee-profile
L1
Before: ANOT SET YETZ ANOT SET YETZ
L2
localChanges: AL2
localChanges: ANOT SET YETZ
L3
L3
L5
RESULTS: AYESZ
L6Z
L3
L3
L5
RESULTS: AYESZ
L6

Even forgetting about the fact that the comparison is not working right, in both cases it seems to be running lines multiple times and in the wrong place. This is driving me crazy.

Any clues? Thanks.

share|improve this question
    
Does that hg command run in the background? Does it send its output to stderr? –  glenn jackman Mar 2 '11 at 17:40
    
Your output shows localChanges: AL2 not localChanges: ALZ which indicates the alias you're actually running is not what you're showing us. Clean up or refresh the aliases in your shell, run it again and then show us what's happening. –  glenn jackman Mar 2 '11 at 17:42
    
No, the HG command doesn't run in the background, but it would not shock me to learn that cygwin deals with running things in backticks in an unusual way. I may try writing out to a file or using grep, which will set an exit level. Hmmm. –  dj_segfault Mar 2 '11 at 18:34
4  
consider using a function() { ... } instead ... i ve never seen such a monster of an alias –  akira Mar 2 '11 at 19:06
2  
@dj_segfault: gnu.org/software/bash/manual/bashref.html#Aliases "For almost every purpose, shell functions are preferred over aliases." .. function can have arguments, functions are not expanded on read, you wont need such a huge quoting block to hold together the subcommands of your alias etc etc etc. –  akira Mar 2 '11 at 20:50
show 7 more comments

2 Answers 2

up vote 4 down vote accepted

This is a long-standing bug in bash's alias expansion. I could reproduce it on Debian lenny amd64 with bash 3.2.29(1), Debian squeeze i386 with 4.1.5(1), and Windows XP with Cygwin 1.7.7-1 with bash 4.1.9(3).

A few experiments show that the bug is very sensitive to variations in how the alias is used. hgfoo or hgfoo : exhibits the bug, but { hgfoo :; } and (hgfoo) work fine. Using a function instead of an alias also works.

I investigated a bit, trying to extract a smaller example. The weirdness seems to stem from having a newline in the alias. Here is a simple example where bash (3.2.29 or 4.1.9) misbehaves. This alias works as expected (from my reading of Single Unix v3) under pdksh, ksh93, ash and zsh under Debian lenny, namely oops prints foo bar.

$ alias oops='a=$(echo bar)
> echo foo $a'
$ oops
foo foo echo bar

The bug may be related to a reported bug about “strange delayed aliases”, but that discussion focuses on line continuations (backslash-newline) which is not used here.


In any case, use a function! Aliases are for one-liners where you're just specifying some default options for a command. A function would have the additional benefit that you could pass arguments to hg:

hgfoo () {
  localChanges=$(hg st -m "$@")
  …
}

¹ I initially thought this was a Cygwin bug, but as ak2 pointed out, this bug occurs under Linux as well.

share|improve this answer
    
It's not Cygwin specific. I've tried it on a Fedora box and got the same effect. No idea why though, but it must be to do with how bash evaluates aliases. –  ak2 Mar 3 '11 at 21:05
    
@ak2: Indeed this is a long-standing alias expansion bug. (Bug, as in, no other shell behaves this way, I can't reconcile the behavior with POSIX, and I don't even understand what could be going on under the hood.) Thanks for the heads-up. –  Gilles Mar 3 '11 at 21:52
add comment

I got it to work. It was indeed the backticks causing the problems. I refactored it as

hg st -m | grep -q M

then checked the exit value of grep, which is working perfectly.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.