Can fish shell do wildcard globbing in an if statement? - fish

I'm having trouble understanding whether I can use wildcard globbing in an if statement in fish. This switch/case works as expected:
# correctly echos macOS on macOS
switch "$OSTYPE"
case 'darwin*'
echo 'macOS'
case '*'
echo 'not macOS'
end
However, I cannot get an if statement version of the same thing to work.
# doesn't work - prints 'not macOS' on macOS
if [ "$OSTYPE" = 'darwin*' ]
echo 'macOS'
else
echo 'not macOS'
end
In zsh/bash you can do something like this:
[[ $OSTYPE == darwin* ]] && echo 'macOS' || echo 'not macOS'
Or, more verbosely,
if [[ $OSTYPE == darwin* ]]
then echo 'macOS'
else echo 'not macOS'
fi
My question is, does fish support wildcard globbing against a variable in if statements? Am I doing this wrong? I cannot find an example in the fish docs that tells me either way.
NOTE: I'm not asking about checking $OSTYPE in fish. I know there are better ways to do that. My question is limited strictly to whether it is possible to do wildcard globbing in an if statement in fish.

No.
Use switch like you said, or the string builtin like
if string match -q 'darwin*' -- "$OSTYPE"
The if isn't important - the command you are running in your example is [, which is an alternative name for test, which is a builtin with documentation at http://fishshell.com/docs/current/commands.html#test (or man test or help test).

Related

shell scripting, if is executed no matter what

why in this script
#!/bin/sh
if [ $1='all' ];
then
echo $1
echo "all"
fi
all is printed no matter what???
For example I do: ./thescript.sh and I got clearly that $1 is empty. But still all is printed. Why? And how can I print "all" when all is passed
shellcheck has the solution:
SC2077: You need spaces around the comparison operator.
Change to
if [ $1 = 'all' ];
Without the spaces, the thing between the [ and ]is treated as one expression, and man [ says
"STRING equivalent to -n STRING
so any non-empty string is considered true. (i.e., if [ "" ]; would be false.)
That said, you better specify which shell you want in the shebang. On some systems /bin/sh may be bash, on others /bin/dash, or something else. Pick your shell and avoid any problems from bad assumptions on which shell you actually get.
Put double-quotes around the variable:
#!/bin/sh
if [ "$1" == "all" ];
then
echo $1
echo "all"
fi

Can fish shell handle if statements within if statements?

I'm trying to get my config.fish to work, but it's not working as expected, and I really can't understand why. The best I can do is guess that maybe Fish can't handle if statements within if statements? Here's my code:
echo "so far so good"
if status --is-interactive
# Chips: fish plugin manager
if [ -e ~/.config/chips/build.fish ] ; . ~/.config/chips/build.fish ; end
echo "def interactive"
# Don't use vi keybindings in unknown terminals,
# since weird things can happen.
set acceptable_terms xterm-256color screen-256color xterm-termite
echo "acceptable terms: $acceptable_terms"
echo "term: $TERM"
if contains $TERM acceptable_terms
echo "good to go!"
fish_vi_key_bindings
# Load pywal colors
cat ~/.cache/wal/sequences
else
echo "why?!?!?"
end
end
And what I'm getting is this:
so far so good
def interactive
acceptable terms: xterm-256color screen-256color xterm-termite
term: xterm-termite
why?!?!?
But what I expect to see is this:
so far so good
def interactive
acceptable terms: xterm-256color screen-256color xterm-termite
term: xterm-termite
good to go!
But when I run this in a shell, if works fine:
❯ echo "term: $TERM / acceptable: $acceptable_terms"
term: xterm-termite / acceptable: xterm-256color screen-256color xterm-termite
❯ if contains $TERM $acceptable_terms
echo "yay!"
end
yay!
What could be going on here?
Of course fish can handle nested if statements!
if contains $TERM acceptable_terms
is missing the $ on the second variable!
if contains $TERM $acceptable_terms

Conditional expression with sh: errors from both [[ $COUNT == 0 ]] and [ $COUNT == 0 ]

Why is it that the following snipped of code return the following message: syntax error in conditional expression and fails.
#!/bin/sh
COUNT=`cat annemarie/new_files.txt | wc -l`
if [[ $COUNT -ge 1 ]]; then
echo "New files found. Stopping deployment"
exit 0
fi
if [[ $COUNT == 0 ]]; then
echo "File not found. Continuing deployment"
fi
but this passes, but gives me the warning unary operator expected, however it doesnt seem as if the code is processed:
#!/bin/sh
COUNT=`cat annemarie/new_files.txt | wc -l`
if [ $COUNT -ge 1 ]; then
echo "New files found. Stopping deployment"
exit 0
fi
if [ $COUNT == 0 ]; then
echo "File not found. Continuing deployment"
fi
Which is the correct format?
With /bin/sh, [[ is not guaranteed to be supported whatsoever -- but == inside of [ ] is also not guaranteed to work. Thus, your code must be:
#!/bin/sh
count=$(wc -l <annemarie/new_files.txt)
if [ "$count" -ge 1 ]; then
echo "New files found. Stopping deployment"
exit 0
fi
if [ "$count" -eq 0 ]; then
echo "File not found. Continuing deployment"
fi
Note:
[[ ... ]] is a ksh extension also picked up by bash and zsh. While this has several benefits (among them, preventing string-splitting and glob expansion, and thus making quotes less necessary), it is not specified as part of the POSIX sh standard, and so is not guaranteed to be available at all when a script is run with /bin/sh.
$(...) is modern command substitution syntax; it has more predictable behavior when nested or when running commands that contain literal backslashes than does the old, pre-POSIX backtick-based syntax.
For string comparisons, == should be replaced with = for portability purposes; see the POSIX test specification, in which = is the only string comparison operator. That said, inasmuch as what we want is a numeric comparison, -eq is arguably the correct operator for the job.
Expansions must be quoted to prevent string-splitting and globbing.
cat foo | bar is potentially much less efficient than bar <foo. (With some commands this is only a minor difference; for commands that can parallelize or seek when given a direct file handle -- such as sort or tail -- it can be much larger)
The POSIX standard reserves all-caps names for environment variables and shell builtin names with meaning to the OS and shell themselves, and guarantees that lower-case names will not unexpectedly modify the behavior of POSIX-defined tools. See http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html, fourth paragraph.

How to check substring in Bourne Shell?

I wanna test whether a string has "substring". Most answers online is based on Bash. I tried
if [ $string == "*substring*" ]
which was not working. Currently
if echo ${string} | grep -q "substring"
worked. Is there any other better way.
Using POSIX compliant parameter-expansion and with the classic test-command.
#!/bin/sh
substring=ab
string=abc
if [ "$string" != "${string%"$substring"*}" ]; then
echo "$substring present in $string"
fi
(or) explicitly using the test operator as
if test "$string" != "${string%$substring*}" ; then
In a POSIX-features only shell you won't be able to do general pattern or regex matching inside a conditional without the help of an external utility.
That said:
Kenster's helpful answer shows how to use the branches of a case ... esac statement for pattern matching.
Inian's helpful answer shows how to match indirectly inside a conditional, using patterns as part of parameter expansions.
Your own grep approach is certainly an option, though you should double-quote ${string}:
if echo "${string}" | grep -q "substring"; ...
A slightly more efficient way is to use the expr utility, but note that per POSIX it is limited to BREs (basic regular expressions), which are limited:
string='This text contains the word "substring".'
if expr "$string" : ".*substring" >/dev/null; then echo "matched"; fi
Note that the regex - the 3rd operand - is implicitly anchored at the start of the input, hence the need for .*.
>/dev/null suppresses expr's default output, which is the length of the matched string in this case. (If nothing matches, the output is 0, and the exit code is set to 1).
If you're just testing for substrings (or anything that can be matched using filename wildcards) you can use case:
#!/bin/sh
while read line; do
case "$line" in
*foo*) echo "$line" contains foo ;;
*bar*) echo "$line" contains bar ;;
*) echo "$line" isnt special ;;
esac
done
$ ./testit.sh
food
food contains foo
ironbar
ironbar contains bar
bazic
bazic isnt special
foobar
foobar contains foo
This is basic Bourne shell functionality. It doesn't require any external programs, it's not bash-specific, and it predates POSIX. So it should be pretty portable.
Short answer is no, not if you are trying to use vanilla sh, without Bash extensions. On many modern systems, /bin/sh is actually a link to /bin/bash, which provides a superset of sh's functionality (for the most part). Your original attempt would have worked with Bash's builtin [[ extended test command: http://mywiki.wooledge.org/BashFAQ/031

How do I test if a perl command embedded in a bash script returns true?

So, I have a bash script inside of which I'd like to have a conditional which depends on what a perl script returns. The idea behind my code is as follows:
for i in $(ls); do
if $(perl -e "if (\$i =~ /^.*(bleh|blah|bluh)/) {print 'true';}"); then
echo $i;
fi;
done
Currently, this always returns true, and when I tried it with [[]] around the if statement, I got errors. Any ideas anyone?
P.s. I know I can do this with grep, but it's just an example. I'd like to know how to have Bash use Perl output in general
P.p.s I know I can do this in two lines, setting the perl output to a variable and then testing for that variables value, but I'd rather avoid using that extra variable if possible. Seems wasteful.
If you use exit, you can just use an if directly. E.g.
if perl -e "exit 0 if (successful); exit 1"; then
echo $i;
fi;
0 is success, non-zero is failure, and 0 is the default if you don't call exit.
To answer your question, you want perl to exit 1 for failure and exit 0 for success. That being said, you're doing this the wrong way. Really. Also, don't parse the output of ls. You'll cause yourself many headaches.
for file in *; do
if [[ $file = *bl[eau]h ]]; then
echo "$file matches"
fi
done
for file in * ; do
perl -e "shift =~ /^.*(bleh|blah|bluh)/ || exit 1" "$file" && echo $file: true
done
You should never parse the output of ls. You will have, at least, problems with file names containing spaces. Plus, why bother when your shell can glob on its own?
Quoting $file when passing to the perl script avoids problems with spaces in file names (and other special characters). Internally I avoided expanding the bash $file variable so as to not run afoul of quoting problems if the file name contained ", ' or \
Perl seems to (for some reason) always return 0 if you don't exit with an explicit value, which seems weird to me. Since this is the case I test for failure inside the script and return nonzero in that case.
The return value of the previous command is stored in the bash variable $?. You can do something like:
perl someargs script.pl more args
if [ $? == 0 ] ; then
echo true
else
echo false
fi
It's a good question, my advice is: keep it simple and go Posix (avoid Bashisms1) where possible..
so ross$ if perl -e 'exit 0'; then echo Good; else echo Bad; fi
Good
so ross$ if perl -e 'exit 1'; then echo Good; else echo Bad; fi
Bad
1. Sure, the OP was tagged bash, but others may want to know the generic-Posix form.