Reputation: 137
I learn bash and I got the next problem using wildcards:
sergs:~ > ls -1
a.sh
Desktop
Downloads
eclipse
sergs:~ > a=*.sh
sergs:~ > echo $a
a.sh
sergs:~ > a=*.sha
sergs:~ > echo $a
*.sha
If there's no file to match a *.sha
pattern why does bash
return the pattern itself instead of nothing?
Thus behavior breaks such a logic:
for i in /usr/lib/opkg/info/*.postinst; do do_some_logic_with_postinst_file; done
Do I have to check that the /usr/lib/opkg/info/
directory has no any *.postinst
files, explicitly, before the loop or there's some other way to do it.
Upvotes: 2
Views: 384
Reputation: 85780
An un-expanded glob string is returned literally in the command-line unless you turn on an expanded shell option nullglob
which returns no results if the glob expansion fails.
For your use case though, it is always a good practice to check if glob expansion is successful in a for-loop by testing it to see if the expanded result is a file i.e.
for file in /usr/lib/opkg/info/*.postinst; do
[ -f "$file" ] || continue
do_some_logic_with_postinst_file
done
The condition [ -f "$file" ]
will check if your glob expansion, i.e. if there are any .postinst
files the [ -f "$file" ]
would be true. For an un-expanded glob, the condition
[ -f /usr/lib/opkg/info/*.postinst ]
would fail because the literal string /usr/lib/opkg/info/*.postinst
is not a valid file, and the failure of this test
command along with the Boolean OR ||
causes the continue
action to the for
loop. Since the for
loop does not have any more arguments to process, the loop exits gracefully.
Doing such a way ( with the -f
against a glob string) is guaranteed to be POSIX compliant. For a bash
specific answer though, you can turn on the option as mentioned before
# '-s' enables the option
shopt -s nullglob
for file in /usr/lib/opkg/info/*.postinst; do
do_some_logic_with_postinst_file
done
# '-u' disables the option
shopt -u nullglob
Once you decide to use the nullglob
option, there are myriad of ways on how you can use it to run your feature. For e.g. you can put all your glob results in a an array type and check if the length is valid
shopt -s nullglob
results=(/usr/lib/opkg/info/*.postinst)
if (( ${#results[@]} )); then
printf '%s\n' 'my array is not empty'
for file in "${results[@]}"; do
printf '%s\n' "some action on ${file}"
done
fi
shopt -u nullglob
Be careful of this approach when not using the nullglob
option. Without the same, the results=(/usr/lib/opkg/info/*.postinst)
part could still be non-empty as the literal string is still stored in the array.
Another small addendum from your OP, is never to use variables to hold your glob string. Because when the variable is expanded unquoted and if the glob expansion is success, the contents of the variable are subject to Word-Splitting done by the shell and the filenames containing spaces could be potentially broken down to multiple entries when you expect it not to.
Always use an array and proper quoted expansion. See more on BashFAQ - BashGuide/Arrays for an excellent read-up on the subject.
Upvotes: 4
Reputation: 1642
That's how glob patterns work (edit: unless you enable nullglob
as pointed out by Inian). Another alternative can be something like
find /usr/lib/opkg/info -name \*.postinst|while read file; do
# do_some_logic_with_postinst_file
done```
Upvotes: 0