simplifying bash code [closed] - linux

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 7 years ago.
Improve this question
I have following code in a shell script. It is all working fine.
#!/bin/bash
baseDirPath = '/stackoverflow/question'
newDir = '/stackoverflow/question/answers'
# first check some business condition
if [[ some condition here ]]
then
# check if base Directory path already exit
if [ -d $baseDirPath ];then
# check if new Directory exits or not, if not create one
if [ ! -d $newDir ];then
mkdir $newDir
if [ $? -ne 0 ] ; then
echo "error occurred while creating directory "
fi
fi
else
exit 1;
fi
fi
This is very confusing and not very clean code i feel.
I am very new to programming so not sure how it can be clean.
I am very curious if it can be made simpler or there is some other way to do this.
(The complete shell script is not shown above, just the complicated if-else part is illustrated.)

#!/bin/bash
die(){ >&2 printf '%s\n' "$#"; exit 1; }
#Can't have spaces here
baseDirPath='/stackoverflow/question'
newDir='/stackoverflow/question/answers'
# first check some business condition
if [ some condition here ]; then
# check if base Directory path already exit
mkdir -p "$newDir" || die 'error occured while creating directory'
fi
This changes the semantics slightly--it exits if the creation of newDirs fails for whatever reasons -- baseDirPath is not a directory or baseDirPath is a directory and newDir cannot be created.
You can probably get rid of that error message too. mkdir will already give you an error on stderr if it fails for some reason:
mkdir -p "$newDir" || exit 1
If most of your commands should work like this (i.e., either succeed or bring down the whole script), then you're probably better off setting set -e (~ exit when a command returns with a nonzero status) and then just doing:
mkdir -p "$newDir"

This can be pretty thoroughly simplified:
#!/bin/bash
baseDirPath='/stackoverflow/question'
newDir='/stackoverflow/question/answers'
# first check some business condition
if [[ some condition here ]]; then
if ! mkdir -p "${newDir}"; then
echo "Unable to create directory ${newDir}. Aborting."
exit 1
fi
# Proceed as normal
fi
If you really do need for the baseDirPath to exist, then that can certainly be added as a condition:
if [[ ! -d "${baseDirPath}" ]] || ! mkdir -p "${newDir}"; then

Related

Shell Scripting to Compress directory [duplicate]

This question already has an answer here:
Shell spacing in square brackets [duplicate]
(1 answer)
Closed 4 years ago.
$1 is file / folder that want to compressed
Output filename is the same name, plus current date and ext
if output name exist, then just give warning
Example:
./cmp.sh /home/user
It will be /home/user to /home/user_2018-03-11.tar.bz2
i already have lead, but i'm stuck
#!/bin/bash
if ["$1" == ""]; then
echo "Help : To compress file use argument with directory"
exit 0
fi
if [[ -f "$1" || -d "$1" ]]; then
tar -cvjSf $1"_"$(date '+%d-%m-%y').tar.bz2 $1
fi
but the output is _22-04-2018.tar.bz2
I see that you're using quotes to avoid the problem the underscore getting used as part of the variable name. So while $1 is a positional paramater, $1_ is a variable that you have not set in your script. You can avoid this issue by using curly braces, like ${1}. Anything inside the braces is part of the variable name, so ${1}_ works. This notation would be preferable to $1"_" which leaves a user-provided variable outside of quotes. (Of course, "$1"_ would do the job as wel.)
Also, it's probably safer to set the filename in a variable, then use that for all your needs:
#!/bin/bash
if [ -z "$1" ]; then
echo "Help : To compress file use argument with directory"
exit 0
fi
filename="${1}_$(date '+%F').tar.bz2"
if [ -e "$filename" ]; then
echo "WARNING: file exists: $filename" >&2
else
tar -cvjSf "$filename" "$#"
fi
Changes:
you need spaces around your square brackets in an if condition,
while you can test for equivalence to a null string, -z is cleaner, though you could also test for [ $# -eq 0 ], counting the parameters provided,
using $filename makes sure that your test and your tar will always use the same name, even if the script runs over midnight, and is way more readable,
variables should always be quoted.
Also, are you sure about the -S option for tar? On my system, that option extracts sparse files, and is only useful in conjunction with -x.
ALSO, I should note that as I've rewritten it, there's nothing in this script which is specific to bash, and it should be portable to POSIX shells as well (ash/dash/etc). Bash is great, but it's not universal, and if through your learning journey you can learn both, it will give you useful skills across multiple operating systems and environments.
Use -z switch to check if blank
#!/bin/bash
if [[ -z "$1" ]]; then
echo "Help : To compress file use argument with directory"
exit 0
fi
if [[ -f "$1" || -d "$1" ]]; then
tar -cvjSf $1"_"$(date '+%d-%m-%y').tar.bz2 $1
fi

check to see if filepath exists bash linux script [duplicate]

What command checks if a directory exists or not within a Bash shell script?
To check if a directory exists:
if [ -d "$DIRECTORY" ]; then
echo "$DIRECTORY does exist."
fi
To check if a directory does not exist:
if [ ! -d "$DIRECTORY" ]; then
echo "$DIRECTORY does not exist."
fi
However, as Jon Ericson points out, subsequent commands may not work as intended if you do not take into account that a symbolic link to a directory will also pass this check.
E.g. running this:
ln -s "$ACTUAL_DIR" "$SYMLINK"
if [ -d "$SYMLINK" ]; then
rmdir "$SYMLINK"
fi
Will produce the error message:
rmdir: failed to remove `symlink': Not a directory
So symbolic links may have to be treated differently, if subsequent commands expect directories:
if [ -d "$LINK_OR_DIR" ]; then
if [ -L "$LINK_OR_DIR" ]; then
# It is a symlink!
# Symbolic link specific commands go here.
rm "$LINK_OR_DIR"
else
# It's a directory!
# Directory command goes here.
rmdir "$LINK_OR_DIR"
fi
fi
Take particular note of the double-quotes used to wrap the variables. The reason for this is explained by 8jean in another answer.
If the variables contain spaces or other unusual characters it will probably cause the script to fail.
Always wrap variables in double quotes when referencing them in a Bash script.
if [ -d "$DIRECTORY" ]; then
# Will enter here if $DIRECTORY exists, even if it contains spaces
fi
Kids these days put spaces and lots of other funny characters in their directory names. (Spaces! Back in my day, we didn't have no fancy spaces!)
One day, one of those kids will run your script with $DIRECTORY set to "My M0viez" and your script will blow up. You don't want that. So use double quotes.
Note the -d test can produce some surprising results:
$ ln -s tmp/ t
$ if [ -d t ]; then rmdir t; fi
rmdir: directory "t": Path component not a directory
File under: "When is a directory not a directory?" The answer: "When it's a symlink to a directory." A slightly more thorough test:
if [ -d t ]; then
if [ -L t ]; then
rm t
else
rmdir t
fi
fi
You can find more information in the Bash manual on Bash conditional expressions and the [ builtin command and the [[ compound commmand.
I find the double-bracket version of test makes writing logic tests more natural:
if [[ -d "${DIRECTORY}" && ! -L "${DIRECTORY}" ]] ; then
echo "It's a bona-fide directory"
fi
Shorter form:
# if $DIR is a directory, then print yes
[ -d "$DIR" ] && echo "Yes"
A simple script to test if a directory or file is present or not:
if [ -d /home/ram/dir ] # For file "if [ -f /home/rama/file ]"
then
echo "dir present"
else
echo "dir not present"
fi
A simple script to check whether the directory is present or not:
mkdir tempdir # If you want to check file use touch instead of mkdir
ret=$?
if [ "$ret" == "0" ]
then
echo "dir present"
else
echo "dir not present"
fi
The above scripts will check if the directory is present or not
$? if the last command is a success it returns "0", else a non-zero value.
Suppose tempdir is already present. Then mkdir tempdir will give an error like below:
mkdir: cannot create directory ‘tempdir’: File exists
To check if a directory exists you can use a simple if structure like this:
if [ -d directory/path to a directory ] ; then
# Things to do
else #if needed #also: elif [new condition]
# Things to do
fi
You can also do it in the negative:
if [ ! -d directory/path to a directory ] ; then
# Things to do when not an existing directory
Note: Be careful. Leave empty spaces on either side of both opening and closing braces.
With the same syntax you can use:
-e: any kind of archive
-f: file
-h: symbolic link
-r: readable file
-w: writable file
-x: executable file
-s: file size greater than zero
You can use test -d (see man test).
-d file True if file exists and is a directory.
For example:
test -d "/etc" && echo Exists || echo Does not exist
Note: The test command is same as conditional expression [ (see: man [), so it's portable across shell scripts.
[ - This is a synonym for the test builtin, but the last argument must, be a literal ], to match the opening [.
For possible options or further help, check:
help [
help test
man test or man [
Or for something completely useless:
[ -d . ] || echo "No"
Here's a very pragmatic idiom:
(cd $dir) || return # Is this a directory,
# and do we have access?
I typically wrap it in a function:
can_use_as_dir() {
(cd ${1:?pathname expected}) || return
}
Or:
assert_dir_access() {
(cd ${1:?pathname expected}) || exit
}
The nice thing about this approach is that I do not have to think of a good error message.
cd will give me a standard one line message to standard error already. It will also give more information than I will be able to provide. By performing the cd inside a subshell ( ... ), the command does not affect the current directory of the caller. If the directory exists, this subshell and the function are just a no-op.
Next is the argument that we pass to cd: ${1:?pathname expected}. This is a more elaborate form of parameter substitution which is explained in more detail below.
Tl;dr: If the string passed into this function is empty, we again exit from the subshell ( ... ) and return from the function with the given error message.
Quoting from the ksh93 man page:
${parameter:?word}
If parameter is set and is non-null then substitute its value;
otherwise, print word and exit from the shell (if not interactive).
If word is omitted then a standard message is printed.
and
If the colon : is omitted from the above expressions, then the
shell only checks whether parameter is set or not.
The phrasing here is peculiar to the shell documentation, as word may refer to any reasonable string, including whitespace.
In this particular case, I know that the standard error message 1: parameter not set is not sufficient, so I zoom in on the type of value that we expect here - the pathname of a directory.
A philosophical note:
The shell is not an object oriented language, so the message says pathname, not directory. At this level, I'd rather keep it simple - the arguments to a function are just strings.
if [ -d "$Directory" -a -w "$Directory" ]
then
#Statements
fi
The above code checks if the directory exists and if it is writable.
More features using find
Check existence of the folder within sub-directories:
found=`find -type d -name "myDirectory"`
if [ -n "$found" ]
then
# The variable 'found' contains the full path where "myDirectory" is.
# It may contain several lines if there are several folders named "myDirectory".
fi
Check existence of one or several folders based on a pattern within the current directory:
found=`find -maxdepth 1 -type d -name "my*"`
if [ -n "$found" ]
then
# The variable 'found' contains the full path where folders "my*" have been found.
fi
Both combinations. In the following example, it checks the existence of the folder in the current directory:
found=`find -maxdepth 1 -type d -name "myDirectory"`
if [ -n "$found" ]
then
# The variable 'found' is not empty => "myDirectory"` exists.
fi
DIRECTORY=/tmp
if [ -d "$DIRECTORY" ]; then
echo "Exists"
fi
Try online
Actually, you should use several tools to get a bulletproof approach:
DIR_PATH=`readlink -f "${the_stuff_you_test}"` # Get rid of symlinks and get abs path
if [[ -d "${DIR_PATH}" ]] ; Then # Now you're testing
echo "It's a dir";
fi
There isn't any need to worry about spaces and special characters as long as you use "${}".
Note that [[]] is not as portable as [], but since most people work with modern versions of Bash (since after all, most people don't even work with command line :-p), the benefit is greater than the trouble.
Have you considered just doing whatever you want to do in the if rather than looking before you leap?
I.e., if you want to check for the existence of a directory before you enter it, try just doing this:
if pushd /path/you/want/to/enter; then
# Commands you want to run in this directory
popd
fi
If the path you give to pushd exists, you'll enter it and it'll exit with 0, which means the then portion of the statement will execute. If it doesn't exist, nothing will happen (other than some output saying the directory doesn't exist, which is probably a helpful side-effect anyways for debugging).
It seems better than this, which requires repeating yourself:
if [ -d /path/you/want/to/enter ]; then
pushd /path/you/want/to/enter
# Commands you want to run in this directory
popd
fi
The same thing works with cd, mv, rm, etc... if you try them on files that don't exist, they'll exit with an error and print a message saying it doesn't exist, and your then block will be skipped. If you try them on files that do exist, the command will execute and exit with a status of 0, allowing your then block to execute.
[[ -d "$DIR" && ! -L "$DIR" ]] && echo "It's a directory and not a symbolic link"
N.B: Quoting variables is a good practice.
Explanation:
-d: check if it's a directory
-L: check if it's a symbolic link
To check more than one directory use this code:
if [ -d "$DIRECTORY1" ] && [ -d "$DIRECTORY2" ] then
# Things to do
fi
Check if the directory exists, else make one:
[ -d "$DIRECTORY" ] || mkdir $DIRECTORY
[ -d ~/Desktop/TEMPORAL/ ] && echo "DIRECTORY EXISTS" || echo "DIRECTORY DOES NOT EXIST"
Using the -e check will check for files and this includes directories.
if [ -e ${FILE_PATH_AND_NAME} ]
then
echo "The file or directory exists."
fi
This answer wrapped up as a shell script
Examples
$ is_dir ~
YES
$ is_dir /tmp
YES
$ is_dir ~/bin
YES
$ mkdir '/tmp/test me'
$ is_dir '/tmp/test me'
YES
$ is_dir /asdf/asdf
NO
# Example of calling it in another script
DIR=~/mydata
if [ $(is_dir $DIR) == "NO" ]
then
echo "Folder doesnt exist: $DIR";
exit;
fi
is_dir
function show_help()
{
IT=$(CAT <<EOF
usage: DIR
output: YES or NO, depending on whether or not the directory exists.
)
echo "$IT"
exit
}
if [ "$1" == "help" ]
then
show_help
fi
if [ -z "$1" ]
then
show_help
fi
DIR=$1
if [ -d $DIR ]; then
echo "YES";
exit;
fi
echo "NO";
As per Jonathan's comment:
If you want to create the directory and it does not exist yet, then the simplest technique is to use mkdir -p which creates the directory — and any missing directories up the path — and does not fail if the directory already exists, so you can do it all at once with:
mkdir -p /some/directory/you/want/to/exist || exit 1
if [ -d "$DIRECTORY" ]; then
# Will enter here if $DIRECTORY exists
fi
This is not completely true...
If you want to go to that directory, you also need to have the execute rights on the directory. Maybe you need to have write rights as well.
Therefore:
if [ -d "$DIRECTORY" ] && [ -x "$DIRECTORY" ] ; then
# ... to go to that directory (even if DIRECTORY is a link)
cd $DIRECTORY
pwd
fi
if [ -d "$DIRECTORY" ] && [ -w "$DIRECTORY" ] ; then
# ... to go to that directory and write something there (even if DIRECTORY is a link)
cd $DIRECTORY
touch foobar
fi
In kind of a ternary form,
[ -d "$directory" ] && echo "exist" || echo "not exist"
And with test:
test -d "$directory" && echo "exist" || echo "not exist"
file="foo"
if [[ -e "$file" ]]; then echo "File Exists"; fi;
The ls command in conjunction with -l (long listing) option returns attributes information about files and directories.
In particular the first character of ls -l output it is usually a d or a - (dash). In case of a d the one listed is a directory for sure.
The following command in just one line will tell you if the given ISDIR variable contains a path to a directory or not:
[[ $(ls -ld "$ISDIR" | cut -c1) == 'd' ]] &&
echo "YES, $ISDIR is a directory." ||
echo "Sorry, $ISDIR is not a directory"
Practical usage:
[claudio#nowhere ~]$ ISDIR="$HOME/Music"
[claudio#nowhere ~]$ ls -ld "$ISDIR"
drwxr-xr-x. 2 claudio claudio 4096 Aug 23 00:02 /home/claudio/Music
[claudio#nowhere ~]$ [[ $(ls -ld "$ISDIR" | cut -c1) == 'd' ]] &&
echo "YES, $ISDIR is a directory." ||
echo "Sorry, $ISDIR is not a directory"
YES, /home/claudio/Music is a directory.
[claudio#nowhere ~]$ touch "empty file.txt"
[claudio#nowhere ~]$ ISDIR="$HOME/empty file.txt"
[claudio#nowhere ~]$ [[ $(ls -ld "$ISDIR" | cut -c1) == 'd' ]] &&
echo "YES, $ISDIR is a directory." ||
echo "Sorry, $ISDIR is not a directoy"
Sorry, /home/claudio/empty file.txt is not a directory
There are great solutions out there, but ultimately every script will fail if you're not in the right directory. So code like this:
if [ -d "$LINK_OR_DIR" ]; then
if [ -L "$LINK_OR_DIR" ]; then
# It is a symlink!
# Symbolic link specific commands go here
rm "$LINK_OR_DIR"
else
# It's a directory!
# Directory command goes here
rmdir "$LINK_OR_DIR"
fi
fi
will execute successfully only if at the moment of execution you're in a directory that has a subdirectory that you happen to check for.
I understand the initial question like this: to verify if a directory exists irrespective of the user's position in the file system. So using the command 'find' might do the trick:
dir=" "
echo "Input directory name to search for:"
read dir
find $HOME -name $dir -type d
This solution is good because it allows the use of wildcards, a useful feature when searching for files/directories. The only problem is that, if the searched directory doesn't exist, the 'find' command will print nothing to standard output (not an elegant solution for my taste) and will have nonetheless a zero exit. Maybe someone could improve on this.
The below find can be used,
find . -type d -name dirname -prune -print
One Liner:
[[ -d $Directory ]] && echo true
(1)
[ -d Piyush_Drv1 ] && echo ""Exists"" || echo "Not Exists"
(2)
[ `find . -type d -name Piyush_Drv1 -print | wc -l` -eq 1 ] && echo Exists || echo "Not Exists"
(3)
[[ -d run_dir && ! -L run_dir ]] && echo Exists || echo "Not Exists"
If an issue is found with one of the approaches provided above:
With the ls command; the cases when a directory does not exists - an error message is shown
[[ `ls -ld SAMPLE_DIR| grep ^d | wc -l` -eq 1 ]] && echo exists || not exists
-ksh: not: not found [No such file or directory]

Bash is symlinking to working directory instead of specified directory

I am working on a bash script that I am working on for a universal Linux dotfile install script. I am attempting to get the symlinking working but I have been bashing (no pun intended) my head against the wall trying to figure out why the symlinks will not work and the copying will not work. I currently have this separated into multiple files so I don't have if statements three miles long.
ultimate-install.sh
#! /bin/bash
#
# The ultimate install script for all dotfiles.
if [[ -z "$1" ]]; then
echo "Please specify the directory where all of you dotfiles are located."
exit 1
fi
# Makes sure that the directory does NOT have a trailing slash!
if [[ ${1:(-1)} == "/" ]]; then
DOTFILE_DIR=${1:0:${#1} - 1}
else
DOTFILE_DIR="$1"
fi
# TODO: Clean this mess up and make it more concise.
if [[ -z "$2" ]]; then
if [[ ! -d $HOME/.config/old_dotfiles ]]; then
mkdir "$HOME/.config/old_dotfiles"
fi
BACKUP_DIR="$HOME/.config/old_dotfiles"
else
if [[ -d "$2" ]]; then
BACKUP_DIR="$2"
else
mkdir "$2"
BACKUP_DIR="$2"
fi
fi
read DECISION
if [ $DECISION == "N" -o $DECISION == "n" ]; then
echo "Aborting installation!"
exit
fi
read DECISION
echo
if [ $DECISION == "N" -o $DECISION == "n" ]; then
source src/no-prompts.sh "$DOTFILE_DIR" "$BACKUP_DIR"
else
source src/prompts.sh "$DOTFILE_DIR" "$BACKUP_DIR"
fi
echo "Installation complete. Old dotfiles are backed up to $BACKUP_DIR."
src/no-prompts.sh
#! /bin/bash
#
# Maintained by Daniel Seymour
DOTFILE_DIR="$1"
BACKUP_DIR="$2"
TEST_DIR="/home/daniel/dotfile-test"
function no_prompt_install(){
FILE_NAME="$1"
if [ "${FILE_NAME:0:1}" == "." ]; then
ln -s "$FILE_NAME $TEST_DIR/$FILE_NAME"
else
ln -s ".$FILE_NAME $TEST_DIR/$FILE_NAME"
fi
}
# TODO: implement a check for file type and deal with unknown files.
for FILE in $DOTFILE_DIR/*; do
cp $FILE $BACKUP_DIR
no_prompt_install $FILE
done
src/prompts.sh
#! /bin/bash
#
# Maintained by Daniel Seymour
DOTFILE_DIR="$1"
BACKUP_DIR="$2"
TEST_DIR="/home/daniel/dotfile-test"
function prompt_install {
FILE_PATH=$1
FILE_NAME=${FILE_PATH##*/}
echo "Would you like to install $FILE_NAME? [Y, n]"
read DECISION
if [ $DECISION == "n" -o $DECISION == "N" ]; then
echo "Not installing."
return
else
# TODO: Clean this up into one statement.
if [ ${FILE_NAME:0:1} == "." ]; then
rm -rf "$TEST_DIR/$FILE_NAME"
ln -sn "$FILE_PATH $TEST_DIR/$FILE_NAME"
else
FILE_NAME="."$FILE_NAME
rm -rf "$TEST_DIR/$FILE_NAME"
ln -sn "$FILE_PATH $TEST_DIR/$FILE_NAME"
fi
fi
}
# TODO: implement a check for file type and deal with unknown files.
for FILE in $DOTFILE_DIR/*; do
cp $FILE $BACKUP_DIR
prompt_install $FILE
done
The above is trimmed for long echo statements that do a lot of explaining.
The basic idea of this script is to take as many as two arguments (the dotfile directory to install and if specified, the custom backup directory, $1 and $2 respectively). The script should then copy all of the files in the target directory to BACKUP_DIR and symlink all of the dotfiles in the DOTFILE_DIR to TEST_DIR. (TEST_DIR will be $HOME in the production scripts.) Great in theory, right?
The complication comes when I run the script. None of the files are copied or symlinked as they should be. Instead, I end up with NO copy (probably due to the same issue as the symlink not working) and a broken symlink in the current directory.
One last piece of information. I am executing the file from the directory that contains ultimate-install.sh (/home/daniel/Projects/Git-Repos/Ultimate-Dotfile-Install-Scripts).
So where did I go wrong?
PS Please don't comment on the TODOs. :)
Short answer
Your quoting is wrong.
ln -sn -- "$FILE_PATH" "$TEST_DIR/$FILE_NAME"
Longer answer
This does not really relate to your problem, but I want to point it out.
Do not use "" inside [[ ]], so instead of this if [[ -z "$1" ]]; then use this if [[ -z $1 ]]; then
What is the point of making sure that directory does not have a trailing slash? It has no effect! /usr/bin/ is the same directory as /usr/bin or /usr////bin or /usr////////bin//////
Do not check if a directory exists when creating directories. Use -p option! Example: mkdir -p "$HOME/.config/old_dotfiles"
Instead of if [ $DECISION == "N" -o $DECISION == "n" ]; use if [[ ${DECISION^^} == N]];
I have another great answer about bash code style HERE. Please check it out! Also read the comments, since I was explaining there exactly your issue.

Issue controlling script flow

I'm new to shell scripting, my script appears to be okay, but its the flow that I'm having an issue controlling. Could someone point out what silly mistake I've made please.
#! /bin/sh
echo "Are you sure youx want to delete $1? Answer y or n"
read ans
echo $ans
if $ans = "y"|"Y"
then
mv $1 /home/parallels/dustbin
echo "File $1 has been deleted"
else echo "File $1 has not been deleted"
fi
Make your if condition like this:
if [ "$ans" = "y" -o "$ans" = "Y" ]
There are a few things wrong with your script. Some are serious, some are less so.
First, the serious problems.
As guru suggested, you need to use square brackets to surround your if condition. This is because if only tests for the output of a condition, it doesn't perform actual string comparisons. Traditionally, a program called /bin/test, which was also called /bin/[ took care of that. These days, that functionality is built in to the shell, but /bin/sh still behaves as if it's a separate program.
In fact, you can do interesting things with if when you don't use square brackets for your condition. For example, if grep -q 'RE' /path/to/file; then is quite common. The grep -q command issues no output, but simply returns a "success" or "fail" that is detected by if.
Second serious problem is that you are echoing a status that may or may not be true. I call this a serious problem because ... well, log messages simply shouldn't make false claims. If the permissions are wrong for the file in $1, or the filename contains a space, then your mv command will fail, but the message will claim that it did not. More on this later.
Next, the less serious problems.
These are mostly style and optimization things.
First off, read on most platforms includes a -p option that lets you specify a prompt. Use this, and you don't need to include an echo command.
Second, your indenting makes it hard to see what the if construct is wrapping. This isn't a huge problem in a program this small, but as you grow, you REALLY want to follow consistent standards.
Third, you can probably get more flexibility in multiple-choice questions like this if you use case statements instead of if.
After all that, here's how I'd write this script:
#!/bin/sh
if [ "$1" = "-y" ]; then
ans=y
shift
elif [ -t 0 ]; then
read -p "Are you sure you want to delete '$1' (y/N) ? " ans
fi
case "$ans" in
Y*|y*)
retval=0
if [ -z "$1" ]; then
retval=64
echo "ERROR: you didn't specify a filename." >&2
if [ ! -f "$1" ]; then
retval=66
echo "ERROR: file '$1' not found!" >&2
elif mv "$1" /home/parallels/dustbin/; then
echo "File '$1' has been deleted" >&2
else
retval=$?
echo "ERROR: file '$1' could not be deleted!" >&2
fi
;;
*)
echo "ABORT: file '$1' has not been deleted" >&2
retval=4
;;
esac
exit $retval
Aside from what's mentioned above, here are some things in this code snippet:
[ "$1" = "-y" ] - if the user specifies a -y option, then we behave as if the question was answered with a "yes".
[ -t 0 ] - this tests whether we are on an interactive terminal. If we are, then it makes sense to ask questions with read.
Y*|y*) - in a case statement, this matches any string that begins with an upper or lower case "y". Valid affirmative responses would therefore be "Y", "yes", "yellow", etc.
[ ! -f "$1" ] - this tests whether the file exists. You can man test or man sh to see the various tests available in shell. (-f may not be the most appropriate for you.)
>&2 - at the end of a line, sends its output to "standard error" instead of "standard out". This changes how output will be handled by pipes, cron, etc. Errors and log data are often sent to stderr, so that stdout can be dedicated to a program's actual output.
mv "$1" ... - The filename is in quotes. This protects you in case the filename has special characters like spaces in it.
$retval - the values for this came from a best guess of the closest item in man sysexits.
retval=$? - this is the exit status of the most recently executed command. In this case, that means we're assigning mv's exit status to the variable $retval, so that if mv failed, the whole script reports the reason for the fail, as far as mv is concerned.
You can also convert the user response to either case and just check it for respective case like
read ans
ans=${ans,,} # make 'ans' lowercase, or use ${ans^^} for making it uppercase
if [ "$ans" = "y" ]
then
....
fi
Below is the perfect code with error handling included
#!/bin/sh
echo "Are you sure you want to delete $1? Answer y or n"
read ans
echo $ans
if [ $ans == "y" ] || [ $ans == "Y" ]
then
if [ -f $1 ]
then
mv $1 /home/parallels/dustbin
echo "File $1 has been deleted"
else
echo " File $1 is not found"
fi
else
echo "File $1 has not been deleted"
fi

shell to find a file , execute it - exit if 'error' and continue if ' no error'

I have to write a shell script and i don't know how to go about it.
Basically i have to write a script where i'd find a file ( it could be possibly named differently). If either file exists then it must be executed, if it returns a 0 ( no error), it should continue the build, if it's not equal to 0 ( returns with error), it should exit. If either file is not found it should continue the build.
the file i have to find could be either file.1 or file.2 so it could be either named (file.1), or (file.2).
some of the conditions to make it more clear.
1) if either file exists , it should execute - if it has any errors it should exit, if no errors it should continue.
2) none could exist, if that's the case then it should continue the build.
3) both files will not be present at the same time ( additional info)
I have tried to write a script but i doubt it's even closer to what i am looking for.
if [-f /home/(file.1) or (file.2)]
then
-exec /home/(file.1) or (file.2)
if [ $! -eq 0]; then
echo "no errors continuing build"
fi
else
if [ $! -ne 0] ; then
exit ;
fi
else
echo "/home/(file.1) or (file.2) not found, continuing build"
fi
any help is much appreciated.
Thanks in advance
DOIT=""
for f in file1.sh file2.sh; do
if [ -x /home/$f ]; then DOIT="/home/$f"; break; fi
done
if [ -z "$DOIT" ]; then echo "Files not found, continuing build"; fi
if [ -n "$DOIT" ]; then $DOIT && echo "No Errors" || exit 1; fi
For those confused about my syntax, try running this:
true && echo "is true" || echo "is false"
false && echo "is true" || echo "is false"
Just putting the line
file.sh
in your script should work, if you set up your script to exit on errors.
For example, if your script was
#!/bin/bash -e
echo one
./file.sh
echo two
Then if file.sh exists and is executable it would run and your whole script would run. If not, the script would fail when it tried to execute the non-existing file.
If you want to execute one file or the other, extend the idea to the following:
#!/bin/bash -e
echo one
./file1.sh || ./file2.sh
echo two
This means if file1.sh does not exist, it will try file2.sh and if that is there it will run and your whole script will run.
This give preference to file1 of course, meaning if they both exist, then only file1 will run.

Resources