This question already has answers here:
Make a Bash alias that takes a parameter?
(24 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question 12 months ago and left it closed:
Original close reason(s) were not resolved
How do I pass the command line arguments to an alias? Here is a sample:
alias mkcd='mkdir $1; cd $1;'
But in this case the $xx is getting translated at the alias creating time and not at runtime. I have, however, created a workaround using a shell function (after googling a little) like below:
function mkcd(){
mkdir $1
cd $1
}
Just wanted to know if there is a way to make aliases that accept CL parameters.
BTW - I use 'bash' as my default shell.
Just to reiterate what has been posted for other shells, in Bash the following works:
alias blah='function _blah(){ echo "First: $1"; echo "Second: $2"; };_blah'
Running the following:
blah one two
Gives the output below:
First: one
Second: two
You found the way: create a function instead of an alias. The C shell has a mechanism for doing arguments to aliases, but bash and the Korn shell don't, because the function mechanism is more flexible and offers the same capability.
You cannot in ksh, but you can in csh.
alias mkcd 'mkdir \!^; cd \!^1'
In ksh, function is the way to go. But if you really really wanted to use alias:
alias mkcd='_(){ mkdir $1; cd $1; }; _'
To quote the bash man page:
There is no mechanism for using arguments in the replacement text. If
arguments are needed, a shell function should be used (see FUNCTIONS
below).
So it looks like you've answered your own question -- use a function instead of an alias
You may also find this command useful:
mkdir dirname && cd $_
where dirname is the name of the directory you want to create
The easiest way, is to use function not alias. you can still call a function at any time from the cli. In bash, you can just add function name() { command } it loads the same as an alias.
function mkcd() { mkdir $1; cd $1 ;}
Not sure about other shells
I found that functions cannot be written in ~/.cshrc file. Here in alias which takes arguments
for example, arguments passed to 'find' command
alias fl "find . -name '\!:1'"
Ex: >fl abc
where abc is the argument passed as !:1
You actually can't do what you want with Bash aliases, since aliases are static. Instead, use the function you have created.
Look here for more information: http://www.mactips.org/archives/2008/01/01/increase-productivity-with-bash-aliases-and-functions/. (Yes I know it's mactips.org, but it's about Bash, so don't worry.)
This works in ksh:
$ alias -x mkcd="mkdir \$dirname; cd \$dirname;"
$ alias mkcd
mkcd='mkdir $dirname; cd $dirname;'
$ dirname=aaa
$ pwd
/tmp
$ mkcd
$ pwd
/tmp/aaa
The "-x" option make the alias "exported" - alias is visible in subshells.
And be aware of fact that aliases defined in a script are not visible in that script (because aliases are expanded when a script is loaded, not when a line is interpreted). This can be solved with executing another script file in same shell (using dot).
Here's a simple example function using Python. You can stick in ~/.bashrc.
You need to have a space after the first left curly bracket.
The python command needs to be in double quotes to get the variable substitution. Don't forget that semicolon at the end.
count(){ python -c "for num in xrange($1):print num";}
Example run:
$ count 6
0
1
2
3
4
5
$
An empty alias will execute its args:
alias DEBUG=
Related
I have a script with an alias _test. It works fine, but before printing the output, it does <arguments>: command not found. For example, _test -h gives line 49: -h: command not found
This is a minimal example:
alias _test='
echo Hi
'
shopt -s expand_aliases
_test -h
EDIT: For those asking about using functions, I did in fact, used to have a function instead -- but it started to cause recursion problems. I just wanted something similar to a macro -- something that acts as if the text was inserted into the script.
EDIT 2: I just realized why I kept having recursion with my function/alias. I fixed it, and I switched back to a function, but this question may help someone else.
Remove the newlines. As written, _test -h expands to this, with a blank line above and below the echo:
echo Hi
-h
Make it a one-line alias:
alias _test='echo Hi'
In general, though, avoid aliases. They're really intended for convenience in interactive shells. In a script—or heck, even in interactive shells—it's better to use functions instead. For example:
_test() {
echo Hi "$#"
}
For those asking about using functions, I did in fact, used to have a function instead -- but it started to cause recursion problems. I just wanted something similar to a macro -- something that acts as if the text was inserted into the script.
Were you trying to wrap an existing command, like this?
alias ls='ls --color=auto -F'
If so you can use command to prevent a function calling itself recursively. The equivalent function would be:
ls() {
command ls --color=auto -F "$#"
}
command ls calls the ls command rather than the ls function we've just defined so we don't get stuck in an infinite recursive loop.
Thanks to Kugelman's answer, I was able to solve it with
alias _test='
echo Hi
true'
shopt -s expand_aliases
_test -h
I am trying to set an alias in a script and then execute the alias later on in the script. I've verified that the file path that the alias contains is valid, and I've also set the shell script to expand aliases as well, yet the script still refuses to use the alias. What could I be doing incorrectly here?
Script:
#set location of parallel script and replace ~ with $HOME if necessary
parallellocation="~/newnnm/parallel"
parallellocation="${parallellocation/#\~/$HOME}"
#If the parallellocation variable is set and a parallel command is not currently available,
#proceed with setting an alias that points to the parallellocation variable
if [ -r "$parallellocation" ] && ! command -v parallel &>/dev/null; then
shopt -s expand_aliases
alias parallel="$parallellocation"
parallel
fi
Sample output:
./processlocations_new2.sh
./processlocations_new2.sh: line 98: parallel: command not found
As reflected in the comment record on the question, bash seems not to honor alias definitions or setting of the alias_expand option within the scope of an if block or other compound command. The Bash Manual explains this:
The rules concerning the definition and use of aliases are somewhat
confusing. Bash always reads at least one complete line of input
before executing any of the commands on that line. 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.
(Emphasis added.) The comments do not refer directly to shell options, but the same logic that says alias definitions within a compound command do not apply within the same compound command also implies that it is the value of the expand_aliases option in effect when the compound command is read that applies.
The question arose as to how to use a shell function instead of an alias for this purpose. Here's one way:
altparallel="$HOME/newnnm/parallel"
parallellocation=
if command -v parallel >/dev/null 2>&1; then
parallellocation="command parallel"
elif [[ -x "$altparallel" ]]; then
parallellocation="$altparallel"
fi
# Runs parallel with the given arguments. Uses the 'parallel' command
# found in the path if there is one, or a local one designated by
# $altparallel if that exists and is executable. Exit status is that of
# 'parallel', or 1 if no 'parallel' command is available.
parallel() {
[[ -z "$parallellocation" ]] && return 1
# expansion of $parallellocation is intentionally unquoted
$parallellocation "$#"
}
You source that from your environment setup scripts to get a parallel function defined that does what you want.
On the third hand, if all you want is a script that runs one version of parallel or the other, directly, then you don't need either a function or an alias. Just figure out which you want to run, and run it, something like:
altparallel="$HOME/newnnm/parallel"
if command -v parallel >/dev/null 2>&1; then
parallel "$#"
elif [[ -x "$altparallel" ]]; then
"$altparallel" "$#"
else
exit 1
fi
Aliases are a parse time feature. They work by substituting one string for another during parsing.
Commands are entirely parsed before they're executed, and this includes compound commands like if.
The effect of this is that any changes to the parser, like setting aliases, will not take effect in any of the possibly nested compound commands where the setting takes place.
For example, if you wrap your entire script in {...}, no aliases will work because it's now a giant compound command.
This is yet another reason why you should never use aliases outside .bashrc, and even then just sparingly. Use functions.
You can enable/disable alias expansion with shopt within a bash script/command line.
$ shopt -u expand_aliases
-s Set
-u Unset
You can also use unalias to remove specific aliases (if you know them)
$ unalias <aliasname>
Be aware when you're using aliases in BASH srcipts, they aren't going to expand inside conditional constructs.
So while this one would work inside of a script:
#!/bin/bash
shopt -e expand_aliases
alias list1='ls -lhS'
list1
this one won't:
#!/bin/bash
shopt -e expand_aliases
alias list2='ls -lah'
if [ 1 ]; then
list2
fi
So as the others pointed out in the comment section, use functions, or use eval to execute the command string stored in $parallellocation:
eval $parallellocation
I would like to invoke alias from ruby code so as to test the alias which I programmatically inserted into the dotfile. Say for example, the alias is the following:
alias something="echo somethingelse"
I searched the web and found the solution for bash:
#solution for bash
system %(
source ~/.bash_profile
shopt -s expand_aliases
something
)
However, this does not work for zsh.
I tried to invoke the alias using the following code (and a combination of other commands) but to no avail.
system %(
exec zsh #this seems to source .zshrc
something #this does not work
)
I would like it to work for zsh too. How can I get it working for zsh? Does anyone have a suggestion? Thanks in advance!
I have also tested the following but they don't work:
system %(
# exec /bin/zsh #this causes the subsequent lines to not run.
source ~/.zshrc #this causes the error lines to be printed
# setopt aliases #don't think it helps
something #trying to invoke this which is already in zshrc
)
The error messages:
/Users/ytbryan/.zprezto/init.zsh: line 14: autoload: command not
found
/Users/ytbryan/.zprezto/init.zsh: line 15: print: command not
found
/Users/ytbryan/.zshrc: line 42: `#': not a valid identifier
One approach is to run Zsh code via zsh -c. Aliases are not expanded when run from zsh -c, but the builtin aliases array is still accessible, so one can still expand aliases by manually retrieving expansions from the array and manually performing word splitting. This should cover most of the commonly seen aliases. For more advanced aliases (that involves process substitution, parameter expansion, command substitution, arithmetic expansion, brace expansion, filename expansion or filename generation, or that is more than a simple command), one might need to use eval (but one needs to be very cautious when using eval, and never ever use it when input comes from an untrusted source, or from a trusted but possibly tempered-with source).
Example code that could be embedded in Ruby system calls:
> zsh -c 'alias foo="print bar"; ${=aliases[foo]}'
bar
> zsh -c 'alias foo=print; ${=aliases[foo]} $#' -- 1 2 3
1 2 3
> zsh -c 'alias foo="print a b c | grep -o a"; ${=aliases[foo]}' # simple case where naive expansion fails
a b c | grep -o a
> zsh -c 'alias foo="print a b c | grep -o a"; eval "$aliases[foo]"' # eval comes to rescue, but be extra careful
a
Note that source works in zsh -c, so the alias definitions above could be sourced from any file just fine.
Newcomer to bash scripting here. Been outfitting my bash_profile with some useful functions to query some mysql databases, but am having trouble getting bash to recognize a passed parameter as an alias. See below for details:
function findfield() {
$2 -e
"SELECT TABLE_NAME,TABLE_SCHEMA,COLUMN_NAME AS 'Matched Field'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE COLUMN_NAME LIKE '$1';"
}
example usage:
findfield %field% mpc
Where mpc is an alias that points to the database to query. This usage returns an error:
-bash: mpc: command not found
The above function works if I simply hardcode mpc in place of $2--so why wouldn't it work with an alias as a parameter instead?
Aliases don't work by default in noninteractive shells. You can change that with shopt -s expand_aliases, but I'm not sure it will help.
You need another layer of evaluation. By the time bash finishes substituting everything and wants to run the command, it thinks of "mpc" as a string. You can fix this change that with eval, but then you need to safeguard the other parameters and what if someone passes something naughty? This is why the use of eval is generally frowned upon. Sometimes it is unavoidable though:
$ run() { $1; }
$ alias alal=uname
$ run whoami
lynx
$ run alal
bash: alal: command not found
$ run() { shopt -s expand_aliases; $1; shopt -u expand_aliases; }
$ run alal
bash: alal: command not found
$ run() { shopt -s expand_aliases; eval $1; shopt -u expand_aliases; }
$ run alal
Linux
Anyway, you also need to fix the quoting in the sql or the field will never get expanded. The syntax highlighting here makes this obvious. A simple way is just to enclose $1 in a pair of ", so you effectively split the string into three until it is passed on.
You may need to add an extra line in your bash_profile file:
function myalias_func()
{
some code here with different variables $1, $2...
}
alias myalias=myalias_func
That is, try including the line
alias findfield=findfield
and it should work then.
I want to inject a transparent wrappering command on each shell command in a make file. Something like the time shell command. ( However, not the time command. This is a completely different command.)
Is there a way to specify some sort of wrapper or decorator for each shell command that gmake will issue?
Kind of. You can tell make to use a different shell.
SHELL = myshell
where myshell is a wrapper like
#!/bin/sh
time /bin/sh "$0" "$#"
However, the usual way to do that is to prefix a variable to all command calls. While I can't see any show-stopper for the SHELL approach, the prefix approach has the advantage that it's more flexible (you can specify different prefixes for different commands, and override prefix values on the command line), and could be visibly faster.
# Set Q=# to not display command names
TIME = time
foo:
$(Q)$(TIME) foo_compiler
And here's a complete, working example of a shell wrapper:
#!/bin/bash
RESULTZ=/home/rbroger1/repos/knl/results
if [ "$1" == "-c" ] ; then
shift
fi
strace -f -o `mktemp $RESULTZ/result_XXXXXXX` -e trace=open,stat64,execve,exit_group,chdir /bin/sh -c "$#" | awk '{if (match("Process PID=\d+ runs in (64|32) bit",$0) == 0) {print $0}}'
# EOF
I don't think there is a way to do what you want within GNUMake itself.
I have done things like modify the PATH env variable in the Makefile so a directory with my script linked to all name the bins I wanted wrapped was executed rather than the actual bin. The script would then look at how it was called and exec the actual bin with the wrapped command.
ie. exec time "$0" "$#"
These days I usually just update the targets in the Makefile itself. Keeping all your modifications to one file is usually better IMO than managing a directory of links.
Update
I defer to Gilles answer. It's a better answer than mine.
The program that GNU make(1) uses to run commands is specified by the SHELL make variable. It will run each command as
$SHELL -c <command>
You cannot get make to not put the -c in, since that is required for most shells. -c is passed as the first argument ($1) and <command> is passed as a single argument string as the second argument ($2).
You can write your own shell wrapper that prepends the command that you want, taking into account the -c:
#!/bin/sh
eval time "$2"
That will cause time to be run in front of each command. You need eval since $2 will often not be a single command and can contain all sorts of shell metacharacters that need to be expanded or processed.