Debugging BASH IF Conditions - bash

I am getting an error when I try and run my assignment.
#!/bin/bash
## Assignment 2
echo Please enter a User Name:
read u
if [ $u!="root"]; then
echo Searching for Username!
grep $u /etc/passwd|sed 's/$u/hidden/gi'
elif [ $u!="toor"]; then
echo Root is NOT allowed.
else
echo Toor is definetely NOT allowed.
fi
Output:
Please enter a User Name:
user1
./assign2.sh: line 6: [bthiessen: command not found
./assign2.sh: line 9: [bthiessen: command not found
Toor is definetely NOT allowed.
What is wrong with my if statements?

Try that :
#!/bin/bash
echo Please enter a User Name:
read u
if [[ $u != "root" ]]; then
echo Searching for Username!
grep "$u" /etc/passwd | sed "s/$u/hidden/gi"
elif [[ $u != "toor" ]]; then
echo Root is NOT allowed.
else
echo Toor is definetely NOT allowed.
fi
problems founds :
[ $u!="root"] need spaces around !=
if you use variables in sed, you need " quotes, not simple '
note :
[[ is a bash keyword similar to (but more powerful than) the [ command. See http://mywiki.wooledge.org/BashFAQ/031 and http://mywiki.wooledge.org/BashGuide/TestsAndConditionals . Unless you're writing for POSIX sh, we recommend [[
Learn the difference between ' and " and `. See http://mywiki.wooledge.org/Quotes and http://wiki.bash-hackers.org/syntax/words

Whitespace counts here:
if [[ $u!="root" ]]; then
And:
elif [[ $u!="toor" ]]; then
Also prefer [[ over [.

if [ $u!="root"]; then
elif [ $u!="toor"]; then
There needs to be spaces inside the square brackets, and around the != operator. The whitespace is required. It's also good practice to quote "$u" in case the username has spaces or is blank.
if [ "$u" != "root" ]; then
elif [ "$u" != "toor" ]; then
There are other issues with your script which I suppose should be left to you to find.

To debug bash scripts, you can also use bash -x and set -x:
bash -x script.sh runs an existing script with debug messages, it will echo lines before executing them.
With set -x you can enable this behavior directly in your shell script, e.g. in the first line after the shebang. (This is kind of like echo on in Windows scripting.) set +x disables this option.
It is even possible, although hardly useful, to set -x in interactive shells.
This is all nicely explained in the Bash Guide for Beginners, under Debugging Bash scripts.

Related

Bash script that checks for parts of current folderpath

Clean and simple: how do I check with bash for certain parts of the folder I'm currently in?
#!/usr/bin/sh
CURRENTFOLDER=$(pwd)
echo "${CURRENTFOLDER}"
CHECKFOLDER="/home/*/domains/*/public_html"
if [ $CURRENTFOLDER ! $CHECKFOLDER ]
then
echo "Current folder is not /home/user/domains/domain.com/public_html"
exit
fi
User and domain are variable, I don't need to know them for this checkup, just the 3 pre-defined folders in the variable CHECKFOLDER
There's a problem with this approach.
For example in bash the following expression evaluates to true:
[[ /www/user/domains/local/public_html == /www/*/public_html ]]
It is more accurate to use a bash regex:
[[ /www/user/domains/local/public_html =~ ^/www/[^/]+/public_html$ ]]
So your code would become:
#!/bin/bash
current_folder=$PWD
check_folder='^/home/[^/]+/domains/[^/]+/public_html$'
if ! [[ $current_folder =~ $check_folder ]]
then
echo "Current folder is not /home/user/domains/domain.com/public_html"
exit
fi
BTW, the shebang needs to be a bash, not sh. And it's kind of dangerous to capitalize your variables.
Try this (almost) Shellcheck-clean code:
#! /usr/bin/sh
curr_phpath=''
for phpath in /home/*/domains/*/public_html/; do
if [ "$phpath" -ef . ]; then
curr_phpath=$phpath
break
fi
done
if [ -z "$curr_phpath" ]; then
echo "Current folder is not /home/user/domains/domain.com/public_html" >&2
exit 1
fi
Because of aliasing mechanisms (e.g. symbolic links, bind mounts) it is very difficult in general to determine if two paths reference the same file or directory by comparing them textually. See How to check if two paths are equal in Bash? for more information. This solution uses a more reliable mechanism to determine if the current directory is one of the valid ones.
Since the shebang line references sh instead of bash, the code avoids Bashisms. It's been tested with both bash and dash (probably the most common non-Bash sh).
See Correct Bash and shell script variable capitalization for an explanation of why the code does not use ALL_UPPERCASE variable names.
The [ "$phpath" -ef . ] test is true if the .../public_html path being checked is the same directory as the current directory. The -ef operator is not in POSIX so it is not guaranteed to be supported by an sh shell, and Shellcheck (correctly) warns about it. However, it is supported in both bash and dash, and sh is usually one of those (on Linux at least).
You can save a step just by changing to the directory instead of checking.
Check your glob matches only one file first.
Then, cd to check it's a dir.
#! /bin/bash
IFS="$(printf '\n\t')"
files=( $(compgen -G '/home/*/domains/*/public_html') )
if [[ "${#files[#]}" != 1 ]]
then
printf 'Multiple matches\n' >&2
exit 1
fi
if ! cd "${files[0]}"
then
printf 'Cannot chdir\n'
exit 1
fi

how to check if a file exist and is a text file?

Hi everyone I need to check if a file exist with a shell script. I did some digging and ended up with this syntax but I'm not sure why it isn't working
(please bear in mind that you are talking to beginner)
I've found that you can add -e for example to check if it exist but I didn't get where these shortcuts came form or their names
#! /bin/bash
if [ "$#" = "1" ]
then
if [ -e $($1) ] && [ -f $($1) ]
then echo 'the file exists'
fi
fi
In idiomatic Bash:
#!/usr/bin/env bash
if [[ -f "${1-}" ]]
then
echo 'the file exists'
fi
Correct shebang
[[ rather than [
-f implies -e
No need for semicolons or single-use variables.
Please keep in mind that this does not tell you whether the file is a text file. The only "definition" of a text file as opposed to any other file is whether it contains only printable characters, and even that falls short of dealing with UTF BOM characters and non-ASCII character sets. For that you may want to look at the non-authoritative output of file "${1-}", for example:
$ file ~/.bashrc
/home/username/.bashrc: ASCII text
More in the Bash Guide.
#!/bin/bash
if [ "$#" == 1 ]; then
if [[ -e "$1" && -f "$1" ]]; then
echo 'The file exists';
fi
fi
You should put every conditional && between [[ ]] symbols otherwise it will be interpreted as execute if success.
#! /bin/sh
FILE=$1 # get filename from commandline
if [ -f $FILE ]; then
echo "file $FILE exists"
fi
See the fine manual page of test commands, which are built-in in the different shells: man test; man sh; man bash
You will also find many shell primers which explain this very nicely.
Or see bash reference manual: https://www.gnu.org/software/bash/manual/bash.pdf

How to check if the output of a command contains a string and then run a command if the string exists

Example
if "darwin" in $MACHTYPE;
then
echo "whoa it's a mac!"
fi
And the output should be
whoa it's a mac, if darwin is found in the output of $MACHTYPE
Please guide me!
Provided you're using bash, you could use the =~ operator:
if [[ "$MACHTYPE" =~ "darwin" ]];
then
echo "whoa it's a mac!"
fi
From the bash man page:
An additional binary operator, =~, is available, with the same precedence as == and !=. When it is used, the string to the right of the operator is considered an extended regular expression and matched accordingly (as in regex(3)).
If you don't have a version of bash which supports regular expressions then you can use globbing:
if [[ $MACHTYPE = *darwin* ]]
then
echo "whoa it's a mac!"
fi
Note that you must use [[, not [.
Other shells like sh might support [[ but that is not guaranteed by the standard.
You could evaluate your command directly, for example:
if uname -a | grep -i "darwin" > /dev/null; then
echo "it is a mac"
fi
In this case, grep will exit 0 if found a value and output will be redirected to /dev/null if try then you can call your command, in this case: echo "it is a mac"
The code below can get the output of your command on the cmd, then check if there has the specific word.
command="command here"
if[ `echo $command | grep -c "\"darwin\""` -gt 0 ]; then
Do anything you want here
fi

Bash + check for file exist with a path home ~

I haven't found anything to deal with this particular situation. Maybe there is a easy way that I'm overlooking instead of checking for a string to catch this scenario. When I check an input for existence of a file, if the input is ~/filecheck , this won't work. I get negative results while the file is in my home folder. Any suggestions for improvement to any part of the script I will definitely appreciate. I also have to use an input instead of a argument. Thanks for any help.
my test script
read -p "Enter: " input
echo $input
if [ -f $input ]; then
read -p "Do you REALLY want to delete this file?:" input2
if [[ $input2='y' || $input2 = 'Y' ]]
then
rm -f $input
elif [[ $input2='n' || $input2='N' ]]
then
exit
else
echo "Invaild Option"
exit
fi
else
echo Invaild Option!
exit
fi
Since you are entering input string as ~/filecheck shell doesn't expand tilde while using condition with -f in [ -f $input ]
You can use it this way but it is not recommended and potentially dangerous as arbitrary commands can be run by user:
if [[ -f $(bash -c "echo $input") ]]; then
echo "file exists"
fi
EDIT: As per the comments below to avoid risky bash -c you can use:
if [[ -f "${input/\~/$HOME}" ]]; then
echo "file exists"
fi
You can't have tilde expansion in this part of the program without using something based on eval—and you don't want to do that with user input. So, your poor-man solution will be to substitute any potential leading ~/ with the expansion of $HOME/. Here's the adaptation of your script in an arguably better style:
#!/bin/bash
read -e -p "Enter: " input
input=${input/#~\//$HOME/} # <--- this is the main idea of this answer (and it's rather poor)
echo "$input"
if [[ -f $input ]]; then
read -e -p "Do you REALLY want to delete this file? " input2
if [[ ${input2,,} = y ]]; then
rm -f -- "$input"
elif [[ ${input2,,} = n ]]; then
exit
else
echo "Invalid Option"
exit
fi
else
echo "Invalid Option!"
fi
exit
Now, out of curiosity, why are you spending time to make a wrapper around rm? you're making a clunky interface to an already existing program, without adding anything to it, only rendering it less powerful and less easy to use.
If all what you want it's to ask the user before deleting, you can use:
rm -i
This will give you appropriate error in the case file does not exist.

How to get shell to self-detect using zsh or bash

I've a question on how to tell which shell the user is using. Suppose a script that if the user is using zsh, then put PATH to his .zshrc and if using bash should put in .bashrc. And set rvmrc accordingly.
#!/usr/bin/env bash
export PATH='/usr/local/bin:$PATH' >> ~/.zshrc
source ~/.zshrc
I've tried the following but it does not work : (
if [[ $0 == "bash" ]]; then
export PATH='/usr/local/bin:$PATH' >> ~/.bashrc
elif [[ $0 == "zsh" ]]; then
export PATH='/usr/local/bin:$PATH' >> ~/.zshrc
fi
# ... more commands ...
if [[ $0 == "bash" ]]; then
[[ -s '/Users/`whoami`/.rvm/scripts/rvm' ]] && source '/Users/`whoami`/.rvm/scripts/rvm' >> ~/.bashrc
source ~/.bashrc
elif [[ $0 == "zsh" ]]; then
[[ -s '/Users/`whoami`/.rvm/scripts/rvm' ]] && source '/Users/`whoami`/.rvm/scripts/rvm' >> ~/.zshrc
source ~/.zshrc
fi
If the shell is Zsh, the variable $ZSH_VERSION is defined. Likewise for Bash and $BASH_VERSION.
if [ -n "$ZSH_VERSION" ]; then
# assume Zsh
elif [ -n "$BASH_VERSION" ]; then
# assume Bash
else
# assume something else
fi
However, these variables only tell you which shell is being used to run the above code. So you would have to source this fragment in the user's shell.
As an alternative, you could use the $SHELL environment variable (which should contain absolute path to the user's preferred shell) and guess the shell from the value of that variable:
case $SHELL in
*/zsh)
# assume Zsh
;;
*/bash)
# assume Bash
;;
*)
# assume something else
esac
Of course the above will fail when /bin/sh is a symlink to /bin/bash.
If you want to rely on $SHELL, it is safer to actually execute some code:
if [ -n "$($SHELL -c 'echo $ZSH_VERSION')" ]; then
# assume Zsh
elif [ -n "$($SHELL -c 'echo $BASH_VERSION')" ]; then
# assume Bash
else
# assume something else
fi
This last suggestion can be run from a script regardless of which shell is used to run the script.
Just do echo $0
it says -zsh if it's zsh and -bash if it's bash
EDIT: Sometimes it returns -zsh and sometimes zsh and the same with bash, idk why.
A word of warning: the question you seem to have asked, the question you meant to ask, and the question you should have asked are three different things.
“Which shell the user is using” is ambiguous. Your attempt looks like you're trying to determine which shell is executing your script. That's always going to be whatever you put in the #! line of the script, unless you meant your users to edit that script, so this isn't useful to you.
What you meant to ask, I think, is what the user's favorite shell is. This can't be determined fully reliably, but you can cover most cases. Check the SHELL environment variable. If it contains fish, zsh, bash, ksh or tcsh, the user's favorite shell is probably that shell. However, this is the wrong question for your problem.
Files like .bashrc, .zshrc, .cshrc and so on are shell initialization files. They are not the right place to define environment variables. An environment variable defined there would only be available in a terminal where the user launched that shell and not in programs started from a GUI. The definition would also override any customization the user may have done in a subsession.
The right place to define an environment variable is in a session startup file. This is mostly unrelated to the user's choice of shell. Unfortunately, there's no single place to define environment variables. On a lot of systems, ~/.profile will work, but this is not universal. See https://unix.stackexchange.com/questions/4621/correctly-setting-environment and the other posts I link to there for a longer discussion.
You can simply try
echo $SHELL
the other answers fail with set -u
if [ ! -z ${ZSH_VERSION+x} ]; then
echo "this is zsh"
echo ${(%):-%x}
elif [ ! -z ${BASH_VERSION+x} ]; then
echo "this is bash"
echo $BASH_SOURCE
else
echo "not recognized"
fi
An alternative, might not work for all shells.
for x in $(ps -p $$)
do
ans=$x
done
echo $ans
Myself having a similar problem, settled for:
_shell="$(ps -p $$ --no-headers -o comm=)"
if [[ $_shell == "zsh" ]]; then
read -q -s "?Do it?: "
fi
elif [[ $_shell == "bash" || $_shell == "sh" ]]; then
read -n 1 -s -p "Do it [y/n] "
fi
Here is how I am doing it based on a previous answer from Gilles :
if [ -n "$ZSH_VERSION" ]; then
SHELL_PROFILE="$HOME/.zprofile"
else
SHELL_PROFILE="$HOME/.bash_profile"
fi
echo "export VAR1=whatever" >> $SHELL_PROFILE
echo "INFO: Refreshing your shell profile: $SHELL_PROFILE"
if [ -n "$ZSH_VERSION" ]; then
exec zsh --login
else
source $SHELL_PROFILE
fi

Resources