4

Env: Ubuntu 14.04

I have a file called bc on my home folder. The whole content of the file is

func() {
    local a b=()
    echo $0
}
func

Note there's no #!/bin/bash at the beginning.

Then, if I do /bin/bash ~/bc I get

/home/dev/bc

as expected.

However, if I source it instead . ~/bc I get the following error:

bash: /home/dev/bc: line 2: syntax error near unexpected token `('
bash: /home/dev/bc: line 2: `    local a b=()'

I am assuming a different shell is executed for sourcing. If that is the case, then how can I change it?

If I do a chsh I get

dev@c1:~$ sudo chsh
[sudo] password for dev: 
Changing the login shell for root
Enter the new value, or press ENTER for the default
    Login Shell [/bin/bash]: 

------ from comments --------

echo $SHELL returns /bin/bash

  • 1
    What does echo $SHELL say? – dhag May 12 '15 at 19:09
  • Source run command in current shell context. Maybe your shell is not bash, you have changed shell for root, not your shell – cuonglm May 12 '15 at 19:10
  • You don't need to use sudo to run chsh — it (chsh) exists solely for the purpose of letting users change their own login shells, so it wouldn't make sense to require you to be privileged to run it (administrators already have plenty of tools to let them modify users' attributes).  As is plain from the dialog, when you say sudo chsh, it lets you change root's shell (and don't do that unless you're sure you mean to).  Try running just plain chsh.  Also, echo $SHELL is not as reliable as some people believe.  Type foo and see whether it says bash: foo: command not found. – G-Man May 12 '15 at 20:48
  • 1
    The error message is definitely from bash; not only does it says so at the beginning of both lines, but bash is the only shell whose source code contains "syntax error near unexpected token". – Stephen Kitt May 12 '15 at 21:31
  • @G-Man thanks for the chsh explanation. Running just chsh asks for my password and displays the default shell, which is /bin/bash . Typing "foo" gives me a list of suggestions {fio, fop, xoo, goo, ...} – Alexandre Santos May 13 '15 at 15:53
4

The problem is almost definitely an alias. When you do:

/bin/bash somefile

You load a new shell. You get a fresh start. That executable isn't saddled with all of the stuff you put in your initialization files - rcs - because those are only sourced by default for interactive shells, which is not to mention any additional logic accrued since.

Aliases are weird. Aliases are parser expansions - they're the very first kind of expandable object most shells support, and they are expanded in ways many don't expect. bash, in fact, discourages their use - for whatever reason - by disabling alias expansions by default in any non-interactive shell. And so if an alias causes that error - and I'm pretty sure that's the problem - then even sourcing your environment files would likely still not elicit the same error output when you /bin/bash, anyway.

When I say aliases are weird it is because they are expanded on a different level than other types of shell expansions. Probably this is most keenly obvious in a function definition context; wherein no shell expansions occur - as those are reserved for the function's later execution - except aliases without twice-evaluating the expression. And aliases are never expanded during function execution (again, short of eval), because they were already expanded during its definition.

Here's an example:

alias a='echo not a;b'
b(){ echo a\?; }; a(){ echo le sigh...; }
a; b

...prints...

not a
not a
le sigh...
le sigh...

The first time the alias named a is expanded is when I do:

a(){...

This is possible because the () parens are shell tokens - not reserved words like {} - and so they can delimit a word without whitespace - which makes them particularly useful for things like function and array declarations as the parser performs macro expansion to arrive at an executable statement. The result of the above expansion is...

}; echo not a;b(){ 

The last time it is expanded transforms:

a; b

...into...

echo not a;b; b

You see, typical shell expansions occur in a pre-delimited context - they are bounded on all sides by control operators like:

\n[]{}|&();<>\'"`

In this way most shell expansions serve as a quoting mechanism in and of themselves - quotes expanded out of a variable do not recursively delimit further quoted contexts because the expansion quoted them. Alias expansion occurs before this, though - aliases are expanded if recognized by the parser as it scans the first shell word in a simple command - before it scans in the rest of it. So if the expansion results in more than a single simple command, well... then so be it.

My assumption is you have an alias declared for func - that would be the most simple explanation - but because aliases can chain if they're defined with a trailing space - and will also be expanded within a function definition - there's no way of knowing for certain given only the information you provide. I think I can reproduce your error, though. This is close:

alias func='func('
func(){
    local a b=()
    echo $0
}
func

...which prints...

bash: syntax error near unexpected token `('
bash: local: can only be used in a function
bash
bash: syntax error near unexpected token `}'
bash: syntax error near unexpected token `newline'

So just do:

alias func local a b echo

And let us know.

  • You are right. There's an alias to "local". Thank you very much. – Alexandre Santos May 13 '15 at 15:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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