How to search for multiple file extensions from shell script - bash

for file in "$1"/*
do
if [ ! -d "${file}" ] ; then
if [[ $file == *.c ]]
then
blah
blah
Above code traverses all the .c files in a directory and does some action.I want to include .cpp,.h,.cc files as well.How can I check multiple file extensions in the same if condition ?
Thanks

You can combine conditions using boolean operators :
if [[ "$file" == *.c ]] || [[ "$file" == *.cpp ]] || [[ "$file" == *.h ]] || [[ "$file" == *.cc ]]; then
#...
fi
Another alternative would be to use a regex :
if [[ "$file" =~ \.(c|cpp|h|cc)$ ]]; then
#...
fi

Using extended patterns,
# Only necessary prior to bash 4.1; since then,
# extglob is temporarily turn on for the pattern argument to !=
# and =/== inside [[ ... ]]
shopt -s extglob nullglob
for file in "$1"/*; do
if [[ -f $file && $file = *.#(c|cc|cpp|h) ]]; then
...
fi
done
The extended pattern can also be to generate the file list; in that case, you definitely need the shopt command:
shopt -s extglob nullglob
for file in "$1"/*.#(c|cc|cpp|h); do
...
done

Why not just iterate over selected file extensions?
#!/bin/bash
for file in ${1}/*.[ch] ${1}/*.cc ${1}/*.cpp; do
if [ -f $file ]; then
# action for the file
echo $file
fi
done

Related

BASH test if file name ends with .dylib

I'm walking a file tree in order to identify all .DYLIB files.
#!/bin/bash
#script to recursively travel a dir of n levels
function traverse() {
for file in "$1"/*
do
if [ ! -d "${file}" ] ; then
echo "${file} is a file"
else
echo "entering recursion with: ${file}"
traverse "${file}"
fi
done
}
function main() {
traverse "$1"
}
main "$1"
I want to test if the filename ends with .DYLIB before printing "... is a file". I think I may need to add to the condition "if [ ! -d "${file}" ] ; then", but I'm not sure. Is there a way to do this in bash?
No need to write your own recursive function. You can recursively find all *.dylib files using a ** glob:
shopt -s globstar
ls "$1"/**/*.dylib
Or use find:
find "$1" -name '*.dylib'
To use these results I recommend looping over them directly. It avoids using up memory with a temporary array.
shopt -s globstar
for file in "$1"/**/*.dylib; do
echo "$file"
done
or
while IFS= read -rd '' file; do
echo "$file"
done < <(find "$1" -name '*.dylib')
Is there a way I can store everything in a string array so that I can perform an operation on those .dylib files?
But if you do indeed want an array, you can write:
shopt -s globstar
files=("$1"/**/*.dylib)
or
readarray -td '' files < <(find "$1" -name '*.dylib')
Then to loop over the array you'd write:
for file in "${files[#]}"; do
echo "$file"
done
Don't add to the condition in if [ ! -d "$file" ], because then the else block will try to recurse into files that don't have the suffix. But recursion should only be for directories.
You should add a nested condition for this. You can use bash's [[ ]] condition operator to have = perform wildcard matching.
if [ ! -d "${file}" ] ; then
if [[ "$file" = *.DYLIB ]]; then
echo "${file} is a file"
fi
else
echo "entering recursion with: ${file}"
traverse "${file}"
fi
Or you could invert the sense of the directory test:
if [ -d "$file" ]; then
echo "entering recursion with: ${file}"
traverse "${file}"
elif [ "$file" = *.DYLIB ]; then
echo "$file is a file"
fi

check if file has an appropriate suffix bash

FileI wrote this
function copyFile() {
local source=$1
set -x
for dictionary in $DICT_PATH; do
dictname=$(basename $dictionary)
dict_prefix=${dictname%%.*}
TARGET="gs://bucket/files"
gsutil cp -r $dictionary $TARGET
done
}
I want to add a condition to copy only files whose termination is .json or .xml
I wrote this
function copyFile() {
local source=$1
set -x
for dictionary in $DICT_PATH; do
dictname=$(basename $dictionary)
if [[ ${dictname: -5} == ".json" ]] || [[ ${dictname: -5} == ".xml" ]] ; then
dict_prefix=${dictname%%.*}
TARGET="gs://bucket/files"
gsutil cp -r $dictionary $TARGET
fi
done
}
but this didn't work. Any idea how to fix this please.
xml is a shorter string than json, so your suffix is too long to compare equal to .xml.
# -4, not -5
if [[ ${dictname: -5} == ".json" ]] || [[ ${dictname: -4} == ".xml" ]] ; then
You can avoid this mistake by using the much simpler pattern-matching facilities of [[ ... ]].
if [[ $dictname = *.json || $dictname = *.xml ]]; then
or even a POSIX-compatible case statement:
case $dictname in
*.json|*.xml)
dict_prefix=${dictname%%.*}
TARGET="gs://bucket/files"
gsutil cp -r "$dictionary" "$TARGET"
;;
sac
You can extract the file extension as ${filename#*.}.
This should give something like following,
ext=${dictname#*.}
if [[ $ext == 'json']] || [[ $ext == 'xml' ]]; then
# code
fi
Or, use regular expressions,
if [[ $dictname =~ (json|xml)$ ]]; then
# code
fi
Try this:
filetype=${dictionary##*.}
if [[ "$filetype" == "json" ]] || [[ "$filetype" == "xml" ]]; then
echo YES
fi

Filename prefix test always failing in bash

i have a question about using shell to read files. That is to say, i have a folder like this:
folder
new_file.log (this is a file)
new_file2.log (this is a file)
new_file3.log (this is a file)
new (this is a subfolder)
new_file_from_subfolder.log
new_file2_from_subfolder.log
new_file3_from_subfolder.log
what i want is to read all the content from (direct) files, not files from the subfolder. In the above case, i need new_file.log to new_file3.log.
I know there is a simple way:
$ cat new*.log
but i also like to write a bash script:
for file in $(ls -a)
do
if [[ "$file" != "." && "$file" != ".." ]]; then
if [[ -f "$file" && "$file" == "^new" ]]; then **here is the problem**
[do something]
fi
fi
done
my problem is labeled as above. the bash code seems doesnot like
"$file" == ^new
if i run the bash, it basically does nothing, which means that files fail to meet the condition.
anything wrong?
[[ $foo = $bar ]] is a glob expression, not a regex; ^ has no special meaning there.
You probably want either the glob expression
[[ $file = new* ]]
or the regex
[[ $file =~ ^new ]]
Of course, in a real-world scenario, you'd just iterate only over the names that match your prefix:
for file in new*; do
: something with "$file"
done
...or, recursively (using FD 3 so you can still interact with the user in this code):
while IFS= read -u 3 -r -d '' file; do
: something with "$file"
done 3< <(find . -type f -name 'new*' -print0)
You're headed down the wrong track. Here's how to iterate over all regular files starting with new:
for file in new*
do
if [[ -f $file ]]
then
dosomething "$file"
fi
done

Bash script - Check if a file exists

I need to check if a file with a specific extension exists and if it is not empty.
The problem is that I don't know the name of the file, just the extension and the path.
Here my solution with an known name
FILE="/Users/test/my.out"
if [[ -f $FILE && -s $FILE ]] ; then echo "EXIST"; fi
Using
FILE="/Users/test/*.out"
Do not work
I suggest you use an array, in case there are multiple files:
arr=( /Users/test/*.out )
if (( ${#arr[#]} > 0 )) && [[ -s "${arr[0]}" ]]
then
...
fi
You can either use #dogbane's solution using an array or use a loop:
dir=/users/test
for file in "${dir}"/*.out; do
break
done
if [ -f "${file}" ] && [ -s "${file}" ]; then
echo "found a regular, non-empty .out file: ${file}"
fi
I think this should do:
if [ -f ${var} ]; then
if [ -s ${var} ]; then
echo "Found $var"
fi
fi
Try
FILE=$(ls "Downloads/*.zip" 2>/dev/null)
Only works, if there is only a single file.

Check if at least one file exists in bash

I have this
if [[ -e file.jpg ]] ;then echo "aaaaaaaaa"; fi
and it prints "aaaaaaa"
but I want to print if there is file.png or file.png also
so I need something like this
if [[ -e file.* ]] ;then echo "aaaaaaaaa"; fi
but it doesn't work I am missing something in the syntax
Thanks
If you enable bash's nullglob setting, the pattern file.* will expand to an empty string if there are no such files:
shopt -s nullglob
files=(file.*)
# now check the size of the array
if (( ${#files[#]} == 0 )); then
echo "no such files"
else
echo "at least one:"
printf "aaaaaaaaa %s\n" "${files[#]}"
fi
If you do not enable nullglob, then files=(file.*) will result in an array with one element, the string "file.*"
Why not use a loop ?
for i in file.*; do
if [[ -e $i ]]; then
# exists...
fi
done

Resources