Can't iterate correctly over specific files in folder with endings {*-enterprise.ipa,*.apk} - bash

I have a folder containing these app files:
mles:fairs-ionic mles$ ls build
build/com.solutions.enterprise.fairs.dev_v2.2.0.17_20170720_1423-enterprise.ipa
build/com.solutions.enterprise.fairs_v2.2.0.17_20170720_1423-enterprise.ipa
build/de.fairs.dev_v2.2.0.17_20170720_1423-enterprise.apk
build/de.fairs_v2.2.0.17_20170720_1423-appstore.ipa
build/de.fairs_v2.2.0.17_20170720_1423-enterprise.apk
I need to uppload all of them except the *-appstore.ipa one. More specifically these one:
mles:fairs-ionic mles$ ls build/{*-enterprise.ipa,*.apk}
build/com.solutions.enterprise.fairs.dev_v2.2.0.17_20170720_1423-enterprise.ipa
build/com.solutions.enterprise.fairs_v2.2.0.17_20170720_1423-enterprise.ipa
build/de.fairs.dev_v2.2.0.17_20170720_1423-enterprise.apk
build/de.fairs_v2.2.0.17_20170720_1423-enterprise.apk
In my bash script I've tried:
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
appFiles=($(cd ${DIR}/build;ls {*-enterprise.ipa,*.apk}))
echo ${appFiles};
echo "loop"
for appFile in "${appFiles[#]}"
do
echo ${appfile}
#app_upload "${appfile}"
done
this yields:
mles:fairs-ionic ben$ ./test.sh
com.solutions.enterprise.fairs.dev_v2.2.0.17_20170720_1423-enterprise.ipa
loop
appFiles only contains one row, and the appfile variable in the loop is always empty.
How can I iterate over all the files in the build folder except the .ipa files with appstore in the filename (build/de.fairs_v2.2.0.17_20170720_1423-appstore.ipa) ?

Variable appfile is not defined only appFile, So change
echo ${appfile}
to
echo ${appFile}
Edit the line displaying all the data
echo ${appFiles};
To
echo ${appFiles[*]};

You can just do this simply with extended glob features (see Options which change globbing behavior) provided by bash, turn the options on and stash the values to an array. Run it outside the build/ folder.
shopt -s extglob nullglob
fileList=( build/!(*-appstore.ipa) )
now loop over the array and do whatever you want to do with it.
for file in "${fileList[#]}"; do
printf "%s\n" "$file"
done

You don't need to create an array of selected files as you can directly iterate them using a glob with brace expansion like this:
for file in build/*{-enterprise.ipa,.apk}; do
echo "$file"
# app_upload "$file"
done

Related

What to do to make loop ignore empty directories

I have a loop and I need it to ignore empty directories.
for i in */*/
do
cd "$i"
mv ./*.py ..
cd -
rm -r "$i"
done
What can i add on to make it ignore empty directories?
I have this but I would like something simpler
x=$(shopt -s nullglob dotglob; echo "$i"/*)
(( ${#x} )) || continue
What can i add on to make it ignore empty directories?
Bash does not have a primitive operator for testing whether a directory is empty. The best alternative in your case is probably to test whether pathname expansion matches any files within. That's what you are already considering, though I would write it differently.
As a general rule, I would also avoid changing the working directory. If you must change directory then consider doing it in a subshell, so that you need only let the subshell terminate to revert to the original working directory. Using a subshell is also a good approach when different parts of your script want different shell options.
I would probably write your script like this:
#!/bin/bash
shopt -s nullglob dotglob
for i in */*/; do
anyfiles=( "$i"/* )
if [[ ${#anyfiles[#]} -ne 0 ]]; then
# Process nonempty directory "$i"
# If there are any Python files within then move them to the parent directory
pyfiles=( "$i"/*.py )
if [[ ${#pyfiles[#]} -ne 0 ]]; then
mv "${pyfiles[#]}" "$(dirname "$i")"
fi
# Remove directory "$i" and any remaining contents
rm -r "$i"
fi
done
If you want that as part of a larger script, then you could put everything from the shopt to the end in a subshell to limit the scope of the shopt.
Alternatively, you could simplify that slightly at the cost of some clarity by using loops to skip the capture of the directory contents into explicit variables:
#!/bin/bash
shopt -s nullglob dotglob
for i in */*/; do
for anyfile in "$i"/*; do
# Process nonempty directory "$i"
# If there are any Python files within then move them to the parent directory
for pyfile in "$i"/*.py; do
mv "$i"/*.py "$(dirname "$i")"
break
done
# Remove directory "$i" and any remaining contents
rm -r "$i"
break
done
done
In that case, each inner loop contains an unconditional break at the end, so at most one iteration will be performed.

Why does ''rm "$dir"/* !(.gitignore)'' delete the script itself?

I have this shell script that I'm using to clean up some temp files.
The script is stored in: /root/cronjobs.
When I run the script from this location ./cleanUploader.sh, it deletes all the files in the current folder along with itself.
Here's the script:
#!/bin/bash
# cleanUploader.sh
# Batch file to remove various temp directories and files left over from the Uploader
clear
echo
INHOUSEFILES=/var/www/html/inhouseweb/officedb/uploader/files
shopt -s extglob
if [ -d $INHOUSEFILES ]; then
echo "Removing directory $INHOUSEFILES"
rm -rf $INHOUSEFILES/* !(".gitignore")
else
echo "directory $INHOUSEFILES not found"
fi
echo
shopt -u extglob
echo
echo "Done"
What am I doing wrong?
rm -rf $INHOUSEFILES/* !(".gitignore")
This deletes all files in $INHOUSEFILES/*, and then it also deletes everything in the current directory except .gitignore. That's what !(".gitignore") does when it's a separate argument.
If your intention is to delete everything in $INHOUSEFILES/ except .gitignore then combine the two arguments:
rm -rf $INHOUSEFILES/!(".gitignore")
It's also a good idea to quote variable expansions. (And conversely you don't need them around a literal string like .gitignore.)
rm -rf "$INHOUSEFILES"/!(.gitignore)

Use of mkdir -v output with a newline embedded

First of all, this question is purely theoric; it involves creates a directory with a newline, thing that should NEVER be done.
That said, I'm trying to use mkdir -pv output to remove the created directories in a specific moment of my script, but only the newly created, not the ones that previously existed.
Command mkdir -pv will print one line per directory not-existent before this command call so that I can re-inject in a rm -rf command. It works OK except in the case that directory contains a newline, and I can't see what is wrong with it.
My minimal working example:
declare -a created
# Delete previous traces
mkdir_out=$(mkdir -pv 'new 10'{1,2,3,$'\n',"'a",4})
# Convert to array
IFS=$'\n' read -d '' -a created < <(printf '%s' "${mkdir_out}")
# Debug
printf '=>[1] %s\n' "${created[#]}"
# We only want content between first and last quote
created=( "${created[#]%[\'\"]}" )
created=( "${created[#]#*[\'\"]}" )
# Debug
printf '=>[2] %s\n' "${created[#]}"
rm -rfv "${created[#]}"
ls # Directory "new 10\n" is still there!!
So, what is the safe way to do that?
Output like mkdir: created directory 'foo' is only meant for humans. Don't try to parse it.
If you want to handle all possible filenames and you can't deal in \0 separated lists, you have to do them one by one. Here's an example:
declare -a created dirs
dirs=( 'new 10'{1,2,3,$'\n',"'a",4} )
created=()
for dir in "${dirs[#]}"
do
if [[ ! -d "$dir" ]] && mkdir -p "$dir"
then
created+=( "$dir" )
fi
done
rm -rfv "${created[#]}"
ls # Directory "new 10\n" is not there.

Iterating through files gives odd results when no files [duplicate]

This question already has answers here:
How to skip the for loop when there are no matching files?
(2 answers)
Closed 3 years ago.
I'm trying to iterate through all zip files in a bash script (I'm using Cygwin, but I kind of doubt this is a bug with Cygwin).
It looks like this right now:
for z in *.zip
do
echo $z
done
which works well when there are zip files in the folder, and it echos exactly the zip files and nothing but the zip files. However, when I do it on a folder that's empty, it echos *.zip, when I'd rather it echo nothing.
What should I be doing? I don't think the right solution is if [ $z != "*.zip ]... but is it?
This is the expected behavior. From the documentation:
If no matching filenames are found, and the shell option nullglob is disabled, the word is left unchanged. If the nullglob option is set, and no matches are found, the word is removed.
So the solution is to set the nullglob option before the loop:
shopt -s nullglob
for z in *.zip
...
As one of its steps for executing a command, the shell may perform path expansion in which case it will replace a string, such as *.zip with the list of files that match that glob. If there are no such files, then the string is left unchanged. A reasonable solution is:
for z in *.zip
do
[ -f "$z" ] && echo $z
done
[ -f "$z" ] verifies that the file exists and is a regular file. The && means that echo will be executed only if that test is passed.
Either that or turning on the nullglob option.
$ set -x
$ echo *.none
+ echo '*.none'
*.none
$ shopt -s nullglob
+ shopt -s nullglob
$ echo *.none
$ echo *.none
+ echo

Shell script to browse one or more directories passed as parameters

I made this script that should receive one or more parameter, and those parameter are all directories, and it has to browse those directories (one by one) and do some operations.
The operations work fine if the parameter is 1 (only one directory),
How should I modify my script to make it works if more than 1 parameter is passed
Example if I want it to do the same operations in 2 or 3 directories at the same time?
Thanks
#!/bin/sh
cd $1
for file in ./* # */
do
if [[ -d $file ]]
then
ext=dir
else
ext="${file##*.}"
fi
mv "${file}" "${file}.$ext"
done
First, if you are using bash use bash shebang (#! /bin/bash).
Then use
#! /bin/bash
for d in "$#"
do
echo "Do something with $d"
done
to iterate over the command line arguments (dirs in your case)
#!/bin/sh
for dir in "$#"; do
for file in "$dir"/*; do
echo "Doing something with '$file'"
done
done

Resources