Check if something is not a directory in Linux - shell

I am trying to write an if statement where it will go to the next iteration of the for loop if i is the variable or if i is a directory. I am struggling with the directory part which would go after the -o
for i in *
if [ "$i" = VARIABLE -o ]
then
continue
fi

I don't fully understand the question, but a common idiom is:
for i in *; do
if test -d "$i"; then continue; fi
...
done
If you also want to compare against a particular value, you really shouldn't use -o. It's been deprecated for a long time. Instead, use:
if test -d "$i" || test "$i" = VARIABLE; then ...

You can use test -d:
if [ "$i" = VARIABLE ] || ! [ -d "$i" ]
then continue
fi
Or more succinctly:
[ "$i" != VARIABLE ] && [ -d "$i" ] || continue
Or as separate tests:
[ "$i" != VARIABLE ] || continue
[ -d "$i" ] || continue

Related

Bash if statement with AND

Trying to figure out how to get working this condition, without using multiple if statement
Check if these files are in the folder, and basically if an integer is major then another, since i use to concat days hours and minute value of time to get two integers, one with current time and the other with schedule time.
FILE1=180.mp4
FILE2=360.mp4
FILE3=720.mp4
FILE4=1080.mp4
FILE5=audio.mp4
TIME=10155
SCHEDULE=20256
if [ test -f "$FILE1" ] && [ test -f "$FILE2" ] && [ test -f "$FILE3" ] && [ test -f "$FILE4" ] && [ test -f "$FILE5" ] && (( TIME > SCHEDULE )) ; then
echo DO EVENT
else
echo DON'T DO EVENT
fi
[ and test are synonyms. You should use one or the other, not both.
Alternative #1:
if test -f "$FILE1" && test -f "$FILE2" && test -f "$FILE3" && test -f "$FILE4" && test -f "$FILE5" && (( TIME > SCHEDULE ))
then
echo "true"
fi
Alternative #2:
if [ -f "$FILE1" ] && [ -f "$FILE2" ] && [ -f "$FILE3" ] && [ -f "$FILE4" ] && [ -f "$FILE5" ] && (( TIME > SCHEDULE ))
then
echo "true"
fi
However, like in any language, you'd be better off using an array:
files=(180.mp4 360.mp4 720.mp4 1080.mp4 audio.mp4)
if (( TIME > SCHEDULE ))
then
for file in "${files[#]}"
do
if ! [ -f "$file" ]
then
echo "Missing $file" >&2
exit 1
fi
done
echo "Do the thing"
fi
It is possible you will get a problem with the echo DON'T DO EVENT. without quoting, you have a single-quote unmatched in that line. The shell will likely issue unexpected EOF while looking for matching `''.
This should really be a comment, but I am still working on the rep points to be able to add comments

How do I use round brackets in an 'if' condition

I'm creating a bash script and somewhere inside I have this code:
if [ $# -eq 2 -a (! -r "$2" -o ! -f "$2") ]; then
echo "rvf: bestand \""$2"\" bestaat niet of is onleesbaar" 1>&2
exit 2
fi
When i try to run this inside the script I get this error:
Syntax Error (bash -n):
rvf: line 14: syntax error in conditional expression
rvf: line 14: syntax error near `-a'
rvf: line 14: `if [[ $# -eq 2 -a (! -r "$2" -o ! -f "$2") ]]; then'
How does '()' work inside Bash scripts?
[[ doens't support -a, and it is considered obsolete and non portable for [. The correct solution using [ would be
if [ "$#" -eq 2 ] && { [ ! -r "$2" ] || [ ! -f "$2" ]; }; then
Grouping is done with { ... } rather than ( ... ) to avoid creating an unnecessary subshell.
Using [[ is simplifies to
if [[ "$#" -eq 2 && ( ! -r "$2" || ! -f "$2" ) ]]; then
Parentheses can be used for grouping inside [[; as a compound command, it uses separate parsing and evaluation rules, compared to an ordinary command like [ (which is just an alias for test, not syntax of any kind).
In either case, De Morgan's laws lets you refactor this to something a little simpler:
if [ "$#" -eq 2 ] && ! { [ -r "$2" ] && [ -f "$2" ] }; then
if [[ "$#" -eq 2 && ! ( -r "$2" && -f "$2" ) ]]; then
There are multiple points of confusion here.
[ can (as an optional XSI extension to the standard) support ( as a separate word (meaning there needs to be spaces around it), but the POSIX sh specification marks it (like -a and -o) as "obsolescent" and advises against its use.
[[ does support (, but again, it needs to always be a separate word.
Don't do that at all, though. You're using only well-supported and portable functionality if you keep each test its own simple command and combine them only with the shell's boolean logic support.
That is:
if [ "$#" -eq 2 ] && { [ ! -r "$2" ] || [ ! -f "$2" ]; }; then
echo "rvf: bestand \"$2\" bestaat niet of is onleesbaar" >&2
exit 2
fi
Restructure your logic.
"Not A or Not B" is just a more complicated way to say "not (A and B)".
In bash, try
if [[ "$#" == 2 ]] && ! [[ -r "$2" && -f "$2" ]]; then
Better,
if [[ "$#" == 2 && -r "$2" && -f "$2" ]]
then : all good code
else : nope code
fi
Even better,
if [[ "$#" == 2 ]] # correct args
then if [[ -r "$2" ]] # is readable
then if [[ if -f "$2" ]] # is a file
then echo "all good"
: do all good stuff
else echo "'$2' not a file"
: do not a file stuff
fi
else echo "'$2' not readable"
: do not readable stuff
fi
else echo "Invalid number of args"
: do wrong args stuff
fi
Clear error logging is worth breaking the pieces out.
Even better, imho -
if [[ "$#" != 2 ]]
then : wrong args stuff
fi
if [[ ! -r "$2" ]]
then : unreadable stuff
fi
if [[ ! -f "$2" ]]
then : do not a file stuff
fi
: do all good stuff

zsh script parser error for nested if/else

I have the following humble zsh function:
function remember()
{
if [ "$1" != "" ]
then
if[ "$1" != "clean" ]
then
echo "Why";
#echo $1 >> {~/.remember_info};
else
rm -r ~/.remember_info;
touch ~/.remember_info;
fi
else
cat .remember_info;
fi
}
When I try to source it I get:
parse error near `echo' (The echo being the line with echo "Why";)
The error is quite non descriptive and I assume its related to part of the loop's logic (since no matter what instruction I give after then it error out there).
Is there any way to "debug" this kind of thing ? zsh -n doesn't help much (at all)
You forgot the space between if and [ when comparing to clean.
This is case, though, where your function can be made simpler by handling the = case first.
function remember()
{
if [ "$1" = "" ]; then
cat ~/.remember_info
elif [ "$1" = clean ]; then
rm -r ~/.remember_info
touch ~/.remember_info
else
echo "$1" >> ~/.remember_info;
fi
}
Or, use a case statement.
remember () {
f=~/.remember_info
case $1 in
"")
cat "$f"
;;
clean)
rm -r "$f"
touch "$f"
;;
*)
print "$1" >> "$f"
;;
esac
}
You are missing a whitespace after [. It should be:
function remember()
{
if [ "$1" != "" ]
then
if [ "$1" != "clean" ]
then
echo "Why";
#echo $1 >> {~/.remember_info};
else
rm -r ~/.remember_info;
touch ~/.remember_info;
fi
else
cat .remember_info;
fi
}
[ is the same as test. It is a separate command, described in man test:
TEST(1)
NAME
test - check file types and compare values
SYNOPSIS
test EXPRESSION
test
[ EXPRESSION ]
[ ]
[ OPTION

Chosing "input" in an AND (&&) and OR (||) list of commands

I have to find a way to have my script read from one of these three options:
a file argument
standard input
a previously established environment variable
Here's what I currently have:
#!/bin/bash
key=$1
[ $# -ge 1 -a -f "$2" ] && input="$2" || [ -f "$INPUT" ] && input="$INPUT" || input="-"
echo $input
Only the environment variable refuses to work, the rest works fine.
I've tried using the export INPUT="pathnametofile" before but it doesn't make any difference, I end up with the shell asking me to enter info as if I called on cat.
The problem in your script
Your attemp is not working due to the way the shell processes a Lists of Commands:
‘&&’ and ‘||’ have equal precedence.
AND and OR lists are executed with left associativity.
Your sentence:
[ $# -ge 1 -a -f "$2" ] && input="$2" || [ -f "$INPUT" ] && input="$INPUT" || input="-"
does the same as follows:
[ $# -ge 1 -a -f "$2" ] && input="$2"
[ $? -eq 0 ] || [ -f "$INPUT" ]
[ $? -eq 0 ] && input="$INPUT"
[ $? -eq 0 ] || input="-"
Now yo may see why your unexpected behaviour.
A better attempt grouping commands
{ [ $# -ge 1 -a -f "$2" ] && input="$2"; } || { [ -f "$INPUT" ] && input="$INPUT"; } || input="-"
Now, due to precedence, the first group is not needed at all:
[ $# -ge 1 -a -f "$2" ] && input="$2" || { [ -f "$INPUT" ] && input="$INPUT"; } || input="-"
Furthermore, unless you have set the positional parameters by hand, you can remove the first check (after all, if $2 is emtpy, -f "" fails the same).
[ -f "$2" ] && input="$2" || { [ -f "$INPUT" ] && input="$INPUT"; } || input="-"
An alternative with the if conditional construct
if [ -f "$2" ]; then
input=$2
elif [ -f "$INPUT" ]; then
input=$INPUT
fi
echo "${input:=-}"
untested, but you'll probably have better luck with if commands, and test that the variable is not empty:
if [ $# -ge 1 -a -f "$2" ]; then
input="$2"
elif [ -n "$INPUT" -a -f "$INPUT" ]; then
input="$INPUT"
else
input="-"
fi

Boolean Expressions in Shell Scripts

What's the "right" way to do the following as a boolean expression?
for i in `ls $1/resources`; do
if [ $i != "database.db" ]
then
if [ $i != "tiles" ]
then
if [ $i != "map.pdf" ]
then
if [ $i != "map.png" ]
then
svn export -q $1/resources/$i ../MyProject/Resources/$i
...
The other solutions have a couple of common mistakes:
http://www.pixelbeat.org/programming/shell_script_mistakes.html
for i in $(ls ...) is redundant/problematic
just do: for i in $1/resources*; do ...
[ $i != file1 -a $1 != file2 ] This actually has 2 problems.
a. The $i is not quoted, hence names with spaces will cause issues
b. -a is inefficient if stating files as it doesn't short circuit (I know the above is not stating files).
So instead try:
for i in $1/resources/*; do
if [ "$i" != "database.db" ] &&
[ "$i" != "tiles" ] &&
[ "$i" != "map.pdf" ] &&
[ "$i" != "map.png" ]; then
svn export -q "$i" "../MyProject/Resources/$(basename $i)"
fi
done
Even shorter:
for i in `ls $1/resources`; do
if [ $i != databse.db -a $i != titles -a $i != map.pdf ]; then
svn export -q $1/resources/$i ../MyProject/Resources/$i
fi
done;
The -a in the if expression is the equivalent of the boolean AND in shell-tests. For more see man test
Consider using a case statement:
for i in $(ls $1/resources); do
case $i in
database.db|tiles|map.pdf|map.png)
;;
*)
svn export -q $1/resources/$i ../MyProject/Resources/$i;;
esac
done
for i in `ls $1/resources`; do
if [ $i != "database.db" ] && [ $i != "tiles" ] && [ $i != "map.pdf" ] && [ $i != "map.png" ]; then
svn export -q $1/resources/$i ../MyProject/Resources/$i
For future reference, the new [[ test operator is preferred. The accepted answer is close and everything mentioned applies, but that answer will require lots of quoting and calls to multiple tests.
The preferred method would be something like:
for i in $1/resources/*; do
if [[ $i != "database.db" && $i != "tiles" &&
$i != "map.pdf" && $i != "map.png" ]]; then
svn export -q "$i" "../MyProject/Resources/$(basename $i)"
fi
done

Resources