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.
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 stripe down my script to the following:
search() {
grep foobar ~/!(test1|test2)
}
Very straight forward. I want to create a procedure that grep text foobar, but don't grep in test1 or test2 folder
If I simply run this in bash command line, it works as expected(!). Procedure is created and can be called:
boyang#vbox-ubuntu:~$ search
grep: /home/boyang/Code/analytics.jpg: Is a directory
/home/boyang/Code/bash_includes: grep foobar ~/Code/!(test1|test2)
If I put this in a script (e.g. bash_includes), and source it in .bashrc:
if [ -f ~/bash_includes ]; then
. ~/bash_includes
fi
it gives:
bash: /home/boyang/bash_includes: line 2: syntax error near unexpected token `('
bash: /home/boyang/bash_includes: line 2: ` grep foobar ~/!(test1|test2)'
I tried adding shebang, tried adding quotes ("~/!(test1|test2)" or '~/!(test1|test2)' or \`~/!(test1|test2)\`). Nothing works
I'm not a bash expert, but this is puzzling. What am I missing?
Ubuntu 14.04 Bash 4.3.11(1)-release
This code is only syntactically valid if extglobs are enabled. Use shopt -s extglob to enable them:
shopt -s extglob
search() {
grep foobar ~/!(test1|test2)
}
Presumably your init files were already running this command (such that extglobs were ultimately turned on for your interactive session), but after the function's definition.
Out of curiosity, say you created this function:
cd () { echo 'hi'; }
Now, whenever you entered cd you'd get "hi" printed. In case, you wanted to use the original cd command, you could do the following:
builtin cd
But what if I also created this:
builtin() { echo 'haha, got ya!'; }
Now, how do you get the builtin command for either builtin or cd?
Okay first off don't override the builtin command, which I assume you're not doing on purpose. Given the fact that you can override all the builtins, I thought up a more ridiculous route:
#!/bin/bash
builtin() { echo "Ha"; }
cd() { echo "Ha"; }
pwd() { echo "foobar"; }
result=$(/bin/bash --noprofile --norc -c "\\cd /home/cwgem/testdir; \\pwd; \\ls" )
/bin/echo "$result"
Result:
$ bash test.sh
/home/cwgem/testdir
test.txt
The idea here is to:
Use absolute paths to functions which can't be overridden as a function (this of course goes under the assumption that you don't go messing around with /bin/bash and /bin/echo)
Utilize the fact that $() is syntactic command substitution and I wasn't able to override that
Calling bash with --noprofile and --norc, which are explicit in not sourcing in the usual files which might contain overrides
Finally \'s are added in front of commands in case aliases are floating around
That said, all and all this hack was done out of academic curiosity and should not be applied in the real world.
I remembered about the unset command while writing this question down. If anyone else wants to know, you can do:
unset builtin
and
unset cd
to make the original commands work as expected. If anyone has a way to do so without un-setting the methods, I'd be interested in knowing how.
Another option would be to use env -i to strip off the environment with its aliases and functions:
builtin() { echo 'haha, got ya!'; }
builtin pwd
/usr/bin/env -i bash -c 'builtin pwd'
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=