#!/bin/bash
file=/home/yaal/temp/hatch/*;
if [[ -f $file ]]; then
echo $file
else
echo "No files found"
fi
I have files under hatch dir but it is displaying "No files found". why is it?
Thanks !!
Pathname expansion does not occur on the right-hand side of a non-array variable assignment, so file contains a literal *, not a list of file names. Pathname expansion is not performed inside [[ ... ]] either, so you are asking if there is a file named * exists in /home/yaal/temp/hatch`.
If you just want to know if there is at least one file (excluding files starting with .) in hatch, try
for f in /home/yaal/temp/hatch/*; do
if [[ -f $f ]]; then
echo "$file"
else
echo "No files found"
fi
break
done
You can also fill an array, then check if it is empty or not:
files=( /home/yaal/temp/hatch/* )
if (( ${#files[#]} > 0 )); then
echo "${files[0]}" # First file found
else
echo "No files found"
fi
If you do want to consider file names beginning with ., either use shopt -s dotglob, or use two patterns /home/yaal/temp/hatch/* /home/yaal/temp/hatch/.*.
Ideally you'd want to use for loop to check if file exists because the pathname expansion does not happen inside [[ ... ]]. Use something like:
#!/bin/bash
for file in /home/yaal/temp/hatch/*; do
if [[ -f $file ]]; then
echo $file
else
echo "No files found"
fi
done
Related
I have an array of possible file extensions, which contains some wild cards e.g.:
FILETYPES=("DBG" "MSG" "OUT" "output*.txt")
I also have a list of files, which I am grabbing the file extension from. I then need to compare the extension with the array of file extensions.
I have tried:
if [[ ${EXTENSION} =~ "${FILETYPES[*]}" ]]; then
echo "file found"
fi
if [[ ${EXTENSION} == "${FILETYPES[*]}" ]]; then
echo "file found"
fi
and
if [[ ${EXTENSION} =~ "${FILETYPES[*]}" ]]; then
echo "file found"
fi
But to no avail
I tried:
if [[ "${FILETYPES[*]}" =~ ${EXTENSION} ]]; then
echo "file found"
fi
However, it ended up comparing "txt" to "output*.txt" and concluding it was a match.
FILETYPES=("DBG" "MSG" "OUT" "output*.txt") First of all, avoid ALL_CAPS variable names except if these are meant as global environment variables.
"output*.txt": is ok as a globing pattern, for bash test [[ $variable == output*.txt ]] for example. But for Regex matching it needs a different syntax like [[ $variable =~ output.*\.txt ]]
"${FILETYPES[*]}": Expanding this array into a single_string was mostly a good approach, but it needs clever use of the IFS environment variable to help it expands into a Regex. Something like IFS='|' regex_fragment="(${array[*]})", so that each array entry will be expanded, separated by a pipe | and enclosed in parenthesis as (entry1|entry2|...).
Here is an implementation you could use:
textscript.sh
#!/usr/bin/env bash
extensions_regexes=("DBG" "MSG" "OUT" "output.*\.txt")
# Expands the extensions regexes into a proper regex string
IFS='|' regex=".*\.(${extensions_regexes[*]})"
# Prints the regex for debug purposes
printf %s\\n "$regex"
# Iterate all filenames passed as argument to the script
for filename; do
# Compare the filename with the regex
if [[ $filename =~ $regex ]]; then
printf 'file found: %s \n' "$filename"
fi
done
Sample usage:
$ touch foobar.MSG foobar.output.txt
$ bash testscript.sh *
.*\.(DBG|MSG|OUT|output.*\.txt)
file found: foobar.MSG
file found: foobar.output.txt
You cannot directly compare a string with an array. Would you please try something like:
filetypes=("DBG" "MSG" "OUT" "output*.txt")
extension="MSG" # example
match=0
for type in "${filetypes[#]}"; do
if [[ $extension = $type ]]; then
match=1
break
fi
done
echo "$match"
You can save looping with regex:
pat="^(DBG|MSG|OUT|output.*\.txt)$"
extension="output_foo.txt" # example
match=0
if [[ $extension =~ $pat ]]; then
match=1
fi
echo "$match"
Please note the expressions of regex differ from wildcards for globbing.
As a side note, we conventionally do not use uppercases for user variables to avoid conflicts with system variables.
I have a bash script that requires a glob expression as a parameter. However I am having trouble using inputs as globs i.e say my input is
Shell_script '*.c'
and my code is iterating through an array of files and filtering them through pattern matching. In this case files which do not have the .c extension. (In this example, the first input could be any pattern otherwise)
count=${#array[#]}
for (( q = 0; q < count; q++ ));
do
if [[ ${array[q]} == $1 ]]; then
:
else unset array[q]
fi
done
.....
Any ideas?
Matching array contents against a glob is entirely possible:
#!/bin/bash
# this array has noncontiguous indexes to demonstrate a potential bug in the original code
array=( [0]="hello.c" [3]="cruel.txt" [5]="world.c" )
glob=$1
for idx in "${!array[#]}"; do
val=${array[$idx]}
if [[ $val = $glob ]]; then
echo "File $val matches glob expression $glob" >&2
else
echo "File $val does not match glob expression $glob; removing" >&2
unset array[$idx]
fi
done
Similarly, you can expand a glob against filesystem contents, though you'll want to clear IFS first to avoid string-splitting:
# here, the expectation is that your script would be invoked as: ./yourscript '*.c'
IFS=
for f in $1; do
[[ -e $f || -L $f ]] || { echo "No file matching $f found" >&2; }
echo "Iterating over file $f"
done
That said, in general, this is extremely unidiomatic, as opposed to letting the calling shell expand the glob before your script is started, and reading the list of matched files off your argument vector. Thus:
# written this way, your script can just be called ./yourscript *.c
for f; do
[[ -e $f || -L $f ]] || { echo "No file matching $f found" >&2; }
echo "Iterating over file $f"
done
You can loop over your list of files like this. If you run your script as
./test.sh "*.c". Then inside your script you can do:
for file in $1
do
#use your file
done
Current directory contains new logs that keep coming.
/tmp/logstash/ dir contains logs to which I will be comparing new ones
Conditions:
If the new log has the same name and the size that already exists in /tmp/logstash, I should get 'identical file already exists' msg.
Otherwise the script will move the new log to /tmp/logstash/.
Note, that if name is same but size is different, the script should still move new file to tmp/logstash/
My script is as follows and it's not working properly with with combining 'then && if', can you please help to fix it?
for file in *.log; do
new_filesize=$(du -b "$file" | cut -f 1)
if [[ -e /tmp/logstash/"$file" ]]
then
old_filesize=$(du -b /tmp/logstash/"$file" | cut -f 1) &&
if [[ "$new_filesize"="$old_filesize" ]]; then
echo "The file already exists"
fi
else mv $file /tmp/logstash
fi
done
You need spaces around the = in the conditional expression:
if [[ $new_filesize = $old_filesize ]]; then
Without the spaces, you're just testing whether the concatenated string "$new_filesize"="$old_filesize" is non-empty.
Test for Existence, if so Test Filesize, otherwise Copy
Per your request in the comments. The following tests whether old_file exists. If it does, it then checks whether the sizes between new_file and old_file differ. If they differ, then it moves new_file to /tmp/logstash/ replacing old_file. If old_file exists and the files sizes are equal, then it will echo "The file already exists". In the event old_file doesn't exist, then is simply copies new_file to /tmp/logstash/.
for file in *.log; do
if [ -e /tmp/logstash/"$file" ]; then
if [ $(stat %s "$file") -ne $(stat %s /tmp/logstash/"$file") ]
mv -f "$file" /tmp/logstash
else
echo "The file already exists"
fi
else
cp "$file" /tmp/logstash/"$file"
fi
done
Note: Remember quote your variables.
With Variables new_filesize and old_filesize
for file in *.log; do
new_filesize=$(stat %s "$file")
if [ -e /tmp/logstash/"$file" ]; then
old_filesize=$(stat %s /tmp/logstash/"$file")
if [ $new_filesize -ne $old_filesize ]
mv -f "$file" /tmp/logstash
else
echo "The file already exists"
fi
else
cp "$file" /tmp/logstash/"$file"
fi
done
Note: mv -f was added to all cases where old_file exists to prevent move failure due to existing file.
I have a bunch of images that I need to rename, so I can use them and I was wondering how to do this.
The way they need to be is that first 5 will be kept and then for the 6th I would write a number from 1-3. I only know that the first 5 are static; on pics belonging to same "family" and can be used for comparison and the 6th char is not known.
Example:
12345random.jpg
12345randomer.jpg
0987654more_random.jpg
09876awesome.jpg
09876awesomer.jpg
09876awesomest.jpg
09876soawesomegalaxiesexplode.jpg
would become.
12345.jpg
123452.jpg
09876.jpg
098761.jpg
098762.jpg
It would be cool if it would only handle the loop so that 3 pics could be only renamed and rest skipped.
I found some stuff on removing letters to certain point, but nothing that use, since I am quite poor at bash scripting.
Here is my approach, but it kind of sucks, since I tried modifying scripts I found, but the idea is there
//I could not figure how to remove the chars after 5th not the other way around
for file in .....*; do echo mv $file `echo $file | cut -c6-`; done
done
//problem also is that once the names conflict it produces only 1 file named 12345.jpg 2nd one will not be created
//do not know how to read file names to array
name=somefile
if [[ -e $name.jpg]] ; then
i=0
while [[ -e $name-$i.jpg]] ; do
let i++
done
name=$name-$i
fi
touch $name.jpg
You can have:
new_file=${file%%[^0-9]*.jpg}.jpg
As a concept you can have this to rename files:
for file in *.jpg; do
[[ $file == [0-9]*[^0-9]*.jpg ]] || continue ## Just a simple check.
new_file=${file%%[^0-9]*.jpg}.jpg
[[ -e $new_file ]] || continue ## Do not overwrite. Delete line if not wanted.
echo "Renaming $file to $new_file." ## Optional message.
mv -- "$file" "$new_file" || echo "Failed to rename $file to $new_file."
done
If you're going to process files that also contain directory names, you'll need some more changes:
for file in /path/to/other/dirs/*.jpg *.jpg; do
base=${file##*/}
[[ $base == [0-9]*[^0-9]*.jpg ]] || continue
if [[ $file == */* ]]; then
new_file=${file%/*}/${base%%[^0-9]*.jpg}.jpg
else
new_file=${file%%[^0-9]*.jpg}.jpg
fi
[[ -e $new_file ]] || continue
echo "Renaming $file to $new_file."
mv -- "$file" "$new_file"
done
you can also try the following code
but be careful all the files should be in .jpg format and pass the name of folder as an argument
#!/bin/bash
a=`ls $1`
for b in $a
do
echo $b
if (( i<4 ))
then
c=`echo $b | cut -c1-5`
let i=i+1
c="$c$i.jpg"
echo $c
else
c=`echo $b | cut -c1-5`
c="$c.jpg"
break
fi
mv $1$b $1$c
done
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)