Bash variable space in directory path - bash

How can I remove a white space when I use a variable in a directory path.
For example, I'm trying to do
alias test='echo /david/$1'
and when I try
test hhh
this produces
/david/ hhh
with a white space before the variable. It seems very simple but I can't find a solution.

alias doesn't do parameter expansion. At all. Use a function instead.
test (){
echo "/david/$1"
}

man bash:
There is no mechanism for using arguments in the replacement text. If arguments are needed, a shell function should be used (see FUNCTIONS below). [...] For almost every purpose, aliases are superseded by shell functions.
As a part of alias expansion a space is added at the end of the expanded string (otherwise no argument could be added, like alias ll='ls -l'. So ll -a would be expanded to ls -l-a which would be wrong). So I see no solution to this problem anything else then to use function as Ignacio proposed.
Anyway using test as function or alias is not the best thing to do as it is a bash built-in command (if no aliased or named a function). You can check how a mnemonic will be interpreted using the type bash built-in.
I defined an alias and a function called test:
$ type test
type test
test is aliased to `echo /xxx/'
$ type -a test
type -a test
test is aliased to `echo /xxx/'
test is a function
test ()
{
echo "/yyy/$1"
}
test is a shell builtin
test is /usr/bin/test

Related

Setting default arguments / parameters for bash commands

I am looking for a way to have a bash command be passed default arguments in place of 0 arguments. For example when passing $ls to the command line the user would actually be passing $ls --color to the command line.
You can use functions or aliases.
If you want to prefix all ls invocations with your additional option, you can define an alias like this :
alias ls="ls --color"
This would be like implicitly using the option each time you use ls. When invoked, an alias is replaced by its assigned value. Aliases must be used at the beginning of a command, and the expanded alias will become part of the final statement that will be interpreted. In other words, aliases are a form of automatic text replacement and are quite limited in what they can do.
As suggested by Barmar, you can also use a function, which will allow you to use your default arguments (or some of them) only when no argument at all follows :
ls()
{
if
(( $# )) # True if at least one argument present
then
command ls "$#"
else
command ls --color
fi
}
One thing aliases allow that functions cannot is they execute in the same context as their calling context (they are a form of text replacement, not a function call at all), so they can interact with positional parameters. As an example, you could have an alias like this :
alias shift_and_echo="shift ; echo"
That would actually shift the positional parameters as active where the alias is used, contrary to a function call which would only shift its own arguments and leave the calling context positional parameters unaffected. The echo would have as its arguments whatever follows the alias invocation (which may be nothing at all).
To me, this is the main reason for using aliases in scripts for some specific purposes, as otherwise functions are generally better, being able to receive arguments, contain local variables...

Bash: declaration of an alias to be used in a single lined multiple commands

I would like to do something like
alias myls=ls && myls
This gives me an error
bash: myls: command not found
Strangely, a subsequent
myls
in the same session would be recognized.
Do you know how to declare alias, that is used in the same command sequence?
According to the bash manual:
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 compound 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.
alias has to be used on a separate line so even this won't work:
alias myls=ls; myls
This will result in -bash: myls: command not found error.
However, you can use function and use it on same line:
myls() { ls "$#"; } && myls
anubhava has already provided a better answer, but if for some reason you don't want to use a function instead of an alias then you can do
alias myls=ls && eval myls
Note, however, that usage of eval introduces an extra level of expansion, which may result in unexpected behaviour for more complex commands:
$ echo "A B"
A B
$ eval echo "A B"
A B
$ echo '$SHELL'
$SHELL
$ eval echo '$SHELL'
/bin/bash

ZSH: Call in-built function from zsh function that uses the same name

I use zsh and would like to slightly extend the in-built cd function.
I'd like that when I call cd, it changes directly and then lists the content of the directory.
function cd() {
cd $1
ls .
}
I'd have expected this code to work, but as it turns out, the call to cd refers to the function definition, resulting in an infinite loop.
Is there a work-around to solve this problem, apart from choosing a different name for my function?
UPDATE
Strangely enough, this worked
function cd() {
`echo $1`
ls .
}
No idea why.
In order to use builtin commands from within functions of the same name, or anywhere else for that matter, you can use the builtin precommand modifier:
function cd() {
builtin cd $1
ls .
}
builtin COMMAND tells zsh to use the builtin with the name COMMAND instead of a alias, function or external command of the same name. If such a builtin does not exist an error message will be printed.
For cases where you want to use an external command instead of an alias, builtin or function of the same name, you can use the command precommand modifier. For example:
command echo foobar
This will use the binary echo (most likely /bin/echo) instead of zsh's builtin echo.
Unlike with functions builtin and command are usually not necessary with aliases to prevent recursions. While it is possible to use an alias in an alias definition
% alias xx="echo x:"
% alias yy="xx y:"
% yy foobar
y: x: foobar
each alias name will only expanded once. On the second occurence the alias will not be expanded and a function, builtin or external command will be used.
% alias echo="echo echo:"
% echo foobar
echo: foobar
% alias xx="yy x:"
% alias yy="xx y:"
% xx foobar
zsh: command not found: xx
Of course, you can still use builtin or command in aliases, if you want to prevent the use of another alias, or if you want to use a builtin or external command specifically. For example:
alias echo="command echo"
With this the binary echo will be used instead of the builtin.
Why the echo command works is because you probably have the autocd option on. You can check this by typing setopt to get your list of options.
Then the echo-ing of the directory name and catching the output triggered the autocd and you went to that directory.

Execute a passed alias inside a function?

Background:
I'm trying make a function that runs commands on a set interval because I don't have access to a "watch" program. Simplified to it's most basic from, the function I'm trying to write is runit() { $1; }.
What works:
This works fine and dandy when I pass it things that aren't aliases. For example, runit "ls -l" works fine. I get the full output from the ls -l command.
What doesn't work:
The problem starts when I pass it an alias. For example, setting alias ll="ls -l" then calling runit "ll" will result in -bash: ll: command not found.
Things I have tried:
When I hard-code the alias runit() { ll; }, it works fine and gives me what I expect.
I feel like I might be overlooking something, but I can't quite place my finger on it.
Why would hard-coding the alias work fine, but passing it into the function fail?
Is there a way to accomplish what I'm attempting to do?
From the bash man page discussion of aliases (emphases mine):
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 compound 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.
You can observe this effect in functions by using the type command:
$ run_it () { ll; }
$ type run_it
You should see that the body of the function contains a call to ls -l, not ll.
The last sentence of the section on aliases:
For almost every purpose, aliases are superseded by shell functions.
My interpretation of that line is: if you think you want to use an alias, try writing a function first. Don't use an alias unless the function demonstrably fails to do what you need.
You can use eval like this:
$ runit() { eval $1; }
$ alias ll="ls -l"
$ runit "ll"
eval will expand any alias in $1 before the execution.
One way to solve this problem is to define a shell function rather than an alias.
ll () {
ls -l "$#"
}
The alias is expanded as a macro on command input, whereas the shell function is matched when the command is executed. This is a perfect example of how the shell's macro processor language is good for interactive grace but rather complicates actual programming.

How do I include an environment variable inside an alias for bash?

I am pretty new to bash, and I want to include an env for bash aliases.. I want to do something like the following
alias foo="bar $(baz)"
So that I could do something like the following
> baz=40
> foo
and foo will expand to the command bar 40. Currently the above does not work because $(baz) is expanded while making the alias. Do I have to wrap this inside a function or something?
You need to use single quotes (') to prevent bash from expanding the variable when creating the alias:
$ alias foo='echo "$bar"'
$ bar="hello"
$ foo
hello
Aliases don't have an "environment". An alias is simply a "dumb" text substitution. In the question, an environment variable isn't being used - only a shell variable. If you want to use the environment, use a function. In this case, there is no advantage to an alias over a function.
$ alias foo='echo "$bar"'
$ bar=hi foo
This produces no output because the environment set for a simple command doesn't apply to expansions.
$ alias foo=$'eval \'echo "$bar"\''
$ bar=hi foo
hi
If a function were used instead, there wouldn't be a problem.
$ foo() { echo "$bar"; }
$ bar=hi foo
hi
When in doubt, always use a function.
Edit
Technically, the above is bash-only. Doing this in a fully portable way is nearly impossible.
In dash, mksh, bash POSIX mode, and other POSIX shells you can do:
foo() { echo "$bar"; }
bar=hi command eval foo
However, this won't work in ksh93 or zsh. (I've already reported a bug for ksh93 but it may never be fixed.) In mksh and ksh93 you should instead define functions using the function keyword, but that isn't POSIX. I'm not aware of any solution that will work everywhere.
To make matters worse, extra exceptions are being added to POSIX 2008-TC1 so that the way environment assignments work will be even more complicated. I suggest not using them unless you really know what you're doing.
I do the following to delay expansion of environment variables in aliases until they are run,
alias foo="ls \${FOO_DIR}"
for example to show the contents of the dynamically-defined value of FOO_DIR.

Resources