What's the equivalent to ${var:-defaultvalue} in fish? - linux

Hello I am trying to translate my .bashrc to fish format almost done, mostly is clear on the documentation but this part is giving me a headache.. is so my gnupg works with my yubikey ssh etc etc..
The fish version is latest 3.0 under Arch GNU/Linux
original on BASH:
# Set SSH to use gpg-agent
unset SSH_AGENT_PID
if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
export SSH_AUTH_SOCK="/run/user/$UID/gnupg/S.gpg-agent.ssh"
fi
echo "UPDATESTARTUPTTY" | gpg-connect-agent > /dev/null 2&>1
Mine half converted into fish:
set -e SSH_AGENT_PID
if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]
set -x SSH_AUTH_SOCK="/run/user/$UID/gnupg/S.gpg-agent.ssh"
end
echo "UPDATESTARTUPTTY" | gpg-connect-agent > /dev/null 2>&1
so as you see above I have so far converted the stdin and stderror pine and the unset variable with set -e the error I am having is a bit more obscure to me:
~/.config/fish/config.fish (line 33): ${ is not a valid variable in fish.
if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]
^
from sourcing file ~/.config/fish/config.fish
called during startup
Any help will be much appreciated,
BTW will be nice a migrate too :) are there any out there?
[edit] ok got this working thanks to the response below, now all my bash environment, profile, bashrc etc is translated to fish and using it solely as my shell 100%

You should not change your login shell until you have a much better understanding of fish syntax and behavior. For example, in fish the equivalent of $$ is %self or $fish_pid depending on which fish version you are using. You should always specify the version of the program you are having problems with.
Assuming you're using fish 2.x that would be written as
if not set -q gnupg_SSH_AUTH_SOCK_by
or test $gnupg_SSH_AUTH_SOCK_by -ne %self
set -gx SSH_AUTH_SOCK "/run/user/$UID/gnupg/S.gpg-agent.ssh"
end
Also, notice that there is no equal-sign between the var name and value in the set -x.

Since ${var:-value} expands to value if $var is empty, you can always replace it by writing your code out the long way:
begin
if test -n "$gnupg_SSH_AUTH_SOCK_by"
set result "$gnupg_SSH_AUTH_SOCK_by"
else
set result 0
end
if [ "$result" -ne %self ]
set -x SSH_AUTH_SOCK "/run/user/$UID/gnupg/S.gpg-agent.ssh"
end
set -e result
end
Note that I don't use (a) endorse, (b) condone the use of, or (c) fail to hold unwarranted prejudices against users of, fish. Thus, my advice is very much suspect, and it's likely that there are considerably better ways to do this.

I had a similar question, related to XDG_* variables.
var1="${XDG_CACHE_HOME:-$HOME/.cache}"/foo
var2="${XDG_CONFIG_HOME:-$HOME/.config}"/foo
var3="${XDG_DATA_HOME:-$HOME/.local/share}"/foo
some-command "$var1" "$var2" ...
What I found as the best alternative is to simply set univeral variables once for the defaults--
set -U XDG_CACHE_HOME ~/.cache
set -U XDG_CONFIG_HOME ~/.config
set -U XDG_DATA_HOME ~/.local/share
Then in fish config file(s) or scripts, simply use "$XDG_CONFIG_HOME"/.... The value of an exported environment variable will override the universal variable if set, otherwise the universal variable is there as a default/fallback. If the universal variable is used, it is not exported to child processes, while an exported environment variable is, which provides the full equivalent to bash|zsh parameter expansion.

Related

Unset all environment variables in C-Shell

I want to unset all environment variables through C-shell script.
With unsetenv * command I can do that with command line.
How can we make it work in our script?
I am hitting undefined identifier issue when I run through script.
From tcsh(1):
unsetenv pattern
Removes all environment variables whose names match pattern.
`unsetenv *' thus removes all environment variables; this is a
bad idea. It is not an error for nothing to be unsetenved.
So using unsetenv * should work. It is possible you have a (possible old) (t)csh version that doesn't support this feature?
Either way, as the manual page says unsetting everything is probably a bad idea. You will want to keep various variables such as HOME, USER, and some others. A better way might be something like:
foreach e (`env`)
set name = `echo "$e" | cut -d "=" -f 1`
# Note: this list should be longer.
if ( "$name" != "HOME" ) then
unsetenv "$name"
endif
end

Using "read" to set variables

In bash from the CLI I can do:
$ ERR_TYPE=$"OVERLOAD"
$ echo $ERR_TYPE
OVERLOAD
$ read ${ERR_TYPE}_ERROR
1234
$ echo $OVERLOAD_ERROR
1234
This works great to set my variable name dynamically; in a script it doesn't work. I tried:
#!/bin/env bash
ERR_TYPE=("${ERR_TYPE[#]}" "OVERLOAD" "PANIC" "FATAL")
for i in "${ERR_TYPE[#]}"
do
sh -c $(echo ${i}_ERROR=$"1234")
done
echo $OVERLOAD_ERROR # output is blank
# I also tried these:
# ${i}_ERROR=$(echo ${i}_ERROR=$"1234") # command not found
# read ${i}_ERROR=$(echo ${i}_ERROR=$"1234") # it never terminates
How would I set a variable as I do from CLI, but in a script? thanks
When you use dynamic variables names instead of associative arrays, you really need to question your approach.
err_type=("OVERLOAD" "PANIC" "FATAL")
declare -A error
for type in "${err_type[#]}"; do
error[$type]=1234
done
Nevertheless, in bash you'd use declare:
declare "${i}_error=1234"
Your approach fails because you spawn a new shell, passing the command OVERLOAD_ERROR=1234, and then the shell exits. Your current shell is not affected at all.
Get out of the habit of using ALLCAPSVARNAMES. One day you'll write PATH=... and then wonder why your script is broken.
If the variable will hold a number, you can use let.
#!/bin/bash
ERR_TYPE=("OVERLOAD" "PANIC" "FATAL")
j=0
for i in "${ERR_TYPE[#]}"
do
let ${i}_ERROR=1000+j++
done
echo $OVERLOAD_ERROR
echo $PANIC_ERROR
echo $FATAL_ERROR
This outputs:
1000
1001
1002
I'd use eval.
I think this would be considered bad practice though (it had some thing to do with the fact that eval is "evil" because it allows bad input or something):
eval "${i}_ERROR=1234"

Can I find out who called a zsh script?

Assume a script master.sh, which is called as
./foo/bar/master.sh
and contains the lines
#!/bin/zsh
. ./x/y/slave.sh
Is it possible to find out from within slave.sh, that the script which is doing the sourcing, is ./foo/bar/master.sh ?
I can not use $0 here, because this would return ./x/y/slave.sh.
I'm using zsh 5.0.6
one way you can achieve this is that for the child script to take as optional argument the name of the caller. Thus this would be accessible with `$1``
ex:
#!/bin/zsh
# master/leader
. ./x/y/slave.sh $0 # or hardcoded path
#!/bin/zsh
# slave/worker
echo "Here is my master $1"
(you can also do another custom protocol using a environment variable set by the master)
(this solution would also works on bash, and other shell)
The information can already be obtained in zsh right now (thanks to Bart Schaefer, who pointed out to me the existence of the variable functrace in the zsh/parameter module):
#!/bin/zsh
# slave/worker
zmodload zsh/parameter
echo "Here is my master ${functrace[$#functrace]%:*}"
The '%:*' is necessary, because the entries in the functrace array also contain the line number of the call.

Shell script for setting environment variable

I am writing a shell script to set the environment variables whose values are available in a file. Below is the shell script I wrote,
VARIABLE_FILE=env-var.dat
if [ -f ${VARIABLE_FILE} ] ; then
. ${VARIABLE_FILE}
if [ ! -z "${TEST_VAR1}" ] ; then
export TEST_VAR1="${TEST_VAR1}"
fi
if [ ! -z "${TEST_VAR2}" ] ; then
export TEST_VAR2="${TEST_VAR2}"
fi
fi
The above code works only in bash shell, since I have used export command to set the environment variable and it fails if I used it with any other shell. Is there is any command to set the environment variable which works in any shell ?
"Fancier" shells like bash and zsh permit you to set a variable and export it as an environment variable at the same time like so:
export FOO=bar
With a standard POSIX bourne shell, the equivalent is achieved by doing it in two commands:
FOO=bar
export FOO
Note that once you've exported a variable, you can reset it to a different value later in the script and it's still exported (you don't need to export it again). Also, you can export several variables at a time:
FOO=bar
BAZ=quux
export FOO BAZ
You mentioned tcsh in your comment, but csh and derivatives are completely different from bourne-based shells (and not recommended for use!). You can rarely make a shell script compatible with both sh and csh at the same time. For csh, look into setenv
If you really want this to happen, it can be done, but it's tricky. One way to do it is to use awk to output the correct syntax and evaluate the text coming back from awk. To share a single environment variable value file between major sh and csh flavors, the following command in a file will import a variable value file to the environment: (yes, yes, it's one huge line, due to the inflexible way that some shells treat the backticks. If you didn't mind having a .awk file too, you could use awk -f...)
eval `awk '{ var = $1; $1=""; val=substr($0,2); if ( ENVIRON["SHELL"] ~ /csh$/) { print "setenv", var, " \"" val "\";" } else { print var "=\"" val "\"" ; print "export", var }}' $HOME/env_value_file`
The variable value file is in this format:
FOO value for foo
BAR foo bar
BAZ $BAR plus values $FOO
Design notes for educational purposes:
In awk, there's no easy way of accessing fields 2-NF, so if there
could be spaces in our variable values we need to modify $1 to get
$0 to be close to get the value we want.
To get this to work, since a SHELL variable is always set, but not as an
environment variable and not with a consistent capitalization, you have to wet
a SHELL environment variable from the shell's value as below.
as an environment variable before you use the script.
Also, if you want the new environment values to be present after the import
environment script you need to source the environment script.
If a shell doesn't do eval well, you'll have to tweak the script.
For bourne shell flavors (bash, sh, ksh, zsh):
export SHELL
. import_environment
For csh flavors: (shell variable tends to be lower case in csh shells)
setenv SHELL "$shell"
source import_environment

check if environment variable is already set [duplicate]

This question already has answers here:
What's a concise way to check that environment variables are set in a Unix shell script?
(14 answers)
Closed 9 years ago.
I am writing a shell script, where I have to check if environment variable is set, if not set then I have to set it. Is there any way to check in shell script, whether an environment variable is already set or not ?
The standard solution to conditionally assign a variable (whether in the environment or not) is:
: ${VAR=foo}
That will set VAR to the value "foo" only if it is unset.
To set VAR to "foo" if VAR is unset or the empty string, use:
: ${VAR:=foo}
To put VAR in the environment, follow up with:
export VAR
You can also do export VAR=${VAR-foo} or export VAR=${VAR:=foo}, but some older shells do not support the syntax of assignment and export in the same line. Also, DRY; using the name on both sides of the = operator is unnecessary repetition. (A second line exporting the variable violates the same principal, but feels better.)
Note that it is very difficult in general to determine if a variable is in the environment. Parsing the output of env will not work. Consider:
export foo='
VAR=var-value'
env | grep VAR
Nor does it work to spawn a subshell and test:
sh -c 'echo $VAR'
That would indicate the VAR is set in the subshell, which would be an indicator that VAR is in the environment of the current process, but it may simply be that VAR is set in the initialization of the subshell. Functionally, however, the result is the same as if VAR is in the environment. Fortunately, you do not usually care if VAR is in the environment or not. If you need it there, put it there. If you need it out, take it out.
[ -z "$VARIABLE" ] && VARIABLE="abc"
if env | grep -q ^VARIABLE=
then
echo env variable is already exported
else
echo env variable was not exported, but now it is
export VARIABLE
fi
I want to stress that [ -z $VARIABLE ] is not enough, because you can have VARIABLE but it was not exported. That means that it is not an environment variable at all.
What you want to do is native in bash, it is called parameter substitution:
VARIABLE="${VARIABLE:=abc}"
If VARIABLE is not set, right hand side will be equal to abc. Note that the internal operator := may be replaced with :- which tests if VARIABLE is not set or empty.
if [ -z "$VARIABLE" ]; then
VARIABLE=...
fi
This checks if the length of $VARIABLE is zero.

Resources