Using "nice" command with an alias - bash

How can I use the "nice" command with an alias?
As an example:
alias list=ls
list # works
nice -10 list # doesn't work
How could I make that last line work?

Alias is a shell feature, and nice is an external program:
$ type nice
nice is hashed (/usr/bin/nice)
It's the program nice that runs the command passed as an argument, calling the C function execve, so all the arguments for it need to be evaluated BEFORE the call.
So, it would probably better not to use an alias and simply put the whole command needed there, but if you really want to, you could try something like this:
$ nice -10 `alias list | sed "s/^\(alias \)\?[^=]\+='//; s/'$//;"`
alias list prints the alias definition in the format alias list='ls' (or list='ls', if it's /bin/sh), so I did some sed substitutions there to get only the command it expands to.
If you're sure to use only bash you can use ${BASH_ALIASES[list]} instead, as pointed out in the comments:
$ nice -10 ${BASH_ALIASES[list]}

Though perhaps not as exotically exciting as nice -10 $UserVar1; or nice -10 ${BASH_ALIASES[list]}, you may also have the nice -10 list you asked for, though via wrapper script instead of alias:
# one-time setup
mkdir -p ~/.local/aliases
echo 'PATH=$HOME/.local/aliases:$PATH' >> ~/.bashrc
# open new terminal window, or
source ~/.bashrc
# create the wrapper. $# to passthrough args.
echo 'ls $#' > ~/.local/aliases/list
chmod +x ~/.local/aliases/list
nice -10 list # works :)
nice -10 list --color=always -lathr # args passthrough also works :)

For Zsh BASH_ALIASES will not work. So you may use it like this:
nice -10 `list`

This is a bad idea. You are defining a command alias but are not even using it as an alias expansion. Poor coding practice here. This is what you want.
declare -x UserVar1='ls';
nice -10 $UserVar1;
And if you will not change the definition of UserVar1 later on in your code. There are zero reasons you can justify to use a variable instead of the actual command name.
You are headed for disaster. Plain and simple. Use a variable or the command name itself it is far safer and marginally more efficient and easier to read.

Related

Use content of file as part of a Bash command

I want to use the content of a file.txt as part of a bash command.
Suppose that the bash command with its options that I want to execute is:
my_command -a first value --b_long_version_option="second value" -c third_value
but the first 2 options (-a and --b_long_version_option ) are very verbose so instead of inserting directly on the command line (or bash script) I wrote them in a file.txt like this:
-a first value \
--b_long_version_option="second value"
Now I expect to call the command "my_command" with the following syntax (where "path_to/file.txt" is the path to file.txt, expressed in relative or absolute form):
my_command "$(cat path_to/file.txt)" -c third_value
This however is not the right syntax, as my command is breaking and complaining.
How should I write the new version of the command and/or the file.txt so that it is equivalent to its native bash usage?
Thanks in advance!
The quotes are preserving the newlines. Take them off.
You also don't need the cat unless you're running an old bash parser.
my_command $(<path_to/file.txt) -c third_value
You'll need to take the backslashes at the ends of lines out.
Be careful doing things like this, though. It's probably better to just put the whole command in the file, rather than just pieces of it. If you really just want arguments, maybe define them a little more carefully in an array, source the file and then apply them, like this:
in file:
myArgs=( "-a" "first value"
"--b_long_version_option=second value"
)
Note the quoting. Then run with
. file
my_command "${myArgs[#]" -c third_value
e.g.,
$: printf "[%s] " "${myArgs[#]}" -c=foo
[-a] [first value] [--b_long_version_option=second value] [-c=foo]
I haven't seen any example of what you're trying. But, there are simpler ways to achieve your goal.
Bash Alias
ll for example is a bash alias for ls -al. It usually is defined in .bash_profile or .bashrc as follows :
alias ll='ls -al'
So, what you can do is to set another alias for your shorthand command.
alias mycmd='mycommand -a first value --b_long_version_option="second value"'
then you can use it as follows :
mycmd -c third_value
Config file
You can also define a mycommand.json file or mycommand.ini file for default arguments. Then, you will need to check for config file in your software, and assign arguments from it.
Using config file is more advanced solution. You can define multiple config files. You can set default config file in /etc/mycommand/config.ini for example. When running on different directories, you should check ${cwd}/mycommand.ini to check local config file exists or not. You can even add a --config-file argument to your command.
Using alias is more convenient for small tasks, or thing that won't change much. If your command's behavior should be different in some other project, the using a config file would be a better solution.

How to force interpretation as keyword instead of alias on command line?

I have
alias time='/usr/bin/time -v'
configured in my bash environment.
However, I sometimes need an access to keyword time for some one-liners such as
(expensive-command1 & expensive-command2 & time wait); something-that-requires-prev-commands-to-be-complete
Where the wait should obviously wait for both expensive-command1 and expensive-command2 to complete and the keyword time is able to measure the time taken.
However, when I have alias time pointing to actual binary I get following error instead:
/usr/bin/time: cannot run wait: No such file or directory
How to force interpretation keyword for the time in command above? I know that if I run unalias time before running above line it works but then my alias is gone for that shell.
I also know that if I wanted to go from keyword to actual binary, I could use syntax (cmd1 & cmd2 & command time wait). And if I needed built-in command I could use syntax (cmd1 & cmd2 & builtin time wait). However, the time needed here is not command nor builtin. It's a keyword but I cannot figure out how to get the above one-liner to be interpreted as such.
Define a function like below before the line where alias time is defined in your bashrc.
_time() { time "$#"; }
Then you can use _time whenever you need the keyword time.
$ _time() { time "$#"; }
$ alias time='echo foo'
$ time ls
foo ls
$ _time ls
.bash_history .bashrc
real 0m0.024s
user 0m0.010s
sys 0m0.000s
The manual explains why this works as follows.
Aliases are expanded when a function definition is read, not when the function is executed, because a function definition is itself a command.
In an environment where you don't have permission to change bashrc, you can do:
$ shopt -u expand_aliases
$ _time() { time "$#"; }
$ shopt -s expand_aliases
and get the same functionality.
As of Bash version 4.4, there's no nice way to force interpretation as keyword instead of alias. For workarounds, see answer by oguz ismail. In short, all workarounds create alternate name for the original keyword behavior and then you have to always use the alternate instead of the correct keyword.
For now, it seems that the best way to proceed is to consider all bash keywords as reserved words and never ever define any alias with identical name. This is the solution that I'm personally using.

Choose placeholders for substitution inside a bash script

NOTE: The goal of this question is to find a suitable character sequence for an effective placeholder substitution in a bash script, not finding if a command is evaluated or not.
I have a skeleton named my_script.skelof a bash script in which I have to put a placeholder. I want to find a placeholder sequence that can be safely substituted (I mean that there aren't any clashes with other bash commands or other pathological substitutions, see A non-safe example for an example).
I've figured out on my own that enveloping placeholder_name within #~ and ~# seems a good solution, but I'm not sure that this solution is safe in every possible case.
A non-safe example
One can decide to use /placeholder_name/. So my_dumb_script.skel is:
#!/bin/bash
AN_INNOCENT_PATH="/a/simple/placeholder_name/path"
echo /placeholder_name/
The goal is to replace only /placeholder_name/ in the echo command. If now I use sed on the placeholder:
sed 's%/placeholder_name/%foobar%g' my_dumb_script.skel > output.bash
The output could be unexpected:
#!/bin/bash
AN_INNOCENT_PATH="/a/simplefoobarpath"
echo foobar
In this case we've obtained an unwanted substitution inside AN_INNOCENT_PATH, since it's easy to pattern-match on placeholders that contains the / character. I know that it seems very dumb, but you cannot know how people will use your code in the future (and someone could create a folder named placeholder_name).
A safe example that uses #~
In this case my_better_script.skel is the following one:
#!/bin/bash
AN_INNOCENT_PATH="/a/simple/placeholder_name/path"
echo #~placeholder_name~#
And now we can use sed:
sed 's%#~placeholder_name~#%foobar%g' my_better_script.skel > output.bash
The output now is better:
#!/bin/bash
AN_INNOCENT_PATH="/a/simple/placeholder_name/path"
echo foobar
Now everything works as intended.
You can use type to check if a command will evaluate.
$ placeholder1="testing"
$ placeholder2="test"
$ type $placeholder1
-bash: type: testing: not found
$ type $placeholder2
test is a shell builtin

Readonly alias in Bash

In Bash I can mark functions as readonly doing something like this
declare -r -f functionName
Is there any way to do the same thing with alias names? I mean: once an alias is set I don't want it to be assigned again.
No; aliases use a separate namespace. You are really better off using functions instead of aliases (not just for this reason).
As per my knowledge I believe no.
Reason for judgement:
You can always invoke the pure command by appending a \ (backslah). e.g. if you would aliased ls to ls -lrt, you can always invoke to non-aliased version of ls by writing \ls. So, in a way, alias is not permanent/unmodifiable in any sense.

alias parameter not working

I am trying to make it easier to use scp so I learned about alias today, and I am using it like this:
alias loudie-scp="scp -i keys/aws.pem $1 ec2-user#ec2-107-20-68-112.compute-1.amazonaws.com:/home/ec2-user"
the $1 is there to specify the file i want to transfer over. However this is not working and giving me an error:
scp: /home/ec2-user: not a regular file
This does not happen when I execute this command manually passing in any file for $1.
BASH FAQ entry #80: "How can I make an alias that takes an argument?"
Unfortunately BASH aliases are kind of like find-and-replace -- they're not very powerful for the sort of task you describe. I would suggest using, instead, a script file and placing it in an executable directory; something like so:
#!/bin/bash
scp -i keys/aws.pem $1 ec2-user#ec2-107-20-68-112.compute-1.amazonaws.com:/home/ec2-user
Then, given that it has the name loudie-scp you could call it like so:
loudie-scp <parameter>
As I'm sure Ignacio's link will explain, an alias does nothing more than textually expand the alias to its value. It does not take arguments, you need to use a function for that.

Resources