How much can I chain bash commands? - zshrc

Very quick question. I have put together a few aliases to make these tedious bluetooth gymnastics that Apple has forced down our throats over the past few years a lot easier. My question is in the following bash aliases may I chain multiple "&&'s or &'s" together so that I don't have to make multiple aliases in my .zshrc file? For instance this is what I currently have:
alias sonosconnect="bluetoothconnector --connect 54-2a-1b-bf-2c-dc && switchaudiosource -s SonosRoam"
alias sonos="blueutil -p 1 && sonosconnect"
alias bton="blueutil -p 1"
My end result would essentially be combining these 3 aliases into one long alias, is this possible by using more than one instance of an &&?

You shouldn't be using aliases at all. Use functions instead.
sonosconnect () {
bluetoothconnector --connect 54-2a-1b-bf-2c-dc && switchaudiosource -s SonosRoam
}
sonos () {
blueutil -p 1 && sonosconnect
}
bton () {
blueutil -p 1
}
some_name_for_all () {
sonosconnect
sonos
bton
}
There's no significant limit on how long the command line can be, but there are very few situations where an alias is a better choice than function, and many cases where a function is the better or even only appropriate choice.

Related

Unix bash dynamically add function using variable in its name

Is it possible to define a function but use a variable to compose its name?
_internal_add_deployment_aliases(){
environment=$1
instanceNumber=$2
shortName=$3
instance="${environment}${instanceNumber}"
ipVar="_ip_$instance"
ip=${!ipVar}
# lots of useful aliases
_full_build_${instance}(){ # how do i dynamically define a function using a variable in its name
#something useful
}
}
Context: I'd like to add bunch of aliases and convenience functions to work with my cloud instances, defining aliases is not a problem, I can easily do
alias _ssh_into_${instance}="ssh -i \"${KEY}\" root#$ip"
and I want to have specific aliases defined when I source from this...
Now when i want to do the same for functions i have a problem, is it possible to do this?
Any help is very very much appreciated :)
You need to use eval for such a problem:
$ var=tmp
$ eval "function echo_${var} {
echo 'tmp'
}"
$ echo_tmp
tmp
An example of your script:
#! /bin/bash
_internal_add_deployment_aliases(){
environment=$1
instanceNumber=$2
shortName=$3
instance="${environment}${instanceNumber}"
ipVar="_ip_$instance"
ip=${!ipVar}
# lots of useful aliases
eval "_full_build_${instance}(){
echo _full_build_${instance}
}"
}
_internal_add_deployment_aliases "stackoverflow" 3 so
_full_build_stackoverflow3 # Will echo _full_build_stackoverflow3
exit 0

Difference when executing bash function in an alias

I have a function in my .bash_profile for printing text some pre-written text and copying it to the clipboard.
copyandprint () {
s='\\033[1;32m' #strong
n='\\033[0m' #normal
printf -- "printf -- '$1' | pbcopy;" #pbcopy copies to clipboard in macOS
printf -- "echo -e copied '${s}$1${n}' to clipboard"
}
I use this to alias things I keep wanting to paste into other applications, like static IDs, or just silly things that are difficult to type quickly on a keyboard.
alias shrug=$( copyandprint '¯\_(ツ)_/¯')
But when I wanted to use it with text generated at the time I use the alias, I can't just call it in the alias definition; the alias needs to call it.
alias copydate=$( copyandprint "$(date)" )
The value is generated when the script is run, not when the alias is used.
Through pretty much sheer trial and error, I was able to make a modified version of the function that does what I wanted:
copyandprint_live () {
s='\\033[1;32m' #strong
n='\\033[0m' #normal
printf -- "$1" | pbcopy
printf -- "echo -e copied ${s}$1${n} to clipboard"
}
alias copydate_live="\$( copyandprint_live \"\$(date)\" )"
The date is generated at the time the alias is used, rather than at the time the script is executed.
But when I use that function the way I used the other one, it fails:
alias shrug_2=$( copyandprint_live '¯\_(ツ)_/¯')
$ shrug_2
#=> -bash: syntax error near unexpected token `ツ'
And I tried putting double quotes, but that didn't work
alias shrug_3=$( copyandprint_live '"¯\_(ツ)_/¯"')
$ shrug_3
#=> copied 033[1
#=> -bash: 32m¯\_(ツ)_/¯033[0m: No such file or directory
My question is, what's going on here? Why do they need to be so different?
Dispensing with the aliases and using functions makes this a lot easier.
copyandprint () {
printf '%s' "$1" | pbcopy
printf 'copied \033[1;32m%s\033[0m to clipboard\n' "$1"
}
shrug () {
copyandprint '¯\_(ツ)_/¯'
}
copydate () {
copyandprint "$(date)"
}
Functions work alike any other command:
$ foo () { echo hi; }
$ foo
hi
You're calling the function when you define the aliases, not when you use them. You need to put the alias definition in single quotes to prevent $(...) from executing the command at that time.
alias shrug='$( copyandprint "¯\_(ツ)_/¯")'

How to define and declare a function name with dynamic variable in it in bash scripting

CODE with details
#!/usr/bin/bash -xv
FUNCTION_DYNAMIC
eval "function APP_$i_$j
{
`enter code here`
}"
DEFINING_FUNC_TO_CALCULATE_VALUE
APP_VAR_MKT()
{
for i in `cat ${SERVER}`
do
for j in `cat ${ZONE}`
do
shopt -s expand_aliases
alias name="APP_${i}_${j}"
declare -fp "APP_${i}_${j}"
done
done
}
MAIN
SERVER_NAME=/path/servers_file
ZONE=/path/zones_file
DECLARING FUNCTION with variable in it
APP_VAR_MKT
You don't; you pass that information as arguments:
app () {
server_name=$1
zone=$2
# ...
}
app "$SERVER_NAME" "$ZONE"
Disclaimer: Declaring functions dynamically is not the approach you should use. See chepner's answer, that is definitely the preferred way!
However, if you really want to create the name dynamically, here is another way to do it, that is a little less problematic than eval:
#!/usr/bin/env bash
SERVER_NAME=foo
ZONE=bar
shopt -s expand_aliases
alias name="APP_${SERVER_NAME}_$ZONE"
name() {
echo hello
}
declare -fp "APP_${SERVER_NAME}_${ZONE}"
The output of declare shows that APP_foo_bar has been declared:
APP_foo_bar ()
{
echo hello
}
Now, this works to some degree. You have to be very cautious if the input is not under your control. This can be potentially dangerous:
#!/usr/bin/env bash
SERVER_NAME='foo() { echo hi; }; echo ouch;'
ZONE=bar
shopt -s expand_aliases
alias name="APP_${SERVER_NAME}_$ZONE"
name() {
echo hello
}
declare -fp APP_foo
declare -fp _bar
When the right alias is used, this approach can be used to execute arbitrary code. The output of this script is:
ouch
APP_foo ()
{
echo hi
}
_bar ()
{
echo hello
}
Not only were the wrong functions declared, echo ouch got executed! Now imagine if I used rm -rf *. Using eval presents the exact same problem.
Conclusion: Don't do it :)
You should not do this, unless you have a good reason for it - functions are reusable encapsulations of code, and their names should not change normally. Also you should not use eval because it is very dangerous. So be warned.
What you can do if you absolutely must is use eval:
#!/bin/bash
eval "function APP_${SERVER_NAME}_${ZONE}
{
echo 'XXX'
}"
APP_${SERVER_NAME}_${ZONE}
The result:
XXX
As others have said, it is not a good idea to generate function (or variable) names dynamically, instead you can use an associative array in a structure sometimes called a despatch table.
The idea is that the keys of the associative array (sometimes called a 'hash', 'hash table', or dictionary) hold the names of functions. When you need a particular function you just call it. Here is a simple example:
# Statically declare each function
func1() {
echo "This is func1"
}
func2() {
echo "This is func2"
}
# Declare the array as associative
declare -A lookup
# Setup the association of dynamic name with function
lookup[APP_fred_CBD]='func1'
lookup[APP_jim_ABCD]='func2'
SERVER_NAME='fred'
ZONE='CBD'
${lookup[APP_${SERVER_NAME}_${ZONE}]}
SERVER_NAME='jim'
ZONE='ABCD'
${lookup[APP_${SERVER_NAME}_${ZONE}]}
Gives:
This is func1
This is func2
If you application does not require unique functions, you can use the same function for more than one key, and pass parameters.

The same result for different parameters

I have a strange situation. For different parameters I always get the same result
function test
{
while getopts 'c:S:T:' opt ; do
case "$opt" in
c) STATEMENT=$OPTARG;;
S) SCHEMA=$OPTARG;;
T) TABLE=$OPTARG;;
esac
done
echo "$STATEMENT, $SCHEMA, $TABLE"
}
test -c CREATE -S schema1 -T tabela1
test -c TRUNCATE -S schema2 -T tabela2
test -c DROP -S schema3 -T tabela3
Result:
CREATE, schema1, tabela1
CREATE, schema1, tabela1
CREATE, schema1, tabela1
What is failed in my script?
In bash, you need to localize the $OPTIND variable.
function test () {
local OPTIND
Otherwise it's global and the next call to getopts returns false (i.e. all arguments processed). Consider localizing the other variables, too, if they're not used outside of the function.
You can also just set it to zero.

Passing argument to alias in bash [duplicate]

This question already has answers here:
Make a Bash alias that takes a parameter?
(24 answers)
Closed 5 years ago.
Is it possible to do the following:
I want to run the following:
mongodb bin/mongod
In my bash_profile I have
alias = "./path/to/mongodb/$1"
An alias will expand to the string it represents. Anything after the alias will appear after its expansion without needing to be or able to be passed as explicit arguments (e.g. $1).
$ alias foo='/path/to/bar'
$ foo some args
will get expanded to
$ /path/to/bar some args
If you want to use explicit arguments, you'll need to use a function
$ foo () { /path/to/bar "$#" fixed args; }
$ foo abc 123
will be executed as if you had done
$ /path/to/bar abc 123 fixed args
To undefine an alias:
unalias foo
To undefine a function:
unset -f foo
To see the type and definition (for each defined alias, keyword, function, builtin or executable file):
type -a foo
Or type only (for the highest precedence occurrence):
type -t foo
to use parameters in aliases, i use this method:
alias myalias='function __myalias() { echo "Hello $*"; unset -f __myalias; }; __myalias'
its a self-destructive function wrapped in an alias, so it pretty much is the best of both worlds, and doesnt take up an extra line(s) in your definitions... which i hate, oh yeah and if you need that return value, you'll have to store it before calling unset, and then return the value using the "return" keyword in that self destructive function there:
alias myalias='function __myalias() { echo "Hello $*"; myresult=$?; unset -f __myalias; return $myresult; }; __myalias'
so..
you could, if you need to have that variable in there
alias mongodb='function __mongodb() { ./path/to/mongodb/$1; unset -f __mongodb; }; __mongodb'
of course...
alias mongodb='./path/to/mongodb/'
would actually do the same thing without the need for parameters, but like i said, if you wanted or needed them for some reason (for example, you needed $2 instead of $1), you would need to use a wrapper like that. If it is bigger than one line you might consider just writing a function outright since it would become more of an eyesore as it grew larger. Functions are great since you get all the perks that functions give (see completion, traps, bind, etc for the goodies that functions can provide, in the bash manpage).
I hope that helps you out :)
Usually when I want to pass arguments to an alias in Bash, I use a combination of an alias and a function like this, for instance:
function __t2d {
if [ "$1x" != 'x' ]; then
date -d "#$1"
fi
}
alias t2d='__t2d'
This is the solution which can avoid using function:
alias addone='{ num=$(cat -); echo "input: $num"; echo "result:$(($num+1))"; }<<<'
test result
addone 200
input: 200
result:201
In csh (as opposed to bash) you can do exactly what you want.
alias print 'lpr \!^ -Pps5'
print memo.txt
The notation \!^ causes the argument to be inserted in the command at this point.
The ! character is preceeded by a \ to prevent it being interpreted as a history command.
You can also pass multiple arguments:
alias print 'lpr \!* -Pps5'
print part1.ps glossary.ps figure.ps
(Examples taken from http://unixhelp.ed.ac.uk/shell/alias_csh2.1.html .)
To simplify leed25d's answer, use a combination of an alias and a function. For example:
function __GetIt {
cp ./path/to/stuff/$* .
}
alias GetIt='__GetIt'

Resources