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
Related
I have a file named my.txt:
abc_default_flow
#abc_default_flow -p sam
abc_default_flow -p sam
# abc_default_flow -p david
abc_default_flow -p david -z what_is_it
I want to match a particular line which has multiple strings and want to match the exact line which contains all the strings.
I tried the below piece of code, but as soon as it matches the partial string it comes out of the loop, rather than actual line which contains all the strings.
#!/bin/bash -f
f_name=abc_default_flow
p_name=sam
file_content=./my.txt
#echo "file_content: $file_content"
while IFS= read -r file_line
do
echo $file_line
if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]] && echo $file_line | grep -E "${f_name}|${p_name}"; then
#if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]] && echo $file_line | grep -v "${f_name}\|${p_name}"; then
#if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]] && [[ $file_line =~ $f_name ]] && [[ $file_line =~ $p_name ]]; then
if [[ -z "$p_name" ]]; then
f_name=${f_name}_${p_name}
fi
echo "f_name: ${f_name}"
break
fi
done < $file_content
What would be the right way to grep or use any other process to find the right line within the file?
Update: With the below code I am able to get the output, but is there any simple way with grep or sed or awk to find the result in single line instead of nested if loops.
#!/bin/bash -f
f_name=abc_default_flow
p_name=david
file_content=./my.txt
echo "f_name: $f_name, p_name: $p_name"
while IFS= read -r file_line
do
if [[ $file_line != *"#"* ]] && [[ $file_line != "" ]]; then
echo "l1"
if [[ ! -z "$f_name" ]] && [[ $file_line =~ "$f_name" ]]; then
echo "l2, $file_line, $f_name, $p_name"
if [[ ! -z "$p_name" ]] && [[ $file_line =~ "$p_name" ]]; then
f_name=${f_name}_${p_name}
echo "f_name: ${f_name}"
break
elif [[ ! -z "$p_name" ]] && [[ ! $file_line =~ "$p_name" ]]; then
continue
else
break
fi
fi
fi
done < $file_content
You could use the same [[ ]] style checking for the two strings you're looking for:
if [[ $file_line != *"#"* ]] && [[ $file_line == *"$f_name"* ]] && [[ $file_line == *"$p_name"* ]]; then
...
fi
I removed the empty string check since an empty line won't contain $f_name and $p_name anyways.
If you expect sam will always come after abc_default_flow then you could combine the two checks into a single test:
if [[ $file_line != *"#"* ]] && [[ $file_line == *"$f_name"*"$p_name"* ]]; then
...
fi
If we look at the script as a whole, it'd be nice to get away from the explicit line-by-line loop. Scripts are more idiomatic when they chain together tools that process entire files. Something like:
sed -r '/^\s*#/d' my.txt | grep "$f_name" | grep "$p_name"
I am trying to call an external bash script in an if condition in my main script.
The code of the external script IsArchive:
#!/bin/bash
STR="$1"
if [[ "$STR" == *".zip"* ]] || [[ "$STR" == *".iso"* ]] || [[ "$STR" == *".tar.gxz"* ]] || [[ "$STR" == *".tar.gx"* ]] || [[ "$STR" == *".tar.bz2"* ]] || \
[[ "$STR" == *".tar.gz"* ]] || [[ "$STR" == *".tar.xz"* ]] || [[ "$STR" == *".tgz"* ]] || [[ "$STR" == *".tbz2"* ]]
then
return 0
else
return 1
fi
and I try calling it in my main script as:
elif [[ $Option = "2" ]]
then
if IsArchive "$SourcePath";
then
less -1Ras "$SourcePath" | tee "$OutputFilePath"
#if file is not an archive
else
ls -1Rasl "$SourcePath" | tee "$OutputFilePath"
fi
when I execute the main script I receive the Error: ./script: line 61: IsArchive: command not found
You just need to make sure that the script is in your PATH. Either that, or reference it with either a full path or a relative path. Perhaps you just need to write:
if ./IsArchive "$SourcePath"; then ...
But there are several issues with IsArchive. You cannot return except from a function, so you probably want to use exit 0 and exit 1 instead of return. You probably don't want to consider a name like foo.zipadeedoodah to be an archive, but *".zip"* will match that, so you should probably remove the trailing *. It would be simpler to write it with a case statement:
#!/bin/bash
case "$1" in
*.zip|*.iso|*.tar.gxz|*.tar.gx|*.tar.bz2| \
*.tar.gz|*.tar.xz|*.tgz|*.tbz2) exit 0;;
*) exit 1;;
esac
I have the following code to test if the file held in $arg is not one of several file extensions. In an effort to learn I want to know if there is a more correct, more compact way to do this.
if ! [[ -f "$arg" && ( $arg = *.mkv || $arg = *.mp4 || $arg = *.mp3 || $arg = *.flac || $arg = *.avi) ]] ; then
echo -e "\"$arg\" is an invalid directory, filename or file format\n"
fi
Maybe wrap it up in a function?
has_element() {
local item="$1"
shift
local e
for e; do
[[ "$e" == "$item" ]] && return 0
done
return 1
}
suffix="${arg##*.}"
if has_element "$suffix" mkv mp4 mp3 flac abcdefg hijklmn; then
echo "$suffix is in one of the extensions"
else
echo "$suffix is not in one of the extensions"
fi
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
I made a short script that changes all files with one extension to a different extension. Both extensions are inputted by the user through command line arguments. I put in an if statement to handle errors but for some reason it considers everything an error and I am not sure why. I have pasted the script below. I am rather new to bash scripting so any help would be greatly appreciated!
if [[ "$#" == 0 ]] || [[ "$1" || "$2" != "."* ]]
then
echo "Parameters are not valid"
exit
fi
for f in *"$1"; do
name=${f%.*}
mv $f "$name$2"
done
[[ "$1" || "$2" != "."* ]] should be [[ "$1" != .* ]] || [[ "$2" != .* ]]