How to test if multiple files exist using a Bash script - bash

How can I use the test command for an arbitrary number of files, passed in using an argument with a wildcard?
For example:
test -f /var/log/apache2/access.log.* && echo "exists one or more files"
Currently, it prints
error: bash: test: too many arguments

This solution seems to me more intuitive:
if [ `ls -1 /var/log/apache2/access.log.* 2>/dev/null | wc -l ` -gt 0 ];
then
echo "ok"
else
echo "ko"
fi

To avoid "too many arguments error", you need xargs. Unfortunately, test -f doesn't support multiple files. The following one-liner should work:
for i in /var/log/apache2/access.log.*; do test -f "$i" && echo "exists one or more files" && break; done
By the way, /var/log/apache2/access.log.* is called shell-globbing, not regexp. Please see Confusion with shell-globbing wildcards and Regex for more information.

First, store files in the directory as an array:
logfiles=(/var/log/apache2/access.log.*)
Then perform a test on the count of the array:
if [[ ${#logfiles[#]} -gt 0 ]]; then
echo 'At least one file found'
fi

This one is suitable for use with the Unofficial Bash Strict Mode, no has non-zero exit status when no files are found.
The array logfiles=(/var/log/apache2/access.log.*) will always contain at least the unexpanded glob, so one can simply test for existence of the first element:
logfiles=(/var/log/apache2/access.log.*)
if [[ -f ${logfiles[0]} ]]
then
echo 'At least one file found'
else
echo 'No file found'
fi

If you wanted a list of files to process as a batch, as opposed to doing a separate action for each file, you could use find, store the results in a variable, and then check if the variable was not empty. For example, I use the following to compile all the .java files in a source directory.
SRC=`find src -name "*.java"`
if [ ! -z $SRC ]; then
javac -classpath $CLASSPATH -d obj $SRC
# stop if compilation fails
if [ $? != 0 ]; then exit; fi
fi

You just need to test if ls has something to list:
ls /var/log/apache2/access.log.* >/dev/null 2>&1 && echo "exists one or more files"

Variation on a theme:
if ls /var/log/apache2/access.log.* >/dev/null 2>&1
then
echo 'At least one file found'
else
echo 'No file found'
fi

ls -1 /var/log/apache2/access.log.* | grep . && echo "One or more files exist."

Or using find
if [ $(find /var/log/apache2/ -type f -name "access.log.*" | wc -l) -gt 0 ]; then
echo "ok"
else
echo "ko"
fi

This condition below doesn't produce stderr. the condition's blackhole (/dev/null) doesn't prevent the stderr in cmd.
if [[ $(ls -1 /var/log/apache2/access.log.* | wc -l ) -gt 0 ]] 2> /dev/null
therefore I suggests this code.
if [[ $(ls -1 /var/log/apache2/access.log.* | wc -l ) -gt 0 ]] 2> /dev/null
then
echo "exists one or more files."
fi

more simplyfied:
if ls /var/log/apache2/access.log.* 2>/dev/null 1>&2; then
echo "ok"
else
echo "ko"
fi

Related

Check if a file exists inside a "Variable" Path

I am trying to find if a file exist in an iPhone application directory
Unfortunately, apps directory differs from a device to another
On my device, i use the following command to see if the file exists:
if [[ -f "/var/mobile/Applications/D0D2B991-3CDA-457B-9187-1F02A84FF3AB/AppName.app/filename.txt" ]]; then
echo "The File Exists";
else
echo "The File Does Not Exist";
fi
I want a command that would automatically search if the file exist without the need to specify the "variable" name inside the path.
I tried this:
if [[ -f "/var/mobile/Applications/*/AppName.app/filename.txt" ]]; then
echo "The File Exists";
else
echo "The File Does Not Exist";
fi
But no luck, it didn't find the file,
Maybe because i have 2 path of /var/mobile/Applications/*/AppName.app/ since i have cloned the app.
I would like to get a way to be able to find if the file filename.txt exists inside any folder named AppName.app inside this directory /var/mobile/Applications/*/
You can do this as follows:
[[ $(find /var/mobile/Applications/*/AppName.app/ -name filename.txt -print -quit | wc -l) -gt 0 ]] && echo "The File Exists" || echo "The File Does Not Exist"
The -f test can only take one argument. You would need to put it in a loop to check if some glob exists and its matches some regular file, i.e.
shopt -s nullglob
found=
for file in /var/mobile/Applications/*/AppName.app/filename.txt; do
[[ -f $file ]] && found=: && break
done
[[ -n $found ]] && echo "The File Exists" || echo "The File Does Not Exist"
If you're not sure specifically where the file is located you can use find, doing something like below which will exit early if found. (should work for gnu find, haven't tested on bsd)
if [[ -f $(find /some_root_directory -type f -name 'filename.txt' -print -quit) ]]; then
echo "The File Exists"
else
echo "The File Does Not Exist"
fi
# if a glob matches nothing, remove it instead of leaving the literal glob
shopt -s nullglob
# stick all matches in an array
files=( /var/mobile/Applications/*/AppName.app/filename.txt )
case "${#files[#]}" in
0 ) echo "Sorry, no such file." ;;
1 ) echo "The file exists: ${files[0]}" ;;
* ) echo "There are multiple files matching this pattern: ${files[*]}" ;;
esac
I like this technique for the purpose:
if find /var/mobile/Applications/*/AppName.app/ -name filename.txt -print -quit | grep -q .; then
echo "The File Exists"
else
echo "The File Does Not Exist"
fi
This has some advantages over this form:
[[ $(find ..... -print -quit | wc -l) -gt 0 ]]
Because:
It doesn't need a $() subshell
It doesn't need to count lines with wc
It doesn't need to compare numbers with the -gt operator
It doesn't need to be inside a [[ ... ]]
Basically it's a find ... | grep -q . versus [[ $(find ... | wc -l) -gt 0 ]]
Or find ... | grep -q . versus [[ -f $(find ...) ]]

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

Bash about repeat until

I want to know about that syntax is correct or not. I cant test it right now sorry, but its important for me. Its an FTP script. The file name is a.txt, I would like to create a script that will upload a file until it is successful. It will works or not? Anyone can help me to build the correct one pls
LOGFILE=/home/transfer_logs/$a.log
DIR=/home/send
Search=`ls /home/send`
firstline=`egrep "Connected" $LOGFILE`
secondline=`egrep "File successfully transferred" $LOGFILE`
if [ -z "$Search" ]; then
cd $DIR
ftp -p -v -i 192.163.3.3 < ../../example.script > ../../$LOGFILE 2>&1
fi
if
egrep "Not connected" $LOGFILE; then
repeat
ftp -p -v -i 192.163.3.3 < ../../example.script > ../../$LOGFILE 2>&1
until
[[ -n $firstline && $secondline ]];
done
fi
example.script contains:
binary
mput a.txt
quit
Does ftp not return a reasonable result? It would be easiest to write:
while ! ftp ...; do sleep 1; done
If you insist on searching the log file, do something like:
while :; do
ftp ... > $LOGFILE
grep -qF "File successfully transferred" $LOGFILE && break
done
Or
while ! test -e $LOGFILE || grep -qF "Not connected" $LOGFILE; do
ftp ... > $LOGFILE
done
It will works or not?
No, it won't work. According to §3.2.4.1 "Looping Constructs" of the Bash Reference Manual, these are the kinds of loops that exist:
until test-commands; do consequent-commands; done
while test-commands; do consequent-commands; done
for name [ [in [words …] ] ; ] do commands; done
for (( expr1 ; expr2 ; expr3 )) ; do commands ; done
You'll notice that none of them begins with repeat.
Additionally, these two lines:
firstline=`egrep "Connected" $LOGFILE`
secondline=`egrep "File successfully transferred" $LOGFILE`
run egrep immediately, and set their variables accordingly. This command:
[[ -n $firstline && $secondline ]]
will always give the same return-value, because nothing in the loop will ever modify $firstline and $secondline. You need to actually put an egrep command inside the loop.

How to check if a file contains a specific string using Bash

I want to check if a file contains a specific string or not in bash. I used this script, but it doesn't work:
if [[ 'grep 'SomeString' $File' ]];then
# Some Actions
fi
What's wrong in my code?
if grep -q SomeString "$File"; then
Some Actions # SomeString was found
fi
You don't need [[ ]] here. Just run the command directly. Add -q option when you don't need the string displayed when it was found.
The grep command returns 0 or 1 in the exit code depending on
the result of search. 0 if something was found; 1 otherwise.
$ echo hello | grep hi ; echo $?
1
$ echo hello | grep he ; echo $?
hello
0
$ echo hello | grep -q he ; echo $?
0
You can specify commands as an condition of if. If the command returns 0 in its exitcode that means that the condition is true; otherwise false.
$ if /bin/true; then echo that is true; fi
that is true
$ if /bin/false; then echo that is true; fi
$
As you can see you run here the programs directly. No additional [] or [[]].
In case if you want to check whether file does not contain a specific string, you can do it as follows.
if ! grep -q SomeString "$File"; then
Some Actions # SomeString was not found
fi
In addition to other answers, which told you how to do what you wanted, I try to explain what was wrong (which is what you wanted.
In Bash, if is to be followed with a command. If the exit code of this command is equal to 0, then the then part is executed, else the else part if any is executed.
You can do that with any command as explained in other answers: if /bin/true; then ...; fi
[[ is an internal bash command dedicated to some tests, like file existence, variable comparisons. Similarly [ is an external command (it is located typically in /usr/bin/[) that performs roughly the same tests but needs ] as a final argument, which is why ] must be padded with a space on the left, which is not the case with ]].
Here you needn't [[ nor [.
Another thing is the way you quote things. In bash, there is only one case where pairs of quotes do nest, it is "$(command "argument")". But in 'grep 'SomeString' $File' you have only one word, because 'grep ' is a quoted unit, which is concatenated with SomeString and then again concatenated with ' $File'. The variable $File is not even replaced with its value because of the use of single quotes. The proper way to do that is grep 'SomeString' "$File".
Shortest (correct) version:
grep -q "something" file; [ $? -eq 0 ] && echo "yes" || echo "no"
can be also written as
grep -q "something" file; test $? -eq 0 && echo "yes" || echo "no"
but you dont need to explicitly test it in this case, so the same with:
grep -q "something" file && echo "yes" || echo "no"
##To check for a particular string in a file
cd PATH_TO_YOUR_DIRECTORY #Changing directory to your working directory
File=YOUR_FILENAME
if grep -q STRING_YOU_ARE_CHECKING_FOR "$File"; ##note the space after the string you are searching for
then
echo "Hooray!!It's available"
else
echo "Oops!!Not available"
fi
grep -q [PATTERN] [FILE] && echo $?
The exit status is 0 (true) if the pattern was found; otherwise blankstring.
if grep -q [string] [filename]
then
[whatever action]
fi
Example
if grep -q 'my cat is in a tree' /tmp/cat.txt
then
mkdir cat
fi
In case you want to checkif the string matches the whole line and if it is a fixed string, You can do it this way
grep -Fxq [String] [filePath]
example
searchString="Hello World"
file="./test.log"
if grep -Fxq "$searchString" $file
then
echo "String found in $file"
else
echo "String not found in $file"
fi
From the man file:
-F, --fixed-strings
Interpret PATTERN as a list of fixed strings, separated by newlines, any of
which is to be matched.
(-F is specified by POSIX.)
-x, --line-regexp
Select only those matches that exactly match the whole line. (-x is specified by
POSIX.)
-q, --quiet, --silent
Quiet; do not write anything to standard output. Exit immediately with zero
status if any match is
found, even if an error was detected. Also see the -s or --no-messages
option. (-q is specified by
POSIX.)
Try this:
if [[ $(grep "SomeString" $File) ]] ; then
echo "Found"
else
echo "Not Found"
fi
I done this, seems to work fine
if grep $SearchTerm $FileToSearch; then
echo "$SearchTerm found OK"
else
echo "$SearchTerm not found"
fi
grep -q "something" file
[[ !? -eq 0 ]] && echo "yes" || echo "no"

sh: Test for existence of files

How does one test for the existence of files in a directory using bash?
if ... ; then
echo 'Found some!'
fi
To be clear, I don't want to test for the existence of a specific file. I would like to test if a specific directory contains any files.
I went with:
(
shopt -s dotglob nullglob
existing_files=( ./* )
if [[ ${#existing_files[#]} -gt 0 ]] ; then
some_command "${existing_files[#]}"
fi
)
Using the array avoids race conditions from reading the file list twice.
From the man page:
-f file
True if file exists and is a regular file.
So:
if [ -f someFileName ]; then echo 'Found some!'; fi
Edit: I see you already got the answer, but for completeness, you can use the info in Checking from shell script if a directory contains files - and lose the dotglob option if you want hidden files ignored.
I typically just use a cheap ls -A to see if there's a response.
Pseudo-maybe-correct-syntax-example-ahoy:
if [[ $(ls -A my_directory_path_variable ) ]] then....
edit, this will work:
myDir=(./*) if [ ${#myDir[#]} -gt 1 ]; then echo "there's something down here"; fi
You can use ls in an if statement thus:
if [[ "$(ls -a1 | egrep -v '^\.$|^\.\.$')" = "" ]] ; then echo empty ; fi
or, thanks to ikegami,
if [[ "$(ls -A)" = "" ]] ; then echo empty ; fi
or, even shorter:
if [[ -z "$(ls -A)" ]] ; then echo empty ; fi
These basically list all files in the current directory (including hidden ones) that are neither . nor ...
If that list is empty, then the directory is empty.
If you want to discount hidden files, you can simplify it to:
if [[ "$(ls)" = "" ]] ; then echo empty ; fi
A bash-only solution (no invoking external programs like ls or egrep) can be done as follows:
emp=Y; for i in *; do if [[ $i != "*" ]]; then emp=N; break; fi; done; echo $emp
It's not the prettiest code in the world, it simply sets emp to Y and then, for every real file, sets it to N and breaks from the for loop for efficiency. If there were zero files, it stays as Y.
Try this
if [ -f /tmp/foo.txt ]
then
echo the file exists
fi
ref: http://tldp.org/LDP/abs/html/fto.html
you may also want to check this out: http://tldp.org/LDP/abs/html/fto.html
How about this for whether directory is empty or not
$ find "/tmp" -type f -exec echo Found file {} \;
#!/bin/bash
if [ -e $1 ]; then
echo "File exists"
else
echo "Files does not exist"
fi
I don't have a good pure sh/bash solution, but it's easy to do in Perl:
#!/usr/bin/perl
use strict;
use warnings;
die "Usage: $0 dir\n" if scalar #ARGV != 1 or not -d $ARGV[0];
opendir my $DIR, $ARGV[0] or die "$ARGV[0]: $!\n";
my #files = readdir $DIR;
closedir $DIR;
if (scalar #files == 2) { # . and ..
exit 0;
}
else {
exit 1;
}
Call it something like emptydir and put it somewhere in your $PATH, then:
if emptydir dir ; then
echo "dir is empty"
else
echo "dir is not empty"
fi
It dies with an error message if you give it no arguments, two or more arguments, or an argument that isn't a directory; it's easy enough to change if you prefer different behavior.
# tested on Linux BASH
directory=$1
if test $(stat -c %h $directory) -gt 2;
then
echo "not empty"
else
echo "empty"
fi
For fun:
if ( shopt -s nullglob ; perl -e'exit !#ARGV' ./* ) ; then
echo 'Found some!'
fi
(Doesn't check for hidden files)

Resources