Aliasing a command and using it in the same line of code - linux

I was wondering if you could alias a command, and use it in the same line of code, see this example:
alias php=/opt/plesk/php/5.6/bin/php; php -v;
I want this to output PHP 5.6.
alias php=/opt/plesk/php/7.3/bin/php; php -v;
and I want this to output PHP 7.3. However, what I get is this:
php -v
# outputs 5.6
alias php=/opt/plesk/php/5.6/bin/php; php -v;
# outputs 5.6
alias php=/opt/plesk/php/7.3/bin/php; php -v;
# outputs 5.6
php -v
# outputs 7.3
I've tried the && operator but it has the same outcome.
I'm wanting to use this in a gitlab continuous integration script, which executes a script through ssh -t by passing a string. However I am calling several php functions and I dont want to paste the full php path every time:
ssh -v -tt $SSH_HOST_NAME "__my_php_commands_here__"

I think the command line is being parsed, and aliases applied, before anything is executed. However, you can do it with shell functions. I don't have PHP, but I have several Perl versions to test with:
$ perl -v |grep version # V
This is perl 5, version 26, subversion 2 (v5.26.2) built for x86_64-cygwin-threads-multi
$ perl(){ /usr/bin/perl "$#" ; } ; perl -v |grep version
This is perl 5, version 26, subversion 3 (v5.26.3) built for x86_64-cygwin-threads-multi
# ^
So defining the pass-through function
perl(){ /usr/bin/perl "$#" ; }
changes how the word perl is interpreted later in the command line. Note that you do need the ; before } — see this answer.
For your use case, I would recommend using a different name to avoid confusion. E.g.:
currphp(){ /opt/plesk/php/5.6/bin/php "$#" ; } ; currphp -v
currphp(){ /opt/plesk/php/7.3/bin/php "$#" ; } ; currphp -v

in a gitlab continuous integration script
Batch non-interactive shells don't support aliases (by default). And that's a good think. alias should be just used as your own, custom shorthand, not in batch scrips.
You could
a) define a function with the same name and use full path for command resolution:
php() { /opt/plesk/php/5.6/bin/php "$#"; }
php -v
Downsides: function are not exported, unless you add export -f php, it is a shell function. Something like xargs php will work incorrectly.
b) use a varaible.
php=/opt/plesk/php/5.6/bin/php
"$php" -v
Downside: you have to modify all scripts and always check out for the $php.
c) Modify the path, so your php is found first. You could create a temporary directory and add it to path:
tmpd=$(mktemp -d)
trap 'rm -r "$tmpd"' EXIT
ln -s /opt/plesk/php/5.6/bin/php "$tmpd"/php
export PATH="$tmpd"/php:$PATH
php -v
If you export PATH correctly, it will work everywhere. Remember to remove the folder tho.
Side note: You don't "alias a variable", you "alias a command". alias allows you to substitute the first (and only the first) word in a simple command. php is a command.

I've went with a .bashrc solution. I initially started with this but for some reason the aliases werent being picked up. It appears you need to set a custom expand_aliases setting.
My .bashrc ended up looking like this:
shopt -s expand_aliases;
alias php=/opt/plesk/php/7.3/bin/php;
alias composer=/usr/lib64/plesk-9.0/composer.phar;
This seemed to do the trick and gave me the correct PHP version while using ssh xx#1.2.3.4 "php -v".

Related

Variable containing alias not being expanded correctly

I am trying to automate minikube start process inside WSL2 Debian environment on Windows 10/11.
I have this alias already set inside the .bash_aliases
$ type minikube
minikube is aliased to `"/mnt/c/minikube/minikube.exe"'
and I have this script running on WSL2 start up:
$ cat wsl_minikube_start.sh
shopt -s expand_aliases
alias_location="${HOME}/.bash_aliases"
source "${alias_location}"
ministart="minikube start"
${ministart}
shopt -u expand_aliases
I believe it is supposed to work that way, but it shows the minikube command not found.
$ bash wsl_minikube_start.sh
wsl_minikube_start.sh: line 5: minikube: command not found
(The title was edited to make this easier to find in the future; it was not clear at the time what exactly the problem was.)
Putting a command in a variable is basically always a bad idea, not only because it interferes with alias expansion (basically, aliases are parsed before variable expansions take place). See e.g. https://mywiki.wooledge.org/BashFAQ/050
Aliases are basically always a bad idea, too. Replace your alias with a function instead.
minikube () { "/mnt/c/minikube/minikube.exe" "$#"; }
Or, basically equivalently, create a wrapper script in your PATH with essentially the same contents.
#!/bin/sh
exec "/mnt/c/minikube/minikube.exe" "$#"
Returning to the first topic, it's not clear why you want to encapsulate the simple command minikube start with a variable; but if this really has some purpose, you want to use a function for that, too.
ministart () { minikube start "$#"; }
(The "$#" is only useful if you want to be able to add command-line arguments.)

Need an elegant way to overload a command in a Bash script dynamically created and called by another tool

There have been a lot of posts about how to overload commands in Bash, either by creating an alias or function, or just changing the script itself or how it is called; these methods make perfect sense to me but don't apply to the problem I'm trying to solve.
It is common in the semiconductor industry to work with EDA tools that provide API commands inside a Tcl interpreter; in my case the EDA tool generates a Bash script, then executes it one API call, so I only have access to the script after the script executed and cannot modify how it is called since that happens under the hood; there is also no blessed method for modifying the commands of interest that are written into the script.
What complicates this further is that this script is generated and called by the EDA tool inside a Singularity container; also, the command that is being called resides in a different EDA tool container, so this command 'vsim' must be transformed into an ssh call to our workload manager (slurm). Example:
vsim <args>
Must be converted to:
ssh <some-host> <ssh-options> "srun <srun-options> vsim <args>"
Again, setting an alias or function called 'vsim' in the parent shell didn't work for me; the alias/function was not inherited by the subprocess (this is difficult for me to accept). I also tried creating a symlink called 'vlog' that pointed at a static dispatcher script; this worked by I'd rather not hard code the path to the dispatcher as a file path; I would rather use a variable, like vlog -> .../<resource>/$VERSION/bin/dispatch_for_flows, but symlinks are not dynamic.
I'm thinking that I should still use the symlink approach but instead of pointing at the dispatcher, I'd point at a wrapper that does a single line exec of an interpolated path to the dispatcher, but I'm still not believing that setting an alias didn't solve the problem. For years I wrote Csh scripts with full paths to commands to get around user aliases; with Bash, it looks like aliases are not inherited ever, even with the shopt 'expanand_alias' on in the parent shell or even in /etc/profile. What am I missing here?
Can someone get the example below to work? I'd like the script to inherit the alias from the parent shell and execute without the "command not found" error, without modification to the script or how it is called. Again, I have no access to the script before it is executed and can't control how it is called; bash is the parent shell, bash is the script, and I do have root access on the OS so I could change the version of bash if neccessary (using 4.4.19(1)-release (x86_64-redhat-linux-gnu))
$ alias hello='echo world'
$ hello
world
$ shopt | grep expand_aliases
expand_aliases on
$ cat test.bash
#!/usr/bin/bash
set -x
alias
shopt | grep expand_aliases
shopt -s expand_aliases
shopt | grep expand_aliases
hello
$ ./test.bash
+ alias
+ shopt
+ grep expand_aliases
expand_aliases off
+ shopt -s expand_aliases
+ shopt
+ grep expand_aliases
expand_aliases on
+ hello
./test.bash: line 7: hello: command not found
Can someone get the example below to work?
No that's not possible with aliases - you can't export an alias. But you can export a function.
$ hello() { echo world; }
$ export -f hello
$ ./test.bash
world
You can create a custom command in PATH:
$ printf "%s\n" "#!/bin/bash" "echo world" > /usr/local/bin/hello
$ chmod +x /usr/local/bin/hello
and similarly add a custom command and add new path with the command to PATH and export.

Command NOT found when called from inside bash script

I have an application named puppet installed on my Linux box. It is installed at location /usr/test/bin/puppet
This is how .bash_profile looks
export PATH=/usr/test/bin
if I run command puppet apply from console, it works fine but when I call puppet command from inside bash script, it says command not found
#!/bin/bash
puppet apply x.pp
Any ideas on what is wrong ?
.bash_profile is loaded only if bash is invoked as login shell (bash -l or from a real tty), at least in Debian based distributions bash in a virtual tty (for example when using xterm, gnome-terminal, etc...) is invoked as interactive shell.
Interactive shells loads the configuration from ~/.bashrc.
bash manpage:
~/.bash_profile
The personal initialization file, executed for login shells
~/.bashrc
The individual per-interactive-shell startup file
Shellscripts don't load any of these.
You can check which files are opened by any program with strace:
strace ./s.sh 2>&1 | grep -e stat -e open
Possible solutions:
You can export the variable at the beginning of every script:
#!/bin/bash
export PATH=$PATH:...
Or you can have another file with the desired variables and source it from any script that need those:
/etc/special_vars.sh:
export PATH=$PATH:...
script:
#!/bin/bash
. /etc/special_vars.sh
puppet ...
Configure the PATH in in ~/.bashrc, ~/.bash_profile and ~/.profile for the user running the script (sub-processes will inherit the environment variables) to have some warranty that the user can run the script from different environments and shells (some bourne compatible shells others than bash do load ~/.profile)
Maybe the export of PATH is wrong?
export PATH=$PATH:/usr/test/bin/puppet
You could try using an alias, like so
in your .bash_profile:
alias puppet='bash puppet.fileextension'
you can also do
alias puppet='bash path/to/puppet.fileextension'
which will let you run the script from anywhere in Terminal.
EDIT:
OP has stated in the comments that there will be two different systems running, and he asked how to check the file path to the bash file.
If you do
#!/bin/bash
runPuppet(){
if [ -e path/to/system1/puppet.fileextension]
then
bash path/to/system1/puppet.fileextension $1 $2
elif [ -e path/to/system2/puppet.fileextension]
then
bash path/to/system2/puppet.fileextension $1 $2
fi
}
runPuppet apply x.pp
and change the runPuppet input to whatever you'd like.
To clarify/explain:
-e is to check if the file exists
$1 & $2 are the first two input parameters, respectively.

Badly placed ()'s while declaring a function in tcsh

I'm trying to declare a function in tcsh and to call it.
#! /bin/tcsh -f
helloWorld () {
echo "a"
}
helloWorld
I'm getting the following error:
< 512 mews2895 ~/tmp/script> 1.sh
Badly placed ()'s.
Does anyone here what the problem might be?
Thanks
tcsh does not support functions.
Best solution: Use a shell that does, such as bash.
If you must use tcsh for some reason, aliases will solve your immediate problem, but are much weaker than functions.
alias helloWorld 'echo "a"'
Another possible solution is to invoke a separate script. (You'll have to ensure that the invoked script is in your $PATH.)
There are not functions in tcsh. So I see 2 options:
Use aliases:
https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.2.0/com.ibm.zos.v2r2.bpxa500/alias.htm
Use goto. (People tend to criticize go-to, but It actually depends on the context).
There is an other option, use source if you want to organize your code with multiple files:
To run a shell script in your current environment, without creating a
new process, use the source command. You could run the calculate shell
script this way: source calculate Should you want to use a shell
script that updates a variable in the current environment, run it with
the source command.
src: OS/390 UNIX System Services tcsh (C Shell) Kit Support Guide - IBM
I think that 'use a different shell' should not be a valid response.
Regards,
Pablo
Try below code for functions usage in tcsh
#! /bin/tcsh -f
goto helloWorld
helloWorld:
echo "a"
Although the C Shell lacks functions, aliases serve as workaround. However, pipes and I/O redirection don't work well with multi-line aliases, except if eval is issued. To avoid eval, have the script in a variable and source it from a FIFO:
setenv qscr 'if -e $1 then\
echo OK\
else\
echo Not OK\
endif'
mkfifo ~/qscr
alias qscr '( echo "$qscr:q" > ~/qscr & ) ; source ~/qscr'
Or have it in an alias alone, with echo:
alias qscr '( echo '\''if -e $1 then\\
echo OK\\
else\\
echo Not OK\\
endif'\'' > ~/qscr & ) ; source ~/qscr'
mkfifo ~/qscr

alias in a script

In linux, if I put a command in a script that contains an alias, I see that it doesn't get expanded. How can I fix that? I am using bash.
According the the TLDP page about aliases, you need to use the line shopt -s expand_aliases in your code to expand aliases. The example below produced the expected output, but without the shopt line it just printed "my_ls: command not found":
#!/bin/bash
shopt -s expand_aliases
alias my_ls='ls -lrt'
echo "Trying my_ls"
my_ls
exit
If you want your shell aliases to be available in a script, you must manually include them. If they are defined in ~/.bashrc, put a line
. ~/.bashrc
after the #!/bin/sh line in your script. This will execute the contents of .bashrc in the context of your script.
Enabling posix mode (such as by calling bash as sh or with the command (set -o posix) 2>/dev/null && set -o posix should do the trick.
Even then, be aware that aliases are expanded as they are parsed, and the ordering between parsing and execution is poorly defined. For example
alias foo=echo; foo bar
or
{
alias foo=echo
foo bar
}
will try to run foo as the alias is not defined at parse time yet. Also, some shells parse the whole input of an eval or . (source) before executing any of it.
So the only portable and reliable way to use aliases in scripts is to define them and then eval or . the code that uses them.

Resources