How to check whether utility 'realpath' is available for the script? - shell

We were asked to write a project in shell that should be compatible with every general shell (dash, ksh, bash). We should also cover the possible option that the utility realpath is not available on a particular machine. Is there any easy way how to checks if the utility is present or not?

Checking if an executable is available can be done in several ways. One simple way is to just run it, and check the return code. If you don't want to see the error, redirect it:
realpath yourArgument 2> /dev/null
echo $?
Or in a if:
realpath yourArgument 2> /dev/null
if [ $? -eq 127 ]; then
#Realpath is missing!
fi
This way if realpath worked and is in the path you will have the real path printed, otherwise you enter the if. Note 0 is the agreed upon success status, adn 127 is the error code indicating missing command (Thanks #user1934428, I used previously -gt 0 which may fail since realpath may return non-0 status.)

If I did not understand bad, you want to know whether a certain utility exists to check if a directory exists?. If so, I have used this line always in several shells and I have not found problems.
if [ -d "$DIRECTORY" ]; then
# Control will enter here if $DIRECTORY exists.
fi

Related

How do I find a file given its basename and retrieve its filetype in a bash script?

I'm writing my first bash script as part of homework and given covid 19 we haven't been able to go over some of this stuff in class (and I'm not doing well googling).
I am writing a script that takes user input and searches the cd to find a file with the name of $userInput. If found the script will determine if the file is a "normal file" (.txt for example) or if the file is a directory. How can I gather this information?
Here is my code currently:
#! /bin/bash
$userInput
echo "Enter a file name. Empty to exit."
while [ 1 ]
do
echo "Please input a file name:"
read userInput
if [ "$userInput" == "" ]; then
exit
else
echo $userInput
fi
done
I have the looping and getting user input down, what am I missing here?
if cd $userInput*; then
cd ..
echo "It is a directory."
else
# the cd will do the rest of informing for you
fi
I also strongly suggest to turn this into a file and get user input in command line, like this:
bash ./yourcode.sh filename
Inside your code:
if cd $1*; then
cd ..
echo "It is a directory."
else
# the cd will do the rest of informing for you
fi
EDITED: EXTRA
Continue here if you really wanna understand what happened. In bash, every command/function by default returns a value that informs the kernel whether it terminated successfully or something went wrong, for sake of simplicity, we will just assume it returns true if successful and false if not, here I used this piece of info with the function cd which changes the current directory to the one specified, if the change happened successfully, it will return true which then I will capture in my if statement to execute the code, which consists of returning to the original directory (cd ..) and saying to the user it was a directory.
Otherwise, cd will return false and will tell you itself what happened, was it a file or was it nonexistent, and the * (a wildcard) exists to find whatever extension it might have or even doesn't have. I hope this helps, cheers!
There are several ways to classify an entry. For instance,
[[ -f $filename ]]
tests for regular files,
[[ -d $filename ]]
tests for directories, and there are other tests too where you can test for symlinks, or whether you have read/write/execute permissions. You find the list of available file test operators here.

How is the command suggestion implemented in the bash shell?

gk#Jarvis:~$ sudi
No command 'sudi' found, did you mean:
Command 'sudo' from package 'sudo-ldap' (universe)
Command 'sudo' from package 'sudo' (main)
sudi: command not found
I have currently implemented a simple 'Did you mean..?' for plain English words which works as follow:
If user enters 'Kack', check the alphabets around every alphabet in the word on a QWERTY keyboard and substitute them one by one. (e.g. here they would be J,L,M,I,O for 'K'; Q,W,S,Z,X for 'a' and so on)
Return the word with the most probability (the user entered word itself too if that is the case) as the most likely word based on training on a corpus of text.
How is the code-suggestion implemented in the linux command line?
bash does not implement the suggestion logic; it is in a function defined as part of your bash initialization file, and it was put there by your distribution (Ubuntu/Debian, at a guess).
bash provides the mechanism for implementating such a function: when it attempts to execute a command, and the command is not found, it invokes the function command_not_found_handle, if it is defined.
On my machine (an Ubuntu variant), that function is defined as follows:
$ type command_not_found_handle
command_not_found_handle is a function
command_not_found_handle ()
{
if [ -x /usr/lib/command-not-found ]; then
/usr/lib/command-not-found -- "$1";
return $?;
else
if [ -x /usr/share/command-not-found/command-not-found ]; then
/usr/share/command-not-found/command-not-found -- "$1";
return $?;
else
printf "%s: command not found\n" "$1" 1>&2;
return 127;
fi;
fi
}
(And /usr/lib/command-not-found exists, is executable, and is a Python script.)
From the Bash man page:
COMMAND EXECUTION
[…]
If the name is neither a shell function nor a builtin, and contains no slashes, bash searches each element of the PATH for a directory containing an executable file by that name. Bash uses a hash table to remember the full pathnames of executable files (see hash under SHELL BUILTIN COMMANDS below). A full search of the directories in PATH is performed only if the command is not found in the hash table. If the search is unsuccessful, the shell searches for a defined shell function named command_not_found_handle. If that function exists, it is invoked with the original command and the original command's arguments as its arguments, and the function's exit status becomes the exit status of the shell. If that function is not defined, the shell prints an error message and returns an exit status of 127.
Let's try this out:
$ foobar
bash: foobar: command not found
$ function command_not_found_handle { echo "I'm so sorry, what is '$1'?"; }
$ foobar
I'm so sorry, what is 'foobar'?
Your shell initialization code might install a more useful command_not_found_handle. You would typically find such code in the system-wide configuration in /etc/bash.bashrc or a file sourced by it. Your distribution might install a handler there to invoke an external program that queries the distribution's package manager for the command or “similar” commands. For your Ubuntu, this would be implemented in the command-not-found package.
The default configuration files shipped by distributions are usually kept very general so the function might check whether the command-not-found binary is installed and, if so, call it or otherwise print a simple error message.
function command_not_found_handle {
if [ -x /usr/bin/command-not-found ]
then
/usr/bin/command-not-found "$1"
else
echo "$1: Command not found" >&2
return 127
fi
}
This way, the configuration file does not have to be changed if the command-not-found package is installed or removed again later.
I don't know how that program for Ubuntu is implemented but typically, such a tool would have a list of all known commands and find the most similar one. It might then check whether that program is installed and, if not, check what package provides it and suggest installing that.
Searching for “similar text” is usually done by computing the edit distance between two strings. Taking into account how likely mistyping a given letter is, given the current keyboard layout, would be a very smart addition.

Bash script giving me a different result on reboot?

I worked on a Bash script for the last day or so and running and debugging it directly on the shell.
The final script will be executed when the Ubuntu server gets rebooted.
I have started testing this, but my script gives me a different result then what I was expected.
I have narrowed it down to an "or condition" and rewrote a more simpler script to test this anomaly:
A call to this script has been made in /etc/rc.local, with a redirection of the output to a log file (log/reboot.log).
I have this in my script (as a test):
#!/bin/bash
YESTERDAY=20131103
SYS_DATE=20131104
LAST_START=20131104
if [[ $LAST_START = $YESTERDAY || $LAST_START = $SYS_DATE ]];
then
echo "is equal"
else
echo "is not equal"
fi
Executing in the shell I get "is equal" (the right answer). After the reboot in the log I get "is not equal".
Could someone tell me why?
I am guessing here,
But do you realize your /bin/sh is not your SHELL.
In UBUNTU and Debian, /bin/sh is DASH, your login shell is BASH.
So it might be related to your syntax of [[ ]] which is BASH.
Did you right in your top of the script:
#!/bin/sh
or
#!/bin/bash
[[
The [[ builtin is a bashism, and has somewhat better-defined semantics
than [ (a.k.a. test). However, it is still quite reasonable to use [
instead, and portable scripts must do so. Note that argument handling
is not quite the same; as above, use = rather than ==.
See here:
https://wiki.ubuntu.com/DashAsBinSh
the right way to do stuff here
The best solution would be actually to put your script in /etc/init.d and link it to run level 6. Which is the run level executed when rebooting. You should consider reading man 8 init when you have got some spare time. It will help you understand how your system is starting and shuting down.

What is [[: not found error?

I tried to execute a file containing a shell script.
I get an error called "[[: not found" error at the last line. How to resolve it?
[[ is bash. sh wants the [ variant.
Either change that to /usr/bin/bash (or wherever bash is located on your system), or adjust the expression accordingly:
if [ status_of_job -eq 0 ];
[ is actually an executable in linux. but [[ is not.
Try
if [ status_of_job -eq 0 ]; then
(note the single [] set).
This interpreter:
#!/usr/bin/sh
Is either not bash or your file doesn't have the shebang in the right place.
ls -l /usr/bin/sh will tell you if it's a symlink to something other than bash.
If it is bash, then check that there's no leading characters before the #!.
You may find you stumble at other blocks later with POSIX shell related issues. People don't really understand how much bash actually provides until it's taken away. Take a look at this so you can hopefully avoid any other issues:
http://pubs.opengroup.org/onlinepubs/7908799/xcu/shellix.html

Better way to make a bash script self-tracing?

I have certain critical bash scripts that are invoked by code I don't control, and where I can't see their console output. I want a complete trace of what these scripts did for later analysis. To do this I want to make each script self-tracing. Here is what I am currently doing:
#!/bin/bash
# if last arg is not '_worker_', relaunch with stdout and stderr
# redirected to my log file...
if [[ "$BASH_ARGV" != "_worker_" ]]; then
$0 "$#" _worker_ >>/some_log_file 2>&1 # add tee if console output wanted
exit $?
fi
# rest of script follows...
Is there a better, cleaner way to do this?
#!/bin/bash
exec >>log_file 2>&1
echo Hello world
date
exec has a magic behavior regarding redirections: “If command is not specified, any redirections take effect in the current shell, and the return status is 0. If there is a redirection error, the return status is 1.”
Also, regarding your original solution, exec "$0" is better than "$0"; exit $?, because the former doesn't leave an extra shell process around until the subprocess exits.
maybe you are looking for set -x?
you may check a common open source trace library with support for bash.
http://sourceforge.net/projects/utalm/
http://www.unifiedsessionsmanager.org/en/downloads.html
The current available component is for scripting by bash, soon available are Python and C++. Additional going to follow are: Ruby, Java, JavaScript, SQL, PowerShell,...
The license is Apache-2.0
WKR
Arno-Can Uestuensoez

Resources