How could I check if a file has a file of extension .java?
So far I have:
for javaFile in *.java ; do
{
echo "The file $javaFile has : " >> ~/Desktop/externalServers.txt
grep -E '^[^/]{2}.*http' $javaFile >> ~/Desktop/externalServers.txt
grep -E '^[^/]{2}.*ftp' $javaFile >> ~/Desktop/externalServers.txt
echo "----------------------------------------" >> ~/Desktop/externalServers.txt
sed -e "s/[[:space:]]+/ /g" ~/Desktop/externalServers.txt >> ~/Desktop/externalServersTemp.txt
rm ~/Desktop/externalServers.txt
mv ~/Desktop/externalServersTemp.txt ~/Desktop/externalServers.txt
sed 's/^n//' ~/Desktop/externalServers.txt >> ~/Desktop/externalServersTemp.txt
rm ~/Desktop/externalServers.txt
mv ~/Desktop/externalServersTemp.txt ~/Desktop/externalServers.txt
} done
But every time I do that, I get the error:
grep: *.java: No such file or directory
Basically I want to first see if the folder has any files of type .java and only continue with the script.
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
In Bash, simply use
shopt -s nullglob
for pdfFile in *.java;do
# your code goes here
done
This syntax is for Bourne-like shells; the nullglob option is specific to bash. The braces you used ({}) are for C-style shells.
shopt -s nullglob sets the nullglob option, which basically tells Bash that globs that failed to match should be expanded into the null string. By default, if *.java fails to match, it is expanded into itself (the asterisk stays).
Method 2
My own rendition of your script would probably look like:
set -- *.java
test -e "$1" && {
fortyequals=$(printf '%040dn' | tr 0 =)
for javaFile do
printf '%snIn file: %sn%sn'
$fortyequals "$javaFile" $fortyequals
grep -E '^[^/]{2}.*(ftp|http)' "$javaFile"
done
} >>~/Desktop/externalservers.txt
The solution already offered is needlessly shell-specific. You could achieve the same effect with portable syntax – which makes for less to remember in the long run with the added advantage of being more robust like:
set -- *.java
test -e "$1" &&
for javaFile do
# ...iterate on $javaFile here...
done
Another advantage is that you not only retain the most recent value of $javaFile following the loop, you also retain all of the values that $javaFile ever had in [email protected]. This makes the following possible:
... done echo "The previous for loop processed $# files." echo "The first file processed was:" printf "///t'%s't///n" "$1" echo "The last file processed was:" printf "///t'%s't///n" "$javaFile" echo 'All files processed in the for loop were:' printf "///t'%s't///n" "<a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d6f296">[email protected]</a>"
And if you do really like the { curlies } you can use them – even in bash (though they are unnecessary) – but you have to delimit between the two shell reserved words } and done like:
for ... do {
...
} ; done
Though my recommendation is that you enclose the entire block – around the for loop and any post processing you do – in curlies dependent on the && reserved words like:
set -- *.java
test -e "$1" && {
for ... done
# ...further processing on <a href="https://getridbug.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="d0f490fefefe">[email protected]</a>
}
Looking back at it and I believe I can help a great deal with the regex as well… It looks like we’re looking for lines containing the words http and/or ftp that do not begin with two //.
I think the rest is a result of the separate grep operations you do. You appear to be attempting to clear blank lines, but, as I imagine, those are only caused in the first place by repeated appended writes to the file.
So, instead, we could just write for loop’s output directly to the outfile in order to maintain the write descriptor for ~/Desktop/externalservers.txt until the loop has finished, which should avoid any blank lines being written. Maybe like:
for ... done >>outfile
Or
{ grouped ; command ; list ; } >>outfile
At the very least I can tell you that this statement probably does not do what you want:
sed 's/^n//' $file
sed is newline delimited – it’s impossible to encounter a newline as the ^first character on a line. You can get newlines into sed‘s pattern space by various means, but never without some processing.
Method 3
Another issue with the sample code, you generally want to use double quotes on any of the iterators as you use them within the for loop.
Your code:
for javaFile in *.java ;
{
echo "The file $javaFile has : " >> ~/Desktop/externalServers.txt
grep -E '^[^/]{2}.*http' $javaFile >> ~/Desktop/externalServers.txt
grep -E '^[^/]{2}.*ftp' $javaFile >> ~/Desktop/externalServers.txt
Should be:
for javaFile in *.java ; do
echo "The file $javaFile has : " >> ~/Desktop/externalServers.txt
grep -E '^[^/]{2}.*http' "$javaFile" >> ~/Desktop/externalServers.txt
grep -E '^[^/]{2}.*ftp' "$javaFile" >> ~/Desktop/externalServers.txt
Method 4
Joseph’s nullglob approach is the most elegant but if you don’t want to or can’t use that (non-bash shell or older bash version for example), you can also do this (assuming your file names contain no newlines):
file="~/Desktop/externalServers.txt"
while IFS= read -r javaFile
do
echo "The file $javaFile has : " >> "$file"
grep -E '^[^/]{2}.*http' "$javaFile" >> "$file"
grep -E '^[^/]{2}.*ftp' "$javaFile" >> "$file"
echo "----------------------------------------" >> "$file"
## The -i flag enables in-place editing so you don't need
## to fiddle about with temp files.
sed -i -e "s/[[:space:]]+/ /g" "$file"
sed -i -n '/[^[:space:]]/p' "$file"
done < <(find . -maxdepth 1 -name '*.java')
The IFS= sets the input field separator to empty so that you can deal with filenames that contain spaces correctly and the -r option to read means that backslashes are not treated specially (in case your filenames can contain them). The <(command) construct is called process substitution and is a way of passing the output of one command as input to another.
I introduced the variable $file so that you don’t need to edit every line if you ever want to change the output file name.
Note that I used the -i flag to sed which allows you to edit the original file and removes the need for temp files. Also note that there is no reason to do rm foo.txt; mv bar.txt foo.txt, you can always just do mv bar.txt foo.txt and that will overwrite the target file. Now, I have no idea what this command was supposed to do:
sed 's/^n//' ~/Desktop/externalServers.txt >> ~/Desktop/externalServersTemp.txt
I am guessing that you want it to remove blank lines but that won’t work so I changed it in the above with
sed -i -n '/[^[:space:]]/p' ~/Desktop/externalServers.txt
The -n suppresses sed’s default behavior of printing each line, the /[^[:space:]]/ will match any lines that match any non-space character and the p at the end means that these lines and only these lines will be printed. If this is not what you wanted to do with your sed command, let me know and I will edit as needed.
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0