Unix while loop to test command line argument if it is a directory - shell

Trying to make a script that will take a command line argument as a pathname and then test if it is a working directory. Then I wish to run commands (tests) on the directory such as how many files in what sub directories etc.
I am unable to think of a logic to test this with. How would you determine if the argument is a directory?
This is what I have tried
if [ -d "$1" ]
then
echo "It works"
fi
I dont get "It works" when I try this. So I switched it to -a for a file because it is easier to test. And again, I do not get the echo output.

Use the -d option to the test command.
if [ -d "$1" ]
then ...
fi

The title mentions a while loop, but none of the previous commentary seems to mention that. You might try a simple script like:
#!/bin/sh
for arg; do
if test -d "$arg";
echo "$arg is a directory"
else
echo "$arg is not a directory"
fi
done
For variety, you could rewrite that as:
#!/bin/sh
for arg; do
not=$(test -d "$arg" || echo "NOT ")
echo "$arg is ${not}a directory"
done

Related

Why does $# always return 0?

I'm trying to write a script that will only accept exactly one argument. I'm still learning so I don't understand what's wrong with my code. I don't understand why, even though I change the number of inputs the code just exits. (Note: I'm going to use $dir for later if then statements but I haven't included it.)
#!/bin/bash
echo -n "Specify the name of the directory"
read dir
if [ $# -ne 1 ]; then
echo "Script requires one and only one argument"
exit
fi
You can use https://www.shellcheck.net/ to double check your syntax.
$# tells you how many arguments the script was called with.
Here you have two options.
Option 1: Use arguments
#!/bin/bash
if [[ $# -ne 1 ]]
then
echo "Script requires one and only one argument"
exit 1
else
echo "ok, arg1 is $1"
fi
To call the script do: ./script.bash argument
Use [[ ]] for testing conditions (http://mywiki.wooledge.org/BashFAQ/031)
exit 1: by default when a script exists with a 0 status code, it means it worked ok. Here since it is an error, specify a non-zero value.
Option 2: Do not use arguments, ask the user for a value.
Note: this version does not use arguments at all.
#!/bin/bash
read -r -p "Specify the name of the directory: " dir
if [[ ! -d "$dir" ]]
then
echo "Error, directory $dir does not exist."
exit 1
else
echo "ok, directory $dir exists."
fi
To call the script do: ./script.bash without any arguments.
You should research bash tutorials to learn how to use arguments.

Shellscript - Show error for specific argument when using mv

I'm currently writing code for a script to move files to a "dustbin".
As it stands - the code works fine but I want it to produce a message when a file has been moved correctly and a message when a specific file has failed to move/doesn't exist.
My code will accept multiple filenames for input, e.g.
# del test1.txt *.html testing.doc
# Successfully moved to Dustbin
However if only one of these does not exist it still produces an error message.
How do I do this but still allow it to accept arguments as seen in the above example?
My code is as follows:
#!/bin/sh
mv -u "$#" /root/Dustbin 2>/dev/null
# END OF SCRIPT
Sorry for what is probably an obvious question! I'm completely new to shellscript !
Many thanks
You would have to iterate over the arguments and try to move each one:
for path in "$#"; do
if mv -u "$path" /root/Dustbin 2>/dev/null; then
echo "Success"
else
printf 'Failed to move %s\n' "$path"
fi
done
As a shorthand for iterating over the arguments you can omit in "$#" like
for path; do
if mv -u "$path" /root/Dustbin 2>/dev/null; then
echo "Success"
else
printf 'Failed to move %s\n' "$path"
fi
done

Bash + check for file exist with a path home ~

I haven't found anything to deal with this particular situation. Maybe there is a easy way that I'm overlooking instead of checking for a string to catch this scenario. When I check an input for existence of a file, if the input is ~/filecheck , this won't work. I get negative results while the file is in my home folder. Any suggestions for improvement to any part of the script I will definitely appreciate. I also have to use an input instead of a argument. Thanks for any help.
my test script
read -p "Enter: " input
echo $input
if [ -f $input ]; then
read -p "Do you REALLY want to delete this file?:" input2
if [[ $input2='y' || $input2 = 'Y' ]]
then
rm -f $input
elif [[ $input2='n' || $input2='N' ]]
then
exit
else
echo "Invaild Option"
exit
fi
else
echo Invaild Option!
exit
fi
Since you are entering input string as ~/filecheck shell doesn't expand tilde while using condition with -f in [ -f $input ]
You can use it this way but it is not recommended and potentially dangerous as arbitrary commands can be run by user:
if [[ -f $(bash -c "echo $input") ]]; then
echo "file exists"
fi
EDIT: As per the comments below to avoid risky bash -c you can use:
if [[ -f "${input/\~/$HOME}" ]]; then
echo "file exists"
fi
You can't have tilde expansion in this part of the program without using something based on eval—and you don't want to do that with user input. So, your poor-man solution will be to substitute any potential leading ~/ with the expansion of $HOME/. Here's the adaptation of your script in an arguably better style:
#!/bin/bash
read -e -p "Enter: " input
input=${input/#~\//$HOME/} # <--- this is the main idea of this answer (and it's rather poor)
echo "$input"
if [[ -f $input ]]; then
read -e -p "Do you REALLY want to delete this file? " input2
if [[ ${input2,,} = y ]]; then
rm -f -- "$input"
elif [[ ${input2,,} = n ]]; then
exit
else
echo "Invalid Option"
exit
fi
else
echo "Invalid Option!"
fi
exit
Now, out of curiosity, why are you spending time to make a wrapper around rm? you're making a clunky interface to an already existing program, without adding anything to it, only rendering it less powerful and less easy to use.
If all what you want it's to ask the user before deleting, you can use:
rm -i
This will give you appropriate error in the case file does not exist.

Bash script - Nested If Statement for If File Doesn't Exist

I'm trying to compile a script that will read user input, and check if the file after the y/n statement. Then it will make files executable. I think the problem with my script is conditional ordering but check it out yourself:
target=/home/user/bin/
cd $target
read -p "This will make the command executable. Are you sure? (y/n)" CONT
if [ "$CONT" == "y" ];
then
chmod +x $1
echo "File $1 is now executable."
else
if [ "$(ls -A /home/user/bin/)" ];
then
echo "File not found."
else
echo "Terminating..."
fi
fi
As I said, I need the script to scan for the file after the y/n statement is printed. The script works fine how it is but still gives the "file is now executable" even if the argument file doesn't exist (but just gives the standard system "cannot find file" message after the echo'd text).
Your script is mostly correct, you just need to check if the file exists first. Also, it's not the best practice to use cd in shell scripts and not needed here.
So re-writing it
#!/bin/bash
target="/home/user/bin/$1"
if [[ ! -f $target ]]; then
echo "File not found."
else
read -p "This will make the command executable. Are you sure? (y/n) " CONT
if [[ $CONT == "y" ]]; then
chmod +x "$target"
echo "File $1 is now executable."
else
echo "Terminating..."
fi
fi
To get an understanding:
Your script will take one argument (a name of a file).
You ask if you want to make that file executable.
If the answer is 'yes', you make the file executable.
Otherwise, you don't.
You want to verify that the file exists too?
I'm trying to understand your logic. What does this:
if [ "$(ls -A /home/user/bin/)" ];
suppose to do. The [ ... ] syntax is a test. And, it has to be one of the valid tests you see here. For example, There's a test:
-e file: True if file exists.
That mean, I can see if your file is under /home/user/bin:
target="/home/user/bin"
if [ -e "$target/$file" ] # The "-e" test for existence
then
echo "Hey! $file exists in the $target directory. I can make it executable."
else
echo "Sorry, $file is not in the $target directory. Can't touch it."
fi
Your $(ls -A /home/user/bin/) will produce a file listing. It's not a valid test like -e unless it just so happens that the first file in your listing is something like -e or -d.
Try to clarify what you want to do. I think this is something more along the lines you want:
#! /bin/bash
target="/home/user/bin"
if [ -z "$1" ] # Did the user give you a parameter
then
echo "No file name given"
exit 2
fi
# File given, see if it exists in $target directory
if [ ! -e "$target/$1" ]
then
echo "File '$target/$1' does not exist."
exit 2
fi
# File was given and exists in the $target directory
read -p"Do you want $target/$1 to be executable? (y/n)" continue
if [ "y" = "$continue" ]
then
chmod +x "$target/$1"
fi
Note how I'm using the testing, and if the testing fails, I simply exit the program. This way, I don't have to keep embedding if/then statements in if/then statements.

Finding error with shell script

I wrote a script with mbratch's help.
I run as below;
./scriptname folder1
However, I see neither error nor results and I'm not sure what's wrong.
sh -x ./scriptname folder1
+ MAIN
+ check_directories
+ [ -d ]
This works fine for me:
Note: updated to support additional options.
opt="$1"
folder1="$2"
folder2="$3"
case "$opt" in
-d)
if [ -d "${folder1}" ] && [ -d "${folder2}" ] ; then
for i in "${folder1}/*" ; do
echo "test <$i>"
if [ -f "${folder2}/${i##*/}" ] ; then
echo "<${i##*/}>"
else
echo "! <${i##*/}>"
fi
done
fi
;;
# Example option
-h)
# Print help and exit.
;;
# Default case
*)
echo "Unknown option '$opt'" >&2
exit 1
;;
esac
Try replacing ~/ with $HOME/, and be sure to set folder1 and folder2 before using them. Note also that this will break if your directory or file names include spaces. In that case, use find; check the find man page for details.
Is that the entirety of your script? The variables $folder1 are never defined anywhere. By default, the program will take the first two chunks of text in as $1 and $2, so use those variables instead.
Put some echo statements in there to see what variables have what values.
You have a for loop already, so go with that. However you might have to put the part where you are getting a file list inside of $() to have it assigned to a variable, and then loop over it.
Do a quick search on "Looping through files in bash" and you will find a good template for the for loop.

Resources