342

In my ~/.bashrc file reside two definitions:

  1. commandA, which is an alias to a longer path
  2. commandB, which is an alias to a Bash script

I want to process the same file with these two commands, so I wrote the following Bash script:


#!/bin/bash

for file in "$@"
    do
    commandA $file
    commandB $file
done

Even after logging out of my session and logging back in, Bash prompts me with command not found errors for both commands when I run this script.

What am I doing wrong?

3
  • 13
    BTW, there's no need to log in and out to have an alias recognized. You need just do source ~/.bashrc.
    – tshepang
    Commented Nov 25, 2010 at 19:10
  • For my case I was connected by SSH agent remotely, after adding alias as I closed the SSH agent and connected again it started working.
    – dav
    Commented Sep 6, 2015 at 5:05
  • An alias is a way of shortening a command. (They are only used in interactive shells and not in scripts — this is one of the very few differences between a script and an interactive shell.) Commented Sep 12, 2019 at 10:18

5 Answers 5

283

If you look into the bash manpage you find:

Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt (see the description of shopt under SHELL BUILTIN COMMANDS below).

So put a

shopt -s expand_aliases

in your script.

Make sure to source your aliases file after setting this in your script.

shopt -s expand_aliases
source ~/.bash_aliases
11
  • 19
    I placed it in my script, but it's still not working. Same error.
    – Zaid
    Commented Sep 2, 2010 at 10:29
  • 8
    Adding shopt -s expand_aliases source ~/.bash_aliases works perfectly for me. Often there is a form of interactive shell detection in .bashrc like this: # If not running interactively, don't do anything [ -z "$PS1" ] && return @Zaid, Maybe you want to check for that in the file you sourced. Commented Apr 4, 2013 at 1:46
  • 3
    Curiously, shopt -s expand_aliases doesn't have to go before the alias definition but before the alias use. Adding to @FrankSchubert: Interactive shell detection may also be done using $- which contains the options for the shell, specifically i if the shell is interactive.
    – valid
    Commented Mar 20, 2015 at 14:12
  • 7
    this isn't a correct answer... Sourcing your aliases inside your script isn't the answer. Your ~/.bash_aliases might depend on other stuff previously loaded on an interactive shell... The closest thing I found is changing your hashbang to #!/bin/bash -li Still not perfect. Ideally you should use functions and not aliases. Commented Feb 5, 2016 at 11:47
  • 2
    Alas, this doesn't work with bash -c, for example bash -c "shopt -s expand_aliases; source my-script.sh; some-alias. Still get some-alias: command not found
    – Hubro
    Commented Feb 17, 2022 at 23:00
164

First of all, as ddeimeke said, aliases by default are not expanded in non-interactive shells.

Second, .bashrc is not read by non-interactive shells unless you set the BASH_ENV environment variable.

But most importantly: don't do that! Please? One day you will move that script somewhere where the necessary aliases are not set and it will break again.

Instead set and use variables as shortcuts in your script:

#!/bin/bash

CMDA=/path/to/gizmo
CMDB=/path/to/huzzah.sh

for file in "$@"
do
    $CMDA "$file"
    $CMDB "$file"
done
11
  • 7
    That solution doesn’t work for the usual alias use cases. E.g. alias mv="mv -v --backup=numbered".
    – anon
    Commented Apr 23, 2016 at 20:11
  • @Evi1M4chine: Yes, it does. At least after I reverted Gilles unnecessary edit. But it might be better to use a different variable for the parameters, anyway.
    – user601
    Commented Apr 24, 2016 at 15:50
  • 1
    Ah, note the lack of quotes around $CMDA / $CMDB… Apart from uppercase variables being reserved for bash itself in bash, and it indeed working, that lack of quotes makes me really uneasy… Thanks anyway.
    – anon
    Commented Apr 30, 2016 at 0:26
  • @Evi1M4chine: Uh, what? 1. I removed the quotes myself in the most recent edit. 2. where do you get the "reserved for bash itself" from? this would be the first I've heard of it. 3. If that makes you uneasy, how do you feel about using bash in the first place? Anyway, use a separate variable for the options as I told you.
    – user601
    Commented Apr 30, 2016 at 17:46
  • 1
    @alvas: the assumption is that gizmo is not on the path or there is a command with the same name but a higher priority. else you could simple set CMDA to plain gizmo in the first place.
    – user601
    Commented Apr 5, 2017 at 9:20
70

Aliases can't be exported so they're not available in shell scripts in which they aren't defined. In other words, if you define them in ~/.bashrc they're not available to your_script.sh (unless you source ~/.bashrc in the script, which I wouldn't recommend but there are ways to do this properly).

However, functions can be exported and would be available to shell scripts that are run from an environment in which they are defined. This can be done by placing this in your bashrc:

foo()
{
    echo "Hello World!"
}
export -f foo

As the Bash manual says, "For almost every purpose, shell functions are preferred over aliases."

7
  • 3
    While this doesn't technically answer the question, as you say you can simply replace alias commandA=... with commandA() { ... } then export commandA and you get identical behavior to the alias. So this is pretty much an identical alternative to aliases as far as I know that works great in bash scripts
    – Phylliida
    Commented May 21, 2018 at 18:35
  • The problem here is when you need an alias that you pass lots of quoted options to. for instance as an example, alias b=bash makes it easy to do b -c "echo test | grep test" which would be difficult to do in functions.
    – Fmstrat
    Commented Aug 13, 2019 at 16:03
  • @Fmstrat: b () { bash "$@"; } then b -c "echo test | grep test" Commented Aug 13, 2019 at 16:10
  • This does not solve the problem when the contents of the function needs to be evaluated in the script that got the function from another script...
    – Akito
    Commented Jan 29, 2020 at 18:10
  • @Akito: I'm not sure what you mean. It works for me. Commented Jun 28, 2022 at 23:46
22
[cmd line] > bash -i [your script's file path]

The i is for interactive and sources your bash profile for you.

0
18

You can also use . before your script. Run it as:

. /path/to/your/script.sh

You can check for this condition on your bash script as follows:

if [[ $- == *i* ]]
then
    echo "running on interactive mode. aliases will work!"
fi
3
  • 2
    For a solution which avoids editing the script itself (that is why a script exists for us so it can be shared among many people) this is really great
    – demongolem
    Commented Mar 30, 2021 at 12:22
  • 1
    Sure, I wouldn't put it into a professional production script, but I just needed something quick and scrappy +1
    – MANA624
    Commented Aug 17, 2021 at 19:13
  • 1
    My script contains a set -e, and running it this way causes the set -e to carry over into other bash sessions once the script exists, causing my terminals to exit when any non-zero code is encountered, even just trying to use tab completion. It was pretty frustrating to track down, not complaining -- just putting it here to help. Commented Nov 2, 2023 at 22:14

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .