How do create an alias in shell scripts? [duplicate] - bash

This question already has answers here:
using alias in shell script? [duplicate]
(7 answers)
Closed 2 years ago.
Definin an alias on Linux system is very simple.
From the following example we see that: the I_am_only_ls_alias alias command gives us the output as ls command
# alias I_am_only_ls_alias=ls
# I_am_only_ls_alias
Output:
file file1
But when I trying to do the same in bash script (define alias I_am_only_ls_alias), I get I_am_only_ls_alias: command not found.
Example of my bash script:
alias_test.bash
#!/bin/bash
alias I_am_only_ls_alias=ls
I_am_only_ls_alias
Run the bash script - alias_test.bash
/tmp/alias_test.bash
Output:
/tmp/: line 88: I_am_only_ls_alias: command not found
So, first I want to ask:
Why doesn't bash recognize the command I_am_only_ls_alias as an alias?
And what do I need to do in order to define aliases inside a bash script? Is it possible?

From the bash man page:
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 this should work:
#!/bin/bash
shopt -s expand_aliases
alias I_am_only_ls_alias=ls
I_am_only_ls_alias
Scripts usually use functions, not aliases.

Barmar's answer is the correct one for including an alias, but it's likely that you'll actually find a Bash function to be more flexible/useful.
For example this is materially the same as the alias version, but can be expanded much more easily:
timp#helez:~/tmp$ cat test.sh
#!/usr/bin/bash
function i_am_only_an_ls_func {
ls "$#"
}
i_am_only_an_ls_func
timp#helez:~/tmp$ ./test.sh
0600871h.html
[snip]
timp#helez:~/tmp$
The $# is irrelavent in this example, but it means that anything after i_am_only_an_ls_func will be added after the ls command, since $#, $1, $2, etc contain the arguments to the function, much the same as for a normal script. (Note that $0 is still the name of the parent script not the function)

Aliases cannot be defined in shell script that you execute - their effect will be gone once shell process finished execution.
You can, however, define aliases in your ~/.bashrc or in separate shell script that you source from (but not execute!). In that case, aliases are imported into already running shell process, and thus survive and actually work as you would expect.

Related

Bash: alias in bash -c

To be short:
How could I execute a
bash -c "set -x; alias ll='ls -l -h'; ll"
?
The output is
+ alias 'll=ls -l -h'
+ ll
bash: ll: command not found
Seems bash will add a single quote before my 'll'...
Thanks in advance!
There are two things preventing the alias ll from being expanded:
Aliases are only expanded in an interactive shell or if you use shopt -s expand_aliases.
Aliases are expanded (if they are expanded) when the line containing the command is read, not when it is executed, so only aliases defined on previous lines can be expanded. (Also, bash reads an entire compound command before executing it, so you can't define an alias and use it in the same compound command. That doesn't apply here but it might be useful to know.)
So you need to both mark the bash as interactive and introduce a newline character into the command line. You could, for example, do this:
bash -ic "alias ll='ls -l -h'"$'\n'"ll"
But it would be a lot easier to use a function:
bash -c 'll() { ls -l -h "$#"; }; ll'
All of that is documented in the Bash manual, §6.6 Aliases:
Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt…
…Bash always reads at least one complete line of input, and all lines that make up a compound command, before executing any of the commands on that line or the compound command. Aliases are expanded when a command is read, not when it is executed. Therefore, an alias definition appearing on the same line as another command does not take effect until the next line of input is read. The commands following the alias definition on that line are not affected by the new alias.
That section ends with the advice to use functions instead:
For almost every purpose, shell functions are preferred over aliases.
To be short:You can't.
According to man bash:
The rules concerning the definition and use of aliases are somewhat
confusing. Bash always reads at least one complete line of input, and
all lines that make up a compound command, before executing any of the
commands on that line or the compound command. Aliases are expanded
when a command is read, not when it is executed. Therefore, an
alias definition appearing on the same line as another command does
not take effect until the next line of input is read. The commands
following the alias definition on that line are not affected by the
new alias. This behavior is also an issue when functions are
executed. Aliases are expanded when a function definition is read,
not when the function is executed, because a function definition is
itself a command. As a consequence, aliases defined in a function
are not available until after that function is executed. To be safe,
always put alias definitions on a separate line, and do not use alias
in compound commands.
For almost every purpose, aliases are superseded by shell functions.
Use functions instead:
bash -c "ll(){ ls -l -h; }; ll"

After alias, script didn't get variable [duplicate]

This question already has answers here:
How to use aliases defined in .bashrc in other scripts?
(6 answers)
Closed 2 years ago.
I'm using a Mac and I have this alias defined in .bashrc:
$cat .bashrc | grep la
alias la='ls -la'
then I try to use it in a script:
$cat ./mytest.sh
#!/bin/bash
la
It runs and says it cannot find la:
./mytest.sh: line 2: la: command not found
Why is this? I tried on both Mac and Linux, same error!
Your .bashrc is only used by interactive shells. https://www.gnu.org/software/bash/manual/bashref.html#Bash-Startup-Files says:
Invoked non-interactively
When Bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for the filename.
As noted above, if a non-interactive shell is invoked with the --login option, Bash attempts to read and execute commands from the login shell startup files.
As you can see, there's nothing about .bashrc there. Your alias simply doesn't exist in the script.
But even if .bashrc were read, there's another problem:
Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt.
So if you wanted aliases to work in a script, you'd have to do shopt -s expand_aliases first. Or just use a shell function instead of an alias:
la() {
ls -la
}
The simplest answer is to fix this issue is to do the 2 important things in your script -or it wont' work, if you just do one thing.
#!/bin/bash -i
# Expand aliases defined in the shell ~/.bashrc
shopt -s expand_aliases
After this, your aliases that you have defined in ~/.bashrc they will be available in your shell script (giga.sh or any.sh) and to any function or child shell within such script.
If you don't do that, you'll get an error:
your_cool_alias: command not found
At the beginning of the ~/.bashrc file usually can be found two lines as:
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
This line aborts the inclusion for scripts which is not recommended anyway. For portability issues, you usually write the full command or define the alias in the script itself.

Why bash alias doesn't work in scripts? [duplicate]

This question already has answers here:
How to use aliases defined in .bashrc in other scripts?
(6 answers)
Closed 2 years ago.
I'm using a Mac and I have this alias defined in .bashrc:
$cat .bashrc | grep la
alias la='ls -la'
then I try to use it in a script:
$cat ./mytest.sh
#!/bin/bash
la
It runs and says it cannot find la:
./mytest.sh: line 2: la: command not found
Why is this? I tried on both Mac and Linux, same error!
Your .bashrc is only used by interactive shells. https://www.gnu.org/software/bash/manual/bashref.html#Bash-Startup-Files says:
Invoked non-interactively
When Bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for the filename.
As noted above, if a non-interactive shell is invoked with the --login option, Bash attempts to read and execute commands from the login shell startup files.
As you can see, there's nothing about .bashrc there. Your alias simply doesn't exist in the script.
But even if .bashrc were read, there's another problem:
Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt.
So if you wanted aliases to work in a script, you'd have to do shopt -s expand_aliases first. Or just use a shell function instead of an alias:
la() {
ls -la
}
The simplest answer is to fix this issue is to do the 2 important things in your script -or it wont' work, if you just do one thing.
#!/bin/bash -i
# Expand aliases defined in the shell ~/.bashrc
shopt -s expand_aliases
After this, your aliases that you have defined in ~/.bashrc they will be available in your shell script (giga.sh or any.sh) and to any function or child shell within such script.
If you don't do that, you'll get an error:
your_cool_alias: command not found
At the beginning of the ~/.bashrc file usually can be found two lines as:
# If not running interactively, don't do anything
[ -z "$PS1" ] && return
This line aborts the inclusion for scripts which is not recommended anyway. For portability issues, you usually write the full command or define the alias in the script itself.

Using aliases with nohup

Why doesn't the following work?
$ alias sayHello='/bin/echo "Hello world!"'
$ sayHello
Hello world!
$ nohup sayHello
nohup: appending output to `nohup.out'
nohup: cannot run command `sayHello': No such file or directory
(the reason I ask this question is because I've aliased my perl and python to different perl/python binaries which were optimized for my own purposes; however, nohup gives me troubles if I don't supply full path to my perl/python binaries)
Because the shell doesn't pass aliases on to child processes (except when you use $() or ``).
$ alias sayHello='/bin/echo "Hello world!"'
Now an alias is known in this shell process, which is fine but only works in this one shell process.
$ sayHello
Hello world!
Since you said "sayHello" in the same shell it worked.
$ nohup sayHello
Here, a program "nohup" is being started as a child process. Therefore, it will not receive the aliases.
Then it starts the child process "sayHello" - which isn't found.
For your specific problem, it's best to make the new "perl" and "python" look like the normal ones as much as possible. I'd suggest to set the search path.
In your ~/.bash_profile add
export PATH="/my/shiny/interpreters/bin:${PATH}"
Then re-login.
Since this is an environment variable, it will be passed to all the child processes, be they shells or not - it should now work very often.
For bash: Try doing nohup 'your_alias'. It works for me. I don't know why back quote is not shown. Put your alias within back quotes.
With bash, you can invoke a subshell interactively using the -i option. This will source your .bashrc as well as enable the expand_aliases shell option. Granted, this will only work if your alias is defined in your .bashrc which is the convention.
Bash manpage:
If the -i option is present, the shell is interactive.
expand_aliases: If set, aliases are expanded as described above under ALIASES. This option is enabled by default for interactive shells.
When an interactive shell that is not a login shell is started, bash reads and executes commands from /etc/bash.bashrc and ~/.bashrc, if these files exist.
$ nohup bash -ci 'sayHello'
If you look at the Aliases section of the Bash manual, it says
The first word of each simple command, if unquoted, is checked to see
if it has an alias.
Unfortunately, it doesn't seem like bash has anything like zsh's global aliases, which are expanded in any position.

script full name and path $0 not visible when called

I have a script "task.sh" with the following content:
#!/bin/bash
CUR_DIR=`pwd`
SCRIPTPATH="${CUR_DIR}/`dirname $0`"
when I call it with "bash task.sh" it works as expected but when it is called with ". task.sh"
$ . log/task.sh
dirname: invalid option -- b
Try `dirname --help' for more information.
When the script is being scheduled in crontab it is not working as well.
Can someone tell me what am I doing wrong or a different way in order to get the directory of a script that is not the current directory
?
When you invoke it as bash task.sh, bash assigns "task.sh" to $0 (from the bash manual: "If Bash is invoked with a file of commands [...] $0 is set to the name of that file.").
When you source the file, bash does not alter $0, it just executes the script in the current environment. What's in $0 in your current enviroment?
$ echo "$0"
-bash
The leading dash will be interpreted by dirname as an option.
If it's in a cron job, why are you sourcing it?
If you need to source your script, this will work if your shell is bash:
SCRIPTPATH="${CUR_DIR}/${BASH_ARGV[0]}"
However, cron's shell is, I believe, /bin/sh. Even if /bin/sh is a symlink to bash, when bash is invoked as sh it will try to behave POSIXly: the BASH_ARGV array probably won't be available to you.
There is no reason to call external binaries such as pwd and dirname when using bash. The functionality of these two binaries can be replicated with pure shell syntax.
Try the following:
#!/bin/bash
CUR_DIR="$PWD"
SCRIPTPATH="${CUR_DIR}/${0#*/}"
When you type,
bash foo.sh
you are executing script foo.sh, and bash sets the input argument $0 to the name of the script which is being run.
When you type,
. foo.sh
you are sourcing the script and the input argument $0 is not set.
In this situation you can use the automatic variable $_ which contains the argument of the last executed command.
In your script you could type,
SCRIPTPATH=$(dirname "$_")
to get the path of foo.sh.
Notice that, for this to work, this has to be the first command executed in the file.
Otherwise $_ will not contain the path of the sourced script.
Kudos to Dennis Williamson for providing this answer to a similar question.
I have used this for a long time without issues.
SCRIPTPATH=$(cd `dirname -- $0` && pwd)
The -- disable further processing of parameters.

Resources