337

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?

ilkkachu
  • 138,973
Zaid
  • 10,642
  • 13
    BTW, there's no need to log in and out to have an alias recognized. You need just do source ~/.bashrc. – tshepang Nov 25 '10 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 Sep 06 '15 at 05: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.) – LF-DevJourney Sep 12 '19 at 10:18

5 Answers5

280

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
ddeimeke
  • 4,597
  • 19
    I placed it in my script, but it's still not working. Same error. – Zaid Sep 02 '10 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. – Frank Schubert Apr 04 '13 at 01: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 Mar 20 '15 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. – Stefanos Kalantzis Feb 05 '16 at 11:47
  • @StefanosKalantzis I don't agree. ~/.bash_aliases might depend on other stuff but in most cases NOT. In my case #!/bin/bash -i or -li doesn't solve the problem because I've been using keychain to autoload ssh keys. So each time I try run my script with bash -i my whole ~/.bashrc was running as well with keychain and print all keys in terminal. I see above solution as most precise then bash -i. – sobi3ch Jun 01 '16 at 19:08
  • @sobi3ch I believe this is way too user specific, and there's no right or wrong. At the end of the day, what hop said in the accepted answer is the correct thing. Just don't depend on your aliases in a bash script. it's just wrong ! – Stefanos Kalantzis Jun 02 '16 at 10:56
  • This did not work for me. But putting the first line in my script and then setting aliases worked. So putting eg. this into my script works in my case: shopt -s expand_aliasesalias my_alias="ls". Edit: #!/bin/bash -i as shebang also worked. – JustAC0der Sep 25 '18 at 11:58
  • Another gotcha: https://unix.stackexchange.com/a/502261/233125 – davidvandebunte Mar 22 '19 at 15:31
  • This is the actual answer, because functions' contents are evaluated when they are defined, whereas an alias is evaluated during its execution. So if you need to run a command, that is 1. imported from a sourced script 2. needs to be evaluated in the current script, then you need to use an alias. – Akito Jan 29 '20 at 18:24
  • Thanks. My problem was that I sourced it before setting expand_aliases. – Geremia Dec 17 '20 at 18:16
  • 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 Feb 17 '22 at 23:00
163

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
mtraceur
  • 1,166
  • 9
  • 14
  • 7
    That solution doesn’t work for the usual alias use cases. E.g. alias mv="mv -v --backup=numbered". –  Apr 23 '16 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. –  Apr 24 '16 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. –  Apr 30 '16 at 00: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. –  Apr 30 '16 at 17:46
  • From experience, unquoted variables have the bad habit of blowing up in one’s face. E.g. when the variable is empty, one expects it to have no spaces but it does, etc. Here, the latter is intended, but it still makes me uneasy. :) 2. Turns out it’s something in-between: http://wiki.bash-hackers.org/scripting/style#syntax_and_coding_guidelines → If you use uppercase variables, there’s a chance that a later version of bash uses that name itself, breaking your script. 3. Don’t worry. You seem to have mistaken my comment for criticism. It wasn’t. :)
  • –  May 01 '16 at 21:08
  • How about CMDA="$(which gizmo)"? – alvas Apr 05 '17 at 06:42
  • 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. –  Apr 05 '17 at 09:20
  • @Evi1M4chine The lowercase rule applies for local and script variables. In contrast, environment variables are typically uppercase. Prefix should be enough to prevent collision since this is what everyone has been doing: Bundler is using BUNDLE_, OpenSSH is using SSH_. If you can't survive the prefix way, you are probably exposing too many environment variables (so you are doomed anyway). – Franklin Yu Apr 09 '17 at 06:47
  • I'm not convinced with the reasoning that script can break when it is moved. The same can happen if path is set by environment variable. The path to command may change when script is moved to a different environment. Or command may not be installed in new environment. So, when script moves, user will have to take care of environment changes anyway. No? – rineez Jan 30 '18 at 03:56
  • @rineez: except this way it's right there in the script –  Jan 30 '18 at 14:24
  • It's a drop-in replacement. What is the correct way without duplicating every script for use with that drop-in replacement? – J.Money Dec 31 '19 at 02:22