How do you refer to an error in Bash/Shell Script? - linux

Is it possible to refer to an error? Here is my code:
read dir
mkdir /Users/Dillon/$dir
And if the directory is already there, it tells me mkdir: /Users/Dillon/(dir): File exists
. Is there a way to state that if it already exists to not not show error?

You can test for directory existence before running the command:
[ -d /Users/Dillon/$dir ] || mkdir /Users/Dillon/$dir
Alternately, you can use the -p flag:
mkdir -p /Users/Dillon/$dir
This will make the directory if it does not yet exist, as well as any missing directories in the path. It does not complain if the directory already exists. It will complain if any segment of the path exists, but isn't a directory or a symlink to a directory.

To suppress error output for any command, redirect the stderr stream to /dev/null
mkdir /Users/Dillion/$dir 2> /dev/null
Or for this one specific case, you could first check for the existence of the directory and bypass the mkdir call if the directory exists:
if [ ! -d /Users/Dillion/$dir ]; then
mkdir /Users/Dillion/$dir
fi

Related

bash: && operator behaviour

I am practicing bash scripting and have a question about && operator
As per O'reily bash cookbook if the command before && operator fails the command that precedes it should cease to execute
See below code
-bash-4.2$ pwd
/home/postgres
-bash-4.2$ mkdir testdir
-bash-4.2$ dir=testdir1
-bash-4.2$ cd $dir1 && rm -rf *
-bash-4.2$ echo $?
0
-bash-4.2$ ls
I expect the rm -rf command to fail as the command cd testdir1 fails, but rm -rf executes and cleans up. The behaviour is more like ";" operator .
Can someone explain please what am i missing
The issue relates to the exit codes.
The && operator will execute the second command if the first one indicates that it was successful.
consider the following:
gmc#linux-ihon:/tmp> rm -rf nonexistant
gmc#linux-ihon:/tmp> echo $? # Note that there is no error message and the exit status is 0 (success).
0
gmc#linux-ihon:/tmp> rm -r nonexistant
rm: cannot remove 'nonexistant': No such file or directory
gmc#linux-ihon:/tmp> echo $? # Note that there was an error message and the exit status is 1 (unsuccessful)
1
gmc#linux-ihon:/tmp>
So now consider the following:
gmc#linux-ihon:/tmp> rm -rf nonexistant && echo "rm success"
rm success
gmc#linux-ihon:/tmp> rm -r nonexistant && echo "rm success"
rm: cannot remove 'nonexistant': No such file or directory
gmc#linux-ihon:/tmp>
In this case, because the rm -rf of the nonexistant directory is deemed successful, the next command is executed.
Whereas rm -r of the same directory is deemed a failure, so the next command is not executed.
I'm not sure why rm -f returns success when the directory does not exist, one school of thought is that if the directory doesn't exist, then you've achieved the desired outcome of the rm command, so therefore Success! Whereas without the -f option, you are explicitly asking rm to remove something and if it doesn't exist let me know!
BTW. There might be an error in your posted code.
You assign a "bad" directory to the variable dir dir=testdir1. But you cd $dir1, this is equivalent to cd with no parameters (because the variable dir1 does not exist). Therefore, it will cd back to your home directory (return value: success). Running rm -rf * from there might not be the best idea.
A command fails means that exit code is different from 0.
If the command should have failed because the variable is not defined set -o nounset or set -u could be used.
set -u
cd "$dir1"
echo "$?"
set +u # disable nounset
cd "$dir1"
echo "$?"
Checking exit code in documentation or implementation.
from man bash / cd
cd [-L|[-P [-e]] [-#]] [dir]
Change the current directory to dir. if dir is not supplied, the value of the HOME shell variable is the default. Any additional arguments following dir are ignored. The variable CDPATH
defines the search path for the directory containing dir: each directory name in CDPATH is searched for dir. Alternative directory names in CDPATH are separated by a colon (:).
A null directory name in CDPATH is the same as the current directory, i.e., ``.''. If dir begins with a slash (/), then CDPATH is not used. The -P option causes cd to use the physical directory structure by
resolving symbolic links while traversing dir and before processing instances of .. in dir (see also the -P option to the set builtin command); the -L option forces symbolic links to be followed
by resolving the link after processing instances of .. in dir. If .. appears in dir, it is processed by removing the immediately previous pathname component from dir, back to a slash or the
beginning of dir. If the -e option is supplied with -P, and the current working directory cannot be successfully determined after a successful directory change, cd will return an unsuccessful
status. On systems that support it, the -# option presents the extended attributes associated with a file as a directory. An argument of - is converted to $OLDPWD before the directory change is
attempted. If a non-empty directory name from CDPATH is used, or if - is the first argument, and the directory change is successful, the absolute pathname of the new working directory is written
to the standard output. The return value is true if the directory was successfully changed; false otherwise.

Checking wether a directory exist in the home directory

Trying to check wether a directory exist in the the home directory
if [ ! -d "$HOME/Smart_Cycle" ]; then
mkdir Smart_Cycle $DIRPATH
echo "Creating DIrecroty""
fi
Trying to check wether the Smart_Cycle directory exist in the home directory, and if it does not exist it will create the directory. Not sure what is going on or if I am on the right track.
When running the script I have these two error that I have never seen before
./smartcycle: line 4: unexpected EOF while looking for matching `"'
./smartcycle: line 6: syntax error: unexpected end of file
EOF is because you have double "" in
"Creating DIrecroty""
As Etan suggested, a better way is to use
mkdir -p "$HOME/Smart_Cycle"
-p will make sure that all directories in the specified path exist and if not, they will be created.
You had an extra quotation mark on
echo "Creating DIrecroty""
Try this
if [ ! -d "$HOME/Smart_Cycle" ]; then
mkdir "$HOME/Smart_Cycle"
echo "Creating DIrecroty"
fi
You can create more than one directory at once
mkdir A B C
In your case, $DIRPATH will be evaluated and a second directory created pointing to the value contained in $DIRPATH

Copying syslog file to a new directory in linux

I'm currently having an assignment to write a bash script that can perform backup log (syslog, dmesg and message) files to a new directory. I wrote my script like this:
cd /var/log
sudo cp syslog Assignment
The file "Assignment" is in my home directory. When I used the "ls" command in my Assignment folder, I don't find a copy of syslog in there. Can someone tell me where did I go wrong? Thanks in advance.
I think you mean Assignment folder, not Assignment file. Anyways if you cd to /var/log, then when you do a cp in /var/log it will think Assignment is local to /var/log. If you do an ls in /var/log now you will see a copy of syslog called Assignment in /var/log. To get syslog copied to the assignment folder in your home directory you need to specify the absolute path not the relative path. Use the tilde, ~, to specify the home directory. So your script should say
cd /var/log
sudo cp syslog ~/Assignment/
You can try this:
#!/bin/sh
if ! [ $1 ] ; then
echo "Usage:";
echo $0 "<directory_where_to_save_logs>";
return;
fi
if [ ! -d "$1" ]; then
echo "Creating directory $1";
mkdir $1;
fi
cp /var/log/syslog* $1
cp /var/log/dmesg* $1
Thanks

Linux, how to create file using directory name?

How to create folders like: wdw/1/11, wdw/2/22, ... wdw/6/66, ..., wdw/9/99, and file using directory name in the deepest directory like directoryname_file.txt
In bash:
mkdir wdw # Creates the top dir.
mkdir {1..9} # Creates the subdirs using brace expansion.
for dir in {1..9} ; do
mkdir $dir/$dir$dir # Creates the subsubdirs.
touch $dir/$dir$dir/$dirdir"_file".txt # Creates the file.
done
In Bash, you can use the mkdir command:
your_dir="wdw/1/11"
if [ ! -d $your_dir ]; then
mkdir $your_dir
fi
The IF clause is to check if the directory already exists.
You can loop to change the value of "your_dir" with /2/22, 6/66, etc...
You'll have use the -p flag with mkdir to create nested directories, for example:
dir_name="wdw/1/11"
mkdir -p $dir_name
Then use touch or echo to create the files:
touch $dir_name/directoryname_file.txt
or
echo "some text" > $dir_name/directoryname_file.txt

Bash: move file/directory and create a link of it

I am trying to make a bash script that moves a file or directory from source directory to destination directory and puts a symlink to it into source directory.
So, <source_path> can be a file or directory, <destination_dir_path> is the directory where I want the original moved to.
Sample usage:
$ mvln /source_dir/file.txt /destination_dir/
OR
$ mvln /source_dir/dir_I_want_to_move/ /destination_dir/
This is what I have managed to put together, but it does not work properly.
It works only if source is a directory, otherwise mv returns an error:
mv: unable to rename `/source_dir/some_file.txt': Not a directory
And the directory is not moved into destination_directory but only its contents are moved.
#!/bin/bash
SCRIPT_NAME='mvln'
USAGE_STRING='usage: '$SCRIPT_NAME' <source_path> <destination_dir_path>'
# Show usage and exit with status
show_usage_and_exit () {
echo $USAGE_STRING
exit 1
}
# ERROR file does not exist
no_file () {
echo $SCRIPT_NAME': '$1': No such file or directory'
exit 2
}
# Check syntax
if [ $# -ne 2 ]; then
show_usage_and_exit
fi
# Check file existence
if [ ! -e "$1" ]; then
no_file $1
fi
# Get paths
source_path=$1
destination_path=$2
# Check that destination ends with a slash
[[ $destination_path != */ ]] && destination_path="$destination_path"/
# Move source
mv "$source_path" "$destination_path"
# Get original path
original_path=$destination_path$(basename $source_path)
# Create symlink in source dir
ln -s "$original_path" "${source_path%/}"
Can some one please help?
The problem is that $destination_path refers to a directory that doesn't exist. Something like this:
mv /path/to/file.txt /path/to/non/existent/directory/
returns an error, and
mv /path/to/directory/ /path/to/non/existent/directory/
will rename /path/to/directory/ to /path/to/non/existent/directory/ (provided that /path/to/non/existent/ is an existent directory, just without a subfolder named directory).
If you are expecting that $destination_path doesn't already exist, then you can add a mkdir command:
mkdir "$destination_path"
mv "$source_path" "$destination_path"
if you're expecting that it might not exist, then you can add it conditionally:
[[ -d "$destination_path" ]] || mkdir "$destination_path"
mv "$source_path" "$destination_path"
and if you're expecting that it does exist, then you have some debugging to do!
(By the way, depending on your exact situation, you might find mkdir -p to be helpful. It recursively creates a directory and all necessary parent directories, and it doesn't mind if the directory already exists.)

Resources