Nesting a for loop in an if else statement - bash

if [ ! -f ./* ]; then
for files in $(find . -maxdepth 1 -type f); do
echo $files
else
echo Nothing here
fi
Returns
syntax error near unexpected token `else'
New to this. Can anyone point me to what I did wrong?

You forgot done!
if [ ! -f ./* ]; then
for files in $(find . -maxdepth 1 -type f); do
echo $files
done
else
echo Nothing here
fi

The reason you get a syntax error is because you are not ending the loop with the "done" statement. You should be using a while loop, instead of a for loop in this case, as the for loop will break if any of the filenames contain spaces or newlines.
Also, the test command you have issued will also give a syntax error if the glob expands to multiple files.
$ [ ! -f ./* ]
bash: [: too many arguments
Here is a better way to check if the directory contains any files:
files=(./*) # populate an array with file or directory names
hasfile=false
for file in "${files[#]}"; do
if [[ -f $file ]]; then
hasfile=true
break
fi
done
if $hasfile; then
while read -r file; do
echo "$file"
done < <(find . -maxdepth 1 -type f)
fi
Also, you could simply replace the while loop with find -print if you have GNU find:
if $hasfile; then
find . -maxdepth 1 -type f -print
fi

The syntax for "for" is
for: for NAME [in WORDS ... ;] do COMMANDS; done
You are missing the "done"
Try
if [ ! -f ./* ]; then
for files in $(find . -maxdepth 1 -type f); do
echo $files
done
else
echo Nothing here
fi
BTW, did you mean echo with lowercase rather than ECHO?

Related

if then else twice and if not found then run a command

I have this code:
if [[ $(find /path/to/folder1 -type f -not -path "*configs*" -size -800k 2>/dev/null) ]]; then
echo "[[Warning]]: The files size is under 800 Kilobytes"
if [[ $(find /path/to/another/folder -type f \( ! -iname "123.file*" \) -not -path "*logs*" -size -40k 2>/dev/null) ]]; then
echo "[[Warning]]: The file size is under 40 Kilobytes"
fi
else
Run a command here
fi
The target is:
Get an echo if a file under size found in any of the above paths or get both echo if a file under size found on both paths and run the command at the end only if not files under size found at any of the above two paths.
Both checks are tested and working but it seems that I have the if or else statements in wrong order?
That should work what you have. An easier way would be to use OR (||) in structure below. If both of those statements are false, then you run command.
if statementA || statementB then...
else ...
Your else is already executed when the first if gets false. You could do a
if [[ $(find ....) ]] || [[ $(find ....) ]]; then
else
run
fi
or
if [[ -n $(find ....) || -n $(find ....) ]]; then
else
run
fi
but this would only take care about the running command; you want, however, also display an individual message if the find commands succeed.
There are several possibilities. The easiest would be IMO to use a control variable:
run_command=1
if [[ $(find ....) ]]; then
echo ....
run_command=0
fi
if [[ $(find ....) ]]; then
echo ....
run_command=0
fi
if ((run_command == 1)); then
run ...
fi

(unix) How to write script to count the number of files in a directory using loop

How can I create a bash script to count the number of files in a directory using a loop.
The script should take a target directory and output: Number of files in ::
#!/bin/bash
counter=0
if [ ! -d "$1" ]
then
printf "%s\n" " $1 is not a directory"
exit 0
fi
directory="$1"
number="${directory##*/}"
number=${#number}
if [ $number -gt 0 ]
then
directory="$directory/"
fi
for line in ${directory}*
do
if [ -d "$line" ]
then
continue
else
counter=$(( $counter + 1))
fi
done
printf "%s\n" "Number of files in $directory :: $counter"
I would use (GNU) find and wc:
find /path/to/dir -maxdepth 1 -type f -printf '.' | wc -c
The above find command prints a dot for every file in the directory and wc -c counts those dots. This would work well with any kind of special character (including whitespaces and newlines) in the filenames.
You don't really need a loop. The following will count the files in a directory:
files=($(ls $1))
echo ${#files[#]}

Bash Script - Find File by User Input

I'm trying to compile a very simple bash script that will do the following actions (the script I have so far doesn't seem to function at all so I won't waste time putting this up for you to look at)
I need it to find files by their names. I need the script to take the user input and search the .waste directory for a match, should the folder be empty i'd need to echo out "No match was found because the folder is empty!", and just normally failing to find a match a simple "No match found."
I have defined: target=/home/user/bin/.waste
You can use the built in find command to do this
find /path/to/your/.waste -name 'filename.*' -print
Alternatively, you can set this as a function in your .bash_profile
searchwaste() {
find /path/to/your/.waste -name "$1" -print
}
Note that there are quotes around the $1. This will allow you to do file globbing.
searchwaste "*.txt"
The above command would search your .waste directory for any .txt files
Here you go, pretty straightforward script:
#!/usr/bin/env bash
target=/home/user/bin/.waste
if [ ! "$(ls -A $target)" ]; then
echo -e "Directory $target is empty"
exit 0
fi
found=0
while read line; do
found=$[found+1]
echo -e "Found: $line"
done < <(find "$target" -iname "*$1*" )
if [[ "$found" == "0" ]]; then
echo -e "No match for '$1'"
else
echo -e "Total: $found elements"
fi
Btw. in *nix world there are not folders, but there are directories :)
This is a solution.
#!/bin/bash
target="/home/user/bin/.waste"
read name
output=$( find "$target" -name "$name" 2> /dev/null )
if [[ -n "$output" ]]; then
echo "$output"
else
echo "No match found"
fi

Parameters or arguments in bash

I saw a question on stackflow about parsing arguments. I tried to write this, but it's not working and now it's getting on my nerves.
The usual way of running a script on the terminal is ./scriptname, but I later introduced the argument -d. So, if I put ./scriptname it will not run. If I put ./scriptname -d it will.
Now I want to put another argument for the path (where the files are moving, in this case "/home/elg19/documents") such that when I do not include the path, it won't run. But, if I put ./scriptname -d path I want to replace $To in the existing script with the command argument after -d.
#!/bin/bash
From="/home/mark/doc"
To=$2
if [ $1 = -d ]; then
cd "$From"
for i in pdf txt doc; do
find . -type f -name "*.${i}" -exec mv "{}" "$To" \;
done
fi
Your desired usage isn't completely clear, but it seems to be:
scriptname -d path
So, you can do it the extensible way, or the brute force way. Since you're changing directories willy-nilly, you also need to ensure that the paths are absolute, not relative.
Brute force
#!/bin/bash
From="/home/mark/doc"
if [ $# = 2 ] && [ "$1" = '-d' ] && [ -d $2 ]
then
case "$2" in
(/*) cd "$From" &&
for extn in pdf txt doc
do find . -type f -name "*.$extn" -exec mv {} "$To" \;
done;;
(*) echo "$0: path name must be absolute ($2 is not)" 1>&2; exit 1;;
esac
else
echo "Usage: $0 -d /absolute/dirname" 1>&2; exit 1
fi
Extensible
#!/bin/bash
From="/home/mark/doc"
To=""
usage()
{
echo "Usage: $(basename $0 .sh) -d /absolute/dirname" 1>&2
exit 1
}
while getopts d: opt
do
case "$opt" in
(d) if [ ! -d "$OPTARG" ]
then echo "$0: $OPTARG is not a directory" 1>&2; exit 1
else
case "$OPTARG" in
(/*) To="$OPTARG";;
(*) echo "$0: path name must be absolute ($2 is not)" 1>&2; exit 1;;
esac
fi;;
(*) usage;;
esac
done
shift $(($OPTIND - 1))
if [ $# != 0 ] || [ -z "$To" ]
then usage
fi
cd "$From" &&
for extn in pdf txt doc
do find . -type f -name "*.$extn" -exec mv {} "$To" \;
done
For example, it will be very easy to add a -f from option to deal with changing the source of the files.
Note that you could also use:
for extn in pdf txt doc
do find "$From" -type f -name "*.$extn" -exec mv {} "$To" \;
done
This would allow you to permit relative names for the 'from' and 'to' directories because it does not change directory.
I assume you want to do some input validation to your command line arguments. I guess the following would be somewhat useful:
#!/bin/bash
usage() {
echo "USAGE :"
echo "./move -d <to-directory>"
}
if [ $# -ne 2 ] ; then
usage
exit
fi
case $1 in
-d ) shift
To=$1
;;
* ) usage
exit
esac
From="/tmp/From/"
cd "$From"
for i in pdf txt doc; do
find . -type f -name "*.${i}" -exec mv "{}" "$To" \;
done
Moreover to debug your script, you may use the following command:
bash -x ./move.sh -d /tmp/To/
You may add more error checking (and informative echo's) for the following cases:
Source/destination directory does not exits
N files have been copied from the to
No files available at
You can take the type of files as arguments f.e. -t doc xls pdf

How to tell if the output of the "find" command is empty?

I want to return an exit status of 0 if the output is empty and 1 otherwise:
find /this/is/a/path/ -name core.*
When you say you want it to return a particular number, are you referring to the exit status? If so:
[[ -z `find /this/is/a/path/ -name core.*` ]]
And since you only care about a yes/no response, you may want to change your find to this:
[[ -z `find /this/is/a/path/ -name core.* -print -quit` ]]
which will stop after the first core file found. Without that, if the root directory is large, the find could take a while.
Here's my version. :)
[ -z "$(find /this/is/a/path/ -name 'core.*')" ] && true
Edited for brevity:
[ -z "$(find /this/is/a/path/ -name 'core.*')" ]
There are probably many variants, but this is one:
test $(find /this/is/a/path/ -name core.* | wc -c) -eq 0
Perhaps this
find /this/is/a/path/ -name 'core.*' | read
I am using this:
if test $(find $path -name value | wc -c) -eq 0
then
echo "has no value"
exit
else
echo "perfect !!!"
fi

Resources