watch has no access to function defined in .zshrc - terminal

I define a function in my .zshrc like:
function foo {
echo bar
}
I can run this function myself:
$ foo
bar
But I can't use watch to run it:
$ watch foo
sh: 1: foo: not found
This SO answer shows how to do it with Bash:
$ watch -x bash foo
But this doesn't seem to work for zsh.
How can I run my user-defined function with watch?

Try this:
$ watch -x zsh -ic foo
zsh - shell. This is how you get to a zsh function.
-i - interactive. ~/.zshrc is only sourced for interactive shells.
-c - indicates that foo is a command, not a shell script file to read in.
watch -x - have watch use exec instead of sh -c (man page). Reduces the number of processes involved a bit.
If using an interactive shell here causes problems, you can move your function to ~/.zshenv. Then you won't need the -i option, since ~/.zshenv is sourced for both interactive shells and shell scripts.

Shell functions are internal to the shell, so naturally external programs can't use them. One general way to call shell functions in such situations is using zsh -c 'ZSH_CODE_HERE'.

Related

Different environments in different shells

Suppose for the sake of simplicity that I am working with bash and zsh. In bash, I have a .bash_profile that puts ~/bash/bin in the PATH, and in zsh, I have in my .zshrc the path ~/zsh/bin in the PATH. Now, suppose I have two executables at ~/bash/bin/foobar and ~/zsh/bin/foobar. As such, if I run command -v foobar, I should be returning one of the two, depending if I was working in bash or zsh. The question I have is as follows: is it possible to, in a bash script, determine what command -v foobar would output in zsh, or vice-versa?
I'm not confident that
#!/bin/bash
zsh -c 'command -v foobar'
would give me the output of ~/zsh/bin/foobar in this case.
The command
zsh -c 'command -v foobar'
does not process ~/.zshrc , so you can't expect any effect of this file on this command. You could do a
zsh -i -c 'command -v foobar'
to force .zshrc to be processed, but this does not necessarily mean that you would see here a different directory. For instance, assume that in your bash, the PATH is set to /usr/bin:$HOME/bash/bin, and your .zshrc sets the PATH by doing a
PATH=$PATH:$HOME/zsh/bin
In this case, even zsh -i -c .... would still show $HOME/bash/bin/foobar as a match.

Is it possible to print all the commands when they are executed in Bash?

From How to echo shell commands as they are executed I've learned how to use set -v to print commands when they are executed. It works well if set -v is added at the beginning of foo.sh or bash -v foo.sh is run. However, if foo.sh invokes bar.sh by bash bar.sh or simply ./bar.sh, the commands in bar.sh are not printed. Is there a global switch that enables all the commands and functions, through all invoked scripts, to be printed when they are executed?
Is there a global switch that enables all the commands and functions, through all invoked scripts, to be printed when they are executed?
Yes, BASH_ENV can be used for that. The manual describes it as follows.
If this variable is set when Bash is invoked to execute a shell script, its value is expanded and used as the name of a startup file to read before executing the script.
So, you can just put set -v in a file and assign its path to BASH_ENV while invoking the first script. For example:
$ cat env.sh
set -v
$
$ cat foo
#!/bin/bash
true
./bar
$
$ cat bar
#!/bin/bash
false
$ BASH_ENV=./env.sh ./foo
#!/bin/bash
true
./bar
#!/bin/bash
false
Alternatively, to do this without having to create an extra file, you can start the script with a fabricated SHELLOPTS value
# too long, and might require adjustments for future versions
env SHELLOPTS=braceexpand:hashall:interactive-comments:verbose ./foo
or, enable verboseness by the set command or by adding -v to the shebang and export SHELLOPTS within the first script.
set -v
# exports all options, not only `verbose'
export SHELLOPTS
# the rest of the script

How can you hide env variables when executing a bash script with -xe?

I have a bash script that I execute with /bin/sh -xe script.sh and I need to capture its output.
The problem is that inside the script I use some ENV variables that I would like to keep them from being displayed in the output.
Is there any way to do this?
Ie. if my script has touch $MY_ENV_VAR and MY_ENV_VAR=ok the output of /bin/sh -xe script.sh will be touch ok.
Is there any way to keep env vars from being replaced in the output of -x? So I would just get + touch $MY_ENV_VAR back.
There is nothing in the POSIX standard which allows this. All you could do is:
set +o xtrace # Switch xtrace off
some hidden code
set -o xtrace # Switch xtrace back on
Alternatively, use the verbose trace instead of xtrace, that's the -v option.
/bin/sh -ve script.sh
By the way, if you are executing with /bin/sh then it is not a bash script, it is a sh script.

using alias in shell script? [duplicate]

This question already has answers here:
How to use aliases defined in .bashrc in other scripts?
(6 answers)
Closed 2 years ago.
My alias defined in a sample shell script is not working. And I am new to Linux Shell Scripting.
Below is the sample shell file
#!/bin/sh
echo "Setting Sample aliases ..."
alias xyz="cd /home/usr/src/xyz"
echo "Setting done ..."
On executing this script, I can see the echo messages. But if I execute the alias command, I see the below error
xyz: command not found
am I missing something ?
source your script, don't execute it like ./foo.sh or sh foo.sh
If you execute your script like that, it is running in sub-shell, not your current.
source foo.sh
would work for you.
You need to set a specific option to do so, expand_aliases:
shopt -s expand_aliases
Example:
# With option
$ cat a
#!/bin/bash
shopt -s expand_aliases
alias a="echo b"
type a
a
$ ./a
# a is aliased to 'echo b'
b
# Without option
$ cat a
#!/bin/bash
alias a="echo b"
type a
a
$ ./a
./a: line 3: type: a: not found
./a: line 4: a: command not found
reference: https://unix.stackexchange.com/a/1498/27031 and https://askubuntu.com/a/98786/127746
sourcing the script source script.sh
./script.sh will be executed in a sub-shell and the changes made apply only the to sub-shell. Once the command terminates, the sub-shell goes and so do the changes.
OR
HACK: Simply run following command on shell and then execute the script.
alias xyz="cd /home/usr/src/xyz"
./script.sh
To unalias use following on shell prompt
unalias xyz
If you execute it in a script, the alias will be over by the time the script finishes executing.
In case you want it to be permanent:
Your alias is well defined, but you have to store it in ~/.bashrc, not in a shell script.
Add it to that file and then source it with . .bashrc - it will load the file so that alias will be possible to use.
In case you want it to be used just in current session:
Just write it in your console prompt.
$ aa
The program 'aa' is currently not installed. ...
$
$ alias aa="echo hello"
$
$ aa
hello
$
Also: From Kent answer we can see that you can also source it by source your_file. In that case you do not need to use a shell script, just a normal file will make it.
You may use the below command.
shopt -s expand_aliases
source ~/.bashrc
eval $command
Your alias has to be in your .profile file not in your script if you are calling it on the prompt.
If you put an alias in your script then you have to call it within your script.
Source the file is the correct answer when trying to run a script that inside has an alias.
source yourscript.sh
Put your alias in a file call ~/.bash_aliases and then, on many distributions, it will get loaded automatically, no need to manually run the source command to load it.

Difference in bash printf output between run script and source script

I can't seem to find the difference between a script run two different ways.
Here's the script (named test.sh):
#! /bin/bash
printf "%b\n" "\u5A"
When the script is sourced:
. test.sh
> Z ## Result I want ##
When the script is run:
./test.sh
> \u5A ## Result I get ##
I want the run script to give the results of the sourced script... what setting do I need to set/change?
You are probably getting different versions of printf; the script you are sourcing from is probably a /bin/sh script, not a Bash script proper?
Shouldn't you be using \x instead of \u? printf "%b\n" "\x5A" works fine in both cases for me.
(Totally different idea here, so I'm posting it as another answer.)
Try running these at the command line:
builtin printf "%b\n" "\u5A"
/usr/bin/env printf "%b\n" "\u5A"
printf is both a shell builtin and an executable, and you may be getting different ones depending on whether you source or run the script. To find out, insert this in the script and run it each way:
type printf
While you're at it, you may as well insert this line too:
echo $SHELL
That will reveal if you're getting different shells, per tripleee.
HAHA!!! I finally traced down the problem! Read ahead if interested (leave the page if not).
These are the only command that will translate \u properly:
. ./test.sh ## Sourcing the script, hash-bang = #! /bin/sh
. ./test.bash ## Sourcing the script, hash-bang = #! /bin/bash
./test ## Running the script with no hash-bang
All of the following produce identical results in that they do NOT translate \u:
./test.sh ## Script is run from an interactive shell but in a non-interactive shell
## test.sh has first line: #! /bin/sh
/bin/sh -c "./test.sh" ## Running the script in a non-interactive sh shell
/bin/sh -lc "./test.sh" ## Running the script in a non-interactive, login sh shell
/bin/sh -c ". ./test.sh" ## Sourcing the file in a non-interactive sh shell
/bin/sh -lc ". ./test.sh" ## Sourcing the file in a non-interactive, login sh shell
## test.bash has first line: #! /bin/bash
/bin/bash -c "./test.bash" ## Running the script in a non-interactive bash shell
/bin/bash -lc "./test.bash" ## Running the script in a non-interactive, login bash shell
/bin/bash -c ". ./test.bash" ## Sourcing the file in a non-interactive bash shell
/bin/bash -lc ". ./test.bash" ## Sourcing the file in a non-interactive, login bash shell
## And from ***tripleee*** (thanks btw):
/bin/sh --norc; . ./test.sh ## Sourcing from an interactive sh shell without the ~/.bashrc file read
/bin/bash --norc; . ./test.bash ## Sourcing from an interactive bash shell without the ~/.bashrc file read
The only way to get proper translation is to run the script without a hash-bang... and I finally figured out why! Without a hash-bang my system chooses the default shell, which btw is NOT /bin/bash... it turns out to be /opt/local/bin/bash... two different versions of bash!
Finally, I removed the OSX /bin/bash [v3.2.48(1)] and replaced it with the MacPorts /opt/local/bin/bash [v4.2.10(2)] and now running the script works! It actually solved about 10-15 other problems I've had (like ${var,,}, read sN1 char, complete -EC "echo ' '", and a host of other commands I have scattered throughout my scripts, ~/.bashrc amd ~/.profile). Honestly, I really should have noticed when my scripts using associative arrays suddenly crapped out on me... how stupid can I get!?
I've been using bash v4 for a looong time now, and my Lion upgrade went and down-graded bash back to v3 (get with the program Apple!)... ugh, I feel so ashamed! Everyone still using bash v3, upgrade!! bash v4 is has many, many beautiful upgrades over version 3. Type bash --version to see what version you are running. One advantage is now bash can translate \uHEX into Unicode!
Try removing the space in the first line, I seem to recall that can cause problems. Offhand I'd guess that because of that space, you're not getting bash, but sh.
Glad you solved it. Still, you might be looking for a portable solution.
Assuming you are always using the same formatting string, we can just discard it, and use something like this;
printf () {
# Discard format string
shift
perl -CSD -le '
print map { s/^\\u//; chr(hex($_)) } #ARGV' "$#"
}
Edit to add: You would simply add this function definition at the beginning of your existing script, overriding the builtin printf. Obviously, if you also use printf for other stuff, this special-purpose replacement isn't good enough.
You could rename the function to uprintf or something, still. It merely translates a sequence of hex codes to the corresponding Unicode characters, discarding any \u prefix.

Resources