TCSH, what does this line do? Trying to port to BASH - bash

I'm trying to update some scripts from tcsh to bash to reflect the bash preference of some users. Needless to say, I don't know csh. Can someone tell me what this line does?
alias prepend 'if (-d \!:2) if ("$\!:1" \!~ *"\!:2"*) export \!:1 "\!:2":${\!:1}'

That probably prepends a directory onto a variable if said directory doesn't already exist in said variable.
Here's what it says, in English: if the second argument is a directory, then if the first argument interpreted as a variable does not contain the text of the second argument, then "export" the string "second argument colon contents of first argument" into the first argument.
It all depends upon what export does. In my experience, export is an alias for setenv, but that is not guaranteed.
I would suggest a test to see if this does what I think. First, echo your path. Then run prepend PATH /a/new/directory/that/exists. Then echo your path again. If you see that "/a/new/directory/that/exists" is now in your path variable, then you can be reasonably sure export is an alias for setenv.
Finally, this SO post lists strategies to implement a similar thing in other languages and shells.

Related

Bash script ignores positional arguments after first time used

I noticed that my script was ignoring my positional arguments in old terminal tabs, but working on recently created ones, so I decided to reduce it to the following:
TAG=test
while getopts 't:' c
do
case $c in
t)
TAG=$OPTARG
;;
esac
done
echo $TAG
And running the script I have:
~ source my_script
test
~ source my_script -t "test2"
test2
~ source my_script -t "test2"
test
I thought it could be that c was an special used variable elsewhere but after changing it to other names I had the exact same problem. I also tried adding a .sh extension to the file to see it that was a problem, but nothing worked.
Am I doing something wrong ? And why does it work the first time, but not the subsequent attempts ?
I am on MacOS and I use zsh.
Thank you very much.
The problem is that you're using source to run the script (the . command does the same thing). This makes it run in your current (interactive) shell (rather than a subprocess, like scripts normally do). This means it uses the same variables as the current shell, which is necessary if you want it to change those variables, but it can also have weird effects if you're not careful.
In this case, the problem is that getopts uses the variable OPTIND to keep track of where it is in the argument list (so it doesn't process the same argument twice). The first time you run the script with -t test2, getopts processes those arguments, and leaves OPTIND set to 3 (meaning that it's already done the first two arguments, "-t" and "test2". The second time you run it with options, it sees that OPTIND is set to 3, so it thinks it's already processed both arguments and just exits the loop.
One option is to add unset OPTIND before the while getopts loop, to reset the count and make it start from the beginning each time.
But unless there's some reason for this script to run in the current shell, it'd be better to make it a standard shell script and have it run as a subprocess. To do this:
Add a "shebang" line as the first line of the script. To make the script run in bash, that'd be either #!/bin/bash or #!/usr/bin/env bash. For zsh, use #!/bin/zsh or #!/usr/bin/env zsh. Since the script runs in a separate shell process, the you can run bash scripts from zsh or zsh scripts from bash, or whatever.
Add execute permission to the script file with chmod -x my_script (or whatever the file's actual name is).
Run the script with ./my_script (note the lack of a space between . and /), or by giving the full path to the script, or by putting the script in some directory in your PATH (the directories that're automatically searched for commands) and just running my_script. Do NOT run it with the bash, sh, zsh etc commands; these override the shebang and therefore can cause confusion.
Note: adding ".sh" to the filename is not recommended; it does nothing useful, and makes the script less convenient to run since you have to type in the extension every time you run it.
Also, a couple of recommendations: there are a bunch of all-caps variable names with special meanings (like PATH and OPTIND), so unless you want one of those special meanings, it's best to use lower- or mixed-case variable names (e.g. tag instead of TAG). Also, double-quoting variable references (e.g. echo "$tag" instead of echo $tag) avoids a lot of weird parsing headaches. Run your scripts through shellcheck.net; it's good at spotting common mistakes like this.

expr command not found? Why is expr not found but everything else is?

I have spent several hours trying to make the following piece of code work
PATH="C:\Ben\MyPictures"
echo $PATH
MY=`expr 2 + 2`
but this will not work because "expr: command not found". The only thing I've dug on StackOverflow are pathing issue (I.E. set my environment variable), but if that's the problem, why would other functions like echo, let, and declare already work fine?
For more context, this is on a near-fresh installation of window's cygwin. My question is why can't I find expr?
You have modified your PATH to have only 1 directory(therefore it cant find expr). You must append your new path to PATH and not replace existing PATH values, like this:
export PATH="$PATH:C:\Ben\MyPictures"
Also instead of calling an external process expr for calculation you can use the bash's builtin arithmetic evaluation:
$ echo $((2+2))
4
Edit:
Yes those would work because they are not executable files found from directories listed in $PATH.
Instead they(echo, type etc) are functionality provided by the bash shell itself called shell built-ins.
Type out type echo and type expr to know what type of command is it(alias/shell builtin/executable file etc.)
Shell built-ins help can be usually found out by help shellBuiltin where as we use man pages for executable files.
PS: type itself is a shell built-in(see type type)
The path variable PATH is used to find the location of standard binaries that ship with your environment. In fact system environment variables are usually capitalized( for example HOME ).
In your command :
PATH="C:\Ben\MyPictures"
You have accidentally(or intentionally?) replaced the standard paths to the standard binaries by C:\Ben\MyPictures so that command interpreter, bash here, can no longer find expr. When you use capitalized variables,say PATH, you could do:
if [ -z "$PATH" ] #check if a variable is empty
then
PATH="C:\Ben\MyPictures"
else
PATH="$PATH:C:\Ben\MyPictures"
fi
The better option indeed is to use small letters for user defined variables :
path="C:\Ben\MyPictures"
#in this case you should not get an error for expr but
# I am not sure how a Linux-Like environment handle character case.
# check that out.

'which' command is incorrect

I have a shell script in my home directory called "echo". I added my home directory to my path, so that this echo would replace the other one.
To do this, I used: export PATH=/home/me:$PATH
When I do which echo, it shows the one I want. /home/me/echo
But when I actually do something like echo asdf it uses the system echo.
Am I doing something wrong?
which is an external command, so it doesn't have access to your current shell's built-in commands, functions, or aliases. In fact, at least on my system, /usr/bin/which is a shell script, so you can examine it and see how it works.
If you want to know how your shell will interpret a command, use type rather than which. If you're using bash, type -a will print all possible meanings in order of precedence. Consult your shell's documentation for details.
For most shells, built-in commands take precedence over commands in your $PATH. The whole point of having a built-in echo, for example, is that it's faster than loading /bin/echo into memory.
If you want your own echo command to override the shell's built-in echo, you can define it as a shell function.
On the other hand, overriding the built-in echo command doesn't strike me as a good idea in the first place. If it behaves the same as the built-in echo, there's not much point. If it doesn't, then it could break scripts that use echo expecting it to work a certain way. If possible, I suggest giving your command a different way. If it's an enhanced version of echo, you could even call it Echo.
It is likely using the shell's builtin.
If you want the one in your path you can do
`which echo` asdf
From this little article that explains the rules, here's a list in descending order of precedence:
Aliases
Shell functions
Shell builtin commands
Hash tables
PATH variable
echo is a shell builtin command (al least in bash) and PATH has the lowest priority. I guess you'll need to create a function or an alias.

Prevent bash alias from evaluating statement at shell start

Say I have the following alias.
alias pwd_alias='echo `pwd`'
This alias is not "dynamic". It evaluates pwd as soon as the shell starts. Is there anyway to delay the evaluation of the expression in the ticks until the alias's runtime?
What you really want is a function, instead of an alias.
pwd_alias() {
echo "$PWD"
}
Aliases do nothing more than replace text. Anything with complexity calls for a function.
As jordanm said, aliases do nothing more than replace text.
If you want the argument of echo to be the output of pwd expanded by bash, then I don't understand your question.
If you want the argument of echo to be `pwd` with the backquotes kept, it's indeed possible, for example:
alias a="echo '\`pwd\`'"
So, if instead of echo you have something which does backquote expansion in its own runtime, maybe that's what you want.
I do not believe you can change the evaluation from occurring at shell start. Since the processes of creating the alias is run at shell start the pwd is evaluated then. You could simple change the alias to just run pwd without the back ticks as pwd outputs without the need to echo. A simple way to resolve this is to change from using an alias to a shell script in your path if you do not wish to change from using an alias.
#!/bin/bash
pwd

How to run my own programm using command in Shell?

I just learned that I could use chmod make myscript.sh executable and the run it as $ ./myscript.sh But how can I attach a custom command to it, like $ connectme [options] ?
You need to do two things:
Give the name you want to use. Either just rename it, or establish a link (hard or symbolic). Make sure the correctly named object has the right permissions.
Make sure it is in you path. But putting "." in you PATH is a bad idea (tm), so copy it to $HOME/bin, and put that in you path.
A completely different approach. Most shells support aliases. You could define one to run your script.
Note: The environment variable PATH tells the shell where to look for programs to run (unless you specify a fully qualified path like /home/jdoe/scripts/myscript.sh or ./myscript.sh), it consists of a ":" seperated list of directories to examine. You can check yours with:
$ printenv PATH
resulting for me in
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/X11/bin:/usr/X11R6/bin
which are the usual directories for binaries. You can add a new path element with (in /bin/sh and derivatives):
$ export PATH=$PATH:$HOME/bin
in csh and derivatives use
$ setenv PATH $PATH:$HOME/bin
either of which which will result in the shell also searching ~/bin for things to run. Then move your script into that directory (giving ta new name if you want). Check that you execute permissions for the script, and just type its name like any other command.
Fianlly, the use of a ".sh" extension to denote a shell script is for human consumption only. Unix does not care about how you name your script: it is the so-called "shebang" ("#!") on the first line of the script that the OS uses to find the interpreter.
You need to learn about arguments in BASH PROGRAMMING. Here is a good tutorial on them. Check section #4 out.
Basically, you need to use special variables $1, $2, $3 to refer to first, second and third command line arguments respectively.
Example:
$ ./mycript.sh A-Rod
With myscript.sh being:
#!/bin/bash
echo "Hello $1"
Will print:
Hello A-Rod

Resources