In my command-line, why does echo $0 return "-"? - linux

When I type echo $0 I see -
I expect to see bash or some filename, what does it mean if I just get a "-"?

A hyphen in front of $0 means that this program is a login shell.
note: $0 does not always contain accurate path to the running executable as there is a way to override it when calling execve(2).

I get '-bash', a few weeks ago, I played with modifying a process name visible when you run ps or top/htop or echo $0. To answer you question directly, I don't think it means anything. Echo is a built-in function of bash, so when it checks the arguments list, bash is actually doing the checking, and seeing itself there.
Your intuition is correct, if you wrote echo $0 in a script file, and ran that, you would see the script's filename.

So based on one of your comments, you're really want to know how to determine what shell you're running; you assumed $0 was the solution, and asked about that, but as you've seen $0 won't reliably tell you what you need to know.
If you're running bash, then several unexported variables will be set, including $BASH_VERSION. If you're running tcsh, then the shell variables $tcsh and $version will be set. (Note that $version is an excessively generic name; I've run into problems where some system-wide startup script sets it and clobbers the tcsh-specific variable. But $tcsh should be reliable.)
The real problem, though, is that bash and tcsh syntax are mostly incompatible. It might be possible to write a script that can execute when invoked (via . or source) from either tcsh or bash, but it would be difficult and ugly.
The usual approach is to have separate setup files, one for each shell you use. For example, if you're running bash you might run
. ~/setup.bash
or
. ~/setup.sh
and if you're running tcsh you might run
source ~/setup.tcsh
or
source ~/setup.csh
The .sh or .csh versions refer to the ancestors of both shells; it makes sense to use those suffixes if you're not using any bash-specific or tcsh-specific features.
But that requires knowing which shell you're running.
You could probably set up an alias in your .cshrc, .tcshrc, or.login, and an alias or function in your.profile,.bash_profile, or.bashrc` that will invoke whichever script you need.
Or if you want to do the setup every time you login, or every time you start a new interactive shell, you can put the commands directly in the appropriate shell startup file(s). Of course the commands will be different for tcsh vs. bash.

Related

Using Bash environment variables from within a Perl script?

I am trying to run a Bash command from within my Perl program.
However Perl seems to be confusing my Bash $PWD environment variable as a Perl variable.
How can I make it just read it all as a string?
This is what I'm trying to run
my $path = /first/path;
`ln -s $path $PWD/second/path`
Those backticks runs the second line in Bash. Using System() produces the same problem.
Any ideas?
There are two queries here, on use of Bash variables and on running external commands.
There is the %ENV hash in Perl, with environment variables
perl -wE'say $ENV{PWD}'
However, you are often better off getting the equivalent within the script, as things may have a subtly different meaning for the script or change as the script runs.
More importantly, using shell commands exposes you to all kinds of potential problems with quoting, shell injection, and interpretation. For instance, the command you show is dangerous, as outlined in Charles Duffy comment. It is in principle better to use Perl's rich functionality. See for example
Executing system commands safely while coding in Perl
Using system commands in Perl instead of built in
libraries/functions [duplicate]
for a sober, and detailed, account of advantages.
In case you do need to run external commands, it is best to avoid the shell altogether, for example by using the multi-argument form of system. If you need the output of the command as well there are various modules in Perl that provide that. See links below.
If you also need to use the shell's capabilities, instead of quoting everything just right in order for the shell to receive what it needs better use a ready tool like String::ShellQuote.
Some examples:
How to use both pipes and prevent shell expansion in perl system function?
Perl is respecting '<' as a regular character rather an output redirection
How to pipe the content of a variable as STDIN in a qx{} statement in Perl?
Perl system command with multiple parameters output to file.
Note that qx operator (backticks) uses /bin/sh, which may or may not get relegated to Bash. So if you want Bash you need system('/bin/bash', '-c', $cmd), where $cmd need be built carefully to avoid problems. See the links with examples.
Here is a full example related to the objective behind the question.
Your program's working directory may be other than expected depending on how it's started. For one, it changes after chdir. I don't know your exact intent with PWD, but in Perl there are core Cwd::cwd and FindBin with $RealBin, for the current working directory and for the directory where the script resides (generally different things).
To create a symbolic link to $path, with the relative path following the current working directory
use warnings;
use strict;
use Cwd qw(cwd);
my $cwd = cwd;
my $path = '/first/path';
symlink($path, "$cwd/second/path") or die "Can't make a symlink: $!";
If the path is meant to be the script's location use $RealBin from FindBin instead of cwd.
Note that with symlink you cannot pass a directory instead of a link name. See this page.

Checking whether a program exists

In the middle of my perl script I want to execute a bash command. The script takes a long time, so at the beginning of the script I want to see if the command exists. This answer says to just try and run it and this other answer suggests some bash commands to test if the program exists.
Is the latter option the best solution? Are there any better ways to do this check in perl?
My best guess is that you want to check for existence of an executable file that you want to run using system or qx//
But if you want your command line to behave the same way as the shell, then you can probably use File::Which
What if we assume that we don't know the command's location?
This means that syck's answer won't work, and zdim's answer is incomplete.
Try this function in perl:
sub check_exists_command {
my $check = `sh -c 'command -v $_[0]'`;
return $check;
}
# two examples
check_exists_command 'pgrep' or die "$0 requires pgrep";
check_exists_command 'readlink' or die "$0 requires readlink";
I just tested it, because I just wrote it.
With perl, you can test files for existence, readability, executability etc., take a look here.
Therefore just use
executeBashStuff() if -x $filename;
or stat it:
stat($filename);
executeBashStuff() if -x _;
To me a better check is to run the program at the beginning of the script (with -V say).
I'd use the same invocation as you use to run the job later (via shell or not, via execvp). Once at it, make sure to see whether it threw errors. This is also discussed in your link but I would in fact get the output back (not send it away) and check that. This is the surest way to see whether the thing actually runs out of your program and whether it is what you expect it to be.
Checking for the executable with -x (if you know the path) is useful, too, but it only tells you that a file with a given name is there and that it is executable.
The system's which seems to be beset with critism for its possible (mis)behavior, it may or may not be a shell-builtin (which complicates how exactly to use it), is an external utility, and its exact behavior is system dependent. The module File::Which pointed out in Borodin's answer would be better -- if it is indeed better than which. (What it may well be, I just don't know.)
Note. I am not sure what "bash command" means: a bash shell built-in, or the fact that you use bash when on terminal? Perl's qx and system use the sh shell, not bash (if they invoke the shell, which depends on how you use them). While sh is mostly a link, and often to bash, it may not be and there are differences, and you cannot rely on your shell configuration.
Can also actually run a shell, qx(/path/bash -c 'cmd args'), if you must. Mind the quotes. You may need to play with it to find the exact syntax on your system. See this page and links.

Dry-run a potentially dangerous script?

A predecessor of mine installed a crappy piece of software on an old machine (running Linux) which I've inherited. Said crappy piece of software installed flotsam all over the place, and also is sufficiently bloated that I want it off ASAP -- it no longer has any functional purpose since we've moved on to better software.
Vendor provided an uninstall script. Not trusting the crappy piece of software, I opened the uninstall script in an editor (a 200+ line Bash monster), and it starts off something like this:
SWROOT=`cat /etc/vendor/path.conf`
...
rm -rf $SWROOT/bin
...
It turns out that /etc/vendor/path.conf is missing. Don't know why, don't know how, but it is. If I had run this lovely little script, it would have deleted the /bin folder, which would have had rather amusing implications. Of course this script required root to run!
I've dealt with this issue by just manually running all the install commands (guh) where sensible. This kind of sucked because I had to interpolate all the commands manually. In general, is there some sort of way I can "dry run" a script to have it dump out all the commands it would execute, without it actually executing them?
bash does not offer dry-run functionality (and neither do ksh, zsh, or any other shell I know).
It seems to me that offering such a feature in a shell would be next to impossible: state changes would have to be simulated and any command invoked - whether built in or external - would have to be aware of these simulations.
The closest thing that bash, ksh, and zsh offer is the ability to syntax-check a script without executing it, via option -n:
bash -n someScript # syntax-check a script, without executing it.
If there are no syntax errors, there will be no output, and the exit code will be 0.
If there are syntax errors, analysis will stop at the first error, an error message including the line number is written to stderr, and the exit code will be:
2 in bash
3 in ksh
1 in zsh
Separately, bash, ksh, and zsh offer debugging options:
-v to print each raw source code line[1]
to stderr before it is executed.
-x to print each expanded simple command to stderr before it is executed (env. var. PS4 allows tweaking the output format).
Combining -n with -v and/or -x offers little benefit:
With -n specified, -x has no effect at all, because nothing is being executed.
With -n specified, -v will effectively simply print the source code.
If there is a syntax error, there may be benefit in the source code getting print up to the point where the error occurs; keep in mind, though that the error message produced by
-n always includes the offending line number.
[1] Typically, it is individual lines that are printed, but the true unit is however many lines a given command - which may be a compound command such as while or a command list (such as a pipeline) - spans.
You could try running the script under Kornshell. When you execute a script with ksh -D, it reads the commands and checks them for syntax, but doesn't execute them. Combine that with set -xv, and you'll print out the commands that will be executed.
You can also use set -n for the same effect. Kornshell and BASH are fairly compatible with each other. If it's a pure Bourne shell script, both Kornshell and BASH will execute it pretty much the same.
You can also run ksh -u which will cause unset shell variables to cause the script to fail. However, that wouldn't have caught the catless cat of a nonexistent file. In that case, the shell variable was set. It was set to null.
Of course, you could run the script under a restricted shell too, but that's probably not going to uninstall the package.
That's the best you can probably do.

Determine interpreter from inside script

I have a script; it needs to use bash's associative arrays (trust me on that one).
It needs to run on normal machines, as well as a certain additional machine that has /bin/bash 3.2.
It works fine if I declare the interpreter to be /opt/userwriteablefolder/bin/bash4, the location of bash 4.2 that I put there.. but it then only works on that machine.
I would like to have a test at the beginning of my script that checks what the interpreting shell is, and if it's bash3.2, calls bash4 $0 $#. The problem is that I can't figure out any way to determine what the interpreting shell is. I would really rather not do a $HOSTNAME based decision, but that will work if necessary (It's also awkward, because it needs to pass a "we've done this already" flag).
For a couple reasons, "Just have two scripts" is not a good solution.
You can check which interpreter is used by looking at $SHELL, which contains the full path to the shell executable (ex. /bin/bash)
Then, if it is Bash, you can check the Bash version in various ways:
${BASH_VERSINFO[*]} -- an array of version components, e.g. (4 1 5 1 release x86_64-pc-linux-gnu)
${BASH_VERSION} -- a string version, e.g. 4.1.5(1)-release
And of course, "$0" --version
This could be an option, depending on how you launch the script:
Install bash 4.2 as /opt/userwriteablefolder/bin/bash.
Use '#!/usr/bin/env bash' as the shebang in your script.
Add '/opt/userwriteablefolder/bin' to the front of PATH in the environment from which
your script is called, so that the bash there will be used if present, otherwise
the regular bash will be used.
The benefit would be to avoid having to detect the version of bash at runtime, but I realize your setup may not make step 3 desirable.

what are shell built-in commands in linux?

I have just started using Linux and I am curious how shell built-in commands such as cd are defined.
Also, I'd appreciate if someone could explain how they are implemented and executed.
If you want to see how bash builtins are defined then you just need to look at Section 4 of The Bash Man Page.
If, however, you want to know how bash bultins are implemented, you'll need to look at the Bash source code because these commands are compiled into the bash executable.
One fast and easy way to see whether or not a command is a bash builtin is to use the help command. Example, help cd will show you how the bash builtin of 'cd' is defined. Similarly for help echo.
The actual set of built-ins varies from shell to shell. There are:
Special built-in utilities, which must be built-in, because they have some special properties
Regular built-in utilities, which are almost always built-in, because of the performance or other considerations
Any standard utility can be also built-in if a shell implementer wishes.
You can find out whether the utility is built in using the type command, which is supported by most shells (although its output is not standardized). An example from dash:
$ type ls
ls is /bin/ls
$ type cd
cd is a shell builtin
$ type exit
exit is a special shell builtin
Re cd utility, theoretically there's nothing preventing a shell implementer to implement it as external command. cd cannot change the shell's current directory directly, but, for instance, cd could communicate new directory to the shell process via a socket. But nobody does so because there's no point. Except very old shells (where there was not a notion of built-ins), where cd used some dirty system hack to do its job.
How is cd implemented inside the shell? The basic algorithm is described here. It can also do some work to support shell's extra features.
Manjari,
Check the source code of bash shell from ftp://ftp.gnu.org/gnu/bash/bash-2.05b.tar.gz
You will find that the definition of shell built-in commands in not in a separate binary executable but its within the shell binary itself (the name shell built-in clearly suggests this).
Every Unix shell has at least some builtin commands. These builtin commands are part of the shell, and are implemented as part of the shell's source code. The shell recognizes that the command that it was asked to execute was one of its builtins, and it performs that action on its own, without calling out to a separate executable. Different shells have different builtins, though there will be a whole lot of overlap in the basic set.
Sometimes, builtins are builtin for performance reasons. In this case, there's often also a version of that command in $PATH (possibly with a different feature set, different set of recognized command line arguments, etc), but the shell decided to implement the command as a builtin as well so that it could save the work of spawning off a short-lived process to do some work that it could do itself. That's the case for bash and printf, for example:
$ type printf
printf is a shell builtin
$ which printf
/usr/bin/printf
$ printf
printf: usage: printf [-v var] format [arguments]
$ /usr/bin/printf
/usr/bin/printf: missing operand
Try `/usr/bin/printf --help' for more information.
Note that in the above example, printf is both a shell builtin (implemented as part of bash itself), as well as an external command (located at /usr/bin/printf). Note that they behave differently as well - when called with no arguments, the builtin version and the command version print different error messages. Note also the -v var option (store the results of this printf into a shell variable named var) can only be done as part of the shell - subprocesses like /usr/bin/printf have no access to the variables of the shell that executed them.
And that brings us to the 2nd part of the story: some commands are builtin because they need to be. Some commands, like chmod, are thin wrappers around system calls. When you run /bin/chmod 777 foo, the shell forks, execs /bin/chmod (passing "777" and "foo") as arguments, and the new chmod process runs the C code chmod("foo", 777); and then returns control to the shell. This wouldn't work for the cd command, though. Even though cd looks like the same case as chmod, it has to behave differently: if the shell spawned another process to execute the chdir system call, it would change the directory only for that newly spawned process, not the shell. Then, when the process returned, the shell would be left sitting in the same directory as it had been in all along - therefore cd needs to be implemented as a shell builtin.
A Shell builtin -- http://linux.about.com/library/cmd/blcmdl1_builtin.htm
for eg. -
which cd
/usr/bin/which: no cd in (/usr/bin:/usr/local/bin......
Not a shell builtin but a binary.
which ls
/bin/ls
http://ss64.com/bash/ this will help you.
and here is shell scripting guide
http://www.freeos.com/guides/lsst/

Resources