I have a shell script where I pass (2) parameters, one to pass a dbname, the other to call one of (2) filenames. I want to check if either filename exists, then proceed with calling that script, else exit because the user can enter the wrong string and construct my_foo.sql which I don't want. I don't think I have the condition for setting "or" correctly since putting the correct param still produces error. Is there a better way to write this?
Here is what I have so far.
#/usr/bin/ksh
if [ $# != 2 ]; then
echo "Usage: test.sh <dbname> <test|live>" 2>&1
exit 1
fi
# Check actual file name
CHKSCRIPT1=/tmp/my_test.sql;
CHKSCRIPT2=/tmp/my_live.sql;
if [ -f "CHKSCRIPT1" ] || [ -f "CHKSCRIPT2" ]
then
/bin/sqlplus -s foo/bar #/my_$2.sql
else
echo "Correct sql script does not exist. Enter test or live"
exit 1
fi
Your issue is that you're not referencing your variables correctly:
if [ -f "$CHKSCRIPT1" ] || [ -f "$CHKSCRIPT2" ]
...
fi
edit: Per #chepner, you shouldn't use -o
In addition to the problem you had with expanding the parameters, you should separate what the user types from what files need to exist. If the user enters "live", the only thing that matters is whether or not /tmp/my_live.sql exists. If the user enters "injection_attack", your script should not execute /tmp/my_injection_attack.sql (which presumably was created without your knowledge). The right thing to do is to first verify that a valid command was entered, then check if the appropriate file exists.
if [ $# != 2 ]; then
echo "Usage: test.sh <dbname> <test|live>" 2>&1
exit 1
fi
case $2 in
test|live)
filename="/tmp/my_{$2}.sql"
;;
*) echo "Must enter test or live"
exit 1
;;
esac
if [ -f "$filename" ]; then
/bin/sqlplus -s foo/bar #/my_$2.sql
else
echo "SQL script $filename does not exist."
exit 1
fi
Related
I'm trying to implement a bash script who supposed to search for a word in a Python script terminal output.
The Python script doesn't stop so "&" in the end of the command is needed but the "if [ $? == 0 ] ; then" condition doesn't work.
How it can be solved?
Thanks, Gal.
#!/bin/bash
#Check if Pixhawk is connected
PORT=/dev/ttyPixhawk
end=$((SECONDS+3))
not_exists=f
/usr/local/bin/mavproxy.py --daemon --non-interactive --master=$PORT | grep 'Failed' &> /dev/null &
while [ $SECONDS -lt $end ] ; do
if [ $? == 0 ] ; then
not_exists=t
fi
sleep 1
done
if [ $not_exists=t ] ; then
echo "Not Exists"
else
echo "Exists"
fi
kill $(pgrep -f '/usr/local/bin/mavproxy.py')
Bash doesn't know anything about the output of background commands. Check for yourself with [ 5444 -lt 3 ] & echo $?.
your if statement wouldn't work in any case because $? checks for the return value of the most recent previous command, which in this case is your while loop.
You have a few different options. If you're waiting for some output, and you know how long it is in the output until whatever target you're looking for occurs, you can have the python write to a file and keep checking on the file size with a timeout for failure.
You can also continue with a simple timed approach as you have where you just check the output after a few seconds and decide success or failure based on that.
You can make your python script actually end, or provide more error messages, or write only the relevant parts to file that way.
Furthermore, you really should run your script through shellcheck.net to notice more problems.
You'll need to define your goal and use case more clearly to get real help; all we can really say is "your approach will not work, but there are definitely approaches which will work"
You are checking the status of grep command output inside while loop using $?. This can be done if $? is the next command to be fired after grep and if grep is not a back-group process . But in your script, $? will return the status of while [$SECONDS -lt $end ]. You can try to re-direct the output to a temp file and check it's status
/usr/local/bin/mavproxy.py --daemon --non-interactive --master=$PORT | grep 'Failed' &> tmp.txt &
sleep 3
# If file exists and it's size is greater than 0, [ -s File] will return true
if [ -s tmp.txt ]; then
echo 'pattern exists'
else
echo 'pattern not exists'
fi
So I'm currently just trying to traverse through my current directory where I'm calling the following bash script that prints 'We found a .c file' every time one is found. I have an if statement to check for args because I will be extending the script where if no args are found it will run anyway, and one arg will tell the script the directory to look in.
The issue is, this code does not work:
if [ -z "$#" ]
then
for i in *.c; do
echo "We found a .c file"
done
fi
But then if I add the echo "Test" in, it works?
if [ -z "$#" ]
echo "Test"
then
for i in *.c; do
echo "We found a .c file"
done
fi
I'm new to bash and no clue why this is happening. Can anyone help me out?
$#, which reports the count of arguments, is NEVER an empty string - if you don't specify arguments, $# evaluates to 0, which is still a nonempty string (-z tests for empty strings).
Therefore, [ -z "$#" ] is always (logically) false.
What you're looking for - using idiomatic Bash - is:
if [[ $# -eq 0 ]]; then ... # -eq compares *numerically*
As anishsane points out in a comment, the POSIX-compliant [ $# -eq 0 ] would work here as well; generally, though - unless your express intent is to write POSIX-compliant shell code - you're better off sticking with the more predictable, more feature-rich (and marginally faster) Bash-specific constructs.
or, using arithmetic evaluation:
if (( $# == 0 )); then ...
As for why your 2nd snippet caused the if branch to be entered:
Your misplaced echo "Test" - due to being placed before the then keyword, caused the echo command to be interpreted as part of the conditional.
In other words: the conditional that was evaluated was effectively
[ -z "$#" ]; echo "Test", a list of (two) commands only whose last command's exit code determined the outcome of the conditional.
Since echo always succeeds (exit code 0)[1]
, the conditional as a whole evaluated to (logical) true, and the if branch was entered.
[1] gniourf_gniourf points out in a comment that you can make a simple echo command fail (with exit code 1), if you use input/output redirection with an invalid source/target; e.g., echo 'fail' > /dev/full.
(Note that if the redirection source/target is fundamentally invalid - an nonexistent input file or an output file that can't be created / opened (as opposed to, say, an output target that can be opened with write permission but ultimately can't be written to, such as /dev/full on Linux) - Bash never even invokes the command at hand, as it "gives up" when it encounters the invalid redirection:
{ echo here >&2; echo hi; } >/dev/full # Linux: 'here' still prints (to stderr)
{ echo here >&2; echo hi; } >'' # invalid target: commands are never invoked)
Problem
The following loop will never run:
if [ -z "$#" ]
then
for i in *.c; do
echo "We found a .c file"
done
fi
The reason is that $# is a number, 0, 1 or more. It will never be an empty string. Thus [ -z "$#" ] will always fail
This loop will always run:
if [ -z "$#" ]
echo "Test"
then
for i in *.c; do
echo "We found a .c file"
done
fi
While [ -z "$#" ] always fails the second statement echo "Test" normally returns a success exit code.
Solution
If no arguments were specified on the command line, this sets the arguments to all .c files in the current directory:
[ "$1" ] || set -- *.c
for i in "$#"; do
echo "We found a .c file: $i"
done
Thus, this allows you to specify the file names on the command line and the script runs on those. If you don't specify any, it runs on all the .c files.
So I started today taking a look at scripting using vim and I'm just so very lost and was looking for some help in a few areas.
For my first project,I want to process a file as a command line argument, and if a file isn't included when the user executes this script, then a usage message should be displayed, followed by exiting the program.
I have no clue where to even start with that, will I need and if ... then statement, or what?
Save vim for later and try to learn one thing at a time. A simpler text editor is called nano.
Now, as far as checking for a file as an argument, and showing a usage message otherwise, this is a typical pattern:
PROGNAME="$0"
function show_usage()
{
echo "Usage: ${PROGNAME} <filename>" >&2
echo "..." >&2
exit 1
}
if [[ $# -lt 1 ]]; then
show_usage
fi
echo "Contents of ${1}:"
cat "$1"
Let's break this down.
PROGNAME="$0"
$0 is the name of the script, as it was called on the command line.
function show_usage()
{
echo "Usage: ${PROGNAME} <filename>" >&2
echo "..." >&2
exit 1
}
This is the function that prints the "usage" message and exits with a failure status code. 0 is success, anything other than 0 is a failure. Note that we redirect our echo to &2--this prints the usage message on Standard Error rather than Standard Output.
if [[ $# -lt 1 ]]; then
show_usage
fi
$# is the number of arguments passed to the script. If that number is less than 1, print the usage message and exit.
echo "Contents of ${1}:"
cat "$1"
$1 is out filename--the first argument of the script. We can do whatever processing we want to here, with $1 being the filename. Hope this helps!
i think you're asking how to write a bash script that requires a file as a command-line argument, and exits with a usage message if there's a problem with that:
#!/bin/bash
# check if user provided exactly one command-line argument:
if [ $# -ne 1 ]; then
echo "Usage: `basename "$0"` file"
exit 1
# now check if the provided argument corresponds to a real file
elif [ ! -f "$1" ]; then
echo "Error: couldn't find $1."
exit 1
fi
# do things with the file...
stat "$1"
head "$1"
tail "$1"
grep 'xyz' "$1"
I am trying to execute a hallo_word.sh that is stored at ~/bin from this script that is stored at my ~/Desktop. I have made both scripts executable. But all the time I get the problem message. Any ideas?
#!/bin/sh
clear
dir="$PATH"
read -p "which file you want to execute" fl
echo ""
for fl in $dir
do
if [ -x "$fl" ]
then
echo "executing=====>"
./$fl
else
echo "Problem"
fi
done
This line has two problems:
for fl in $dir
$PATH is colon separated, but for expects whitespace separated values. You can change that by setting the IFS variable. This changes the FIELD SEPARATOR used by tools like for and awk.
$fl contains the name of the file you want to execute, but you overwrite its value with the contents of $dir.
Fixed:
#!/bin/sh
clear
read -p "which file you want to execute" file
echo
IFS=:
for dir in $PATH ; do
if [ -x "$dir/$file" ]
then
echo "executing $dir/$file"
exec "$dir/$file"
fi
done
echo "Problem"
You could also be lazy and let a subshell handle it.
PATH=(whatever) bash command -v my_command
if [ $? -ne 0 ]; then
# Problem, could not be found.
else
# No problem
fi
There is no need to over-complicate things.
command(1) is a builtin command that allows you to check if a command exists.
The PATH value contains all the directories in which executable files can be run without explicit qualification. So you can just call the command directly.
#!/bin/sh
clear
# r for raw input, e to use readline, add a space for clarity
read -rep "Which file you want to execute? " fl || exit 1
echo ""
"$fl" || { echo "Problem" ; exit 1 ; }
I quote the name as it could have spaces.
To test if the command exists before execution use type -p
#!/bin/sh
clear
# r for raw input, e to use readline, add a space for clarity
read -rep "Which file you want to execute? " fl || exit 1
echo ""
type -p "$fq" >/dev/null || exit 1
"$fl" || { echo "Problem" ; exit 1 ; }
In my program, I have to check whether a command given as a input by a user exists or not and if it exists, program needs to check if the parameters of that command are correct.
For example:
ls ( is correct)
-al (is correct)
do the watch
and if I do this:
ls (is correct)
-kala (not correct)
don't do the watch.
How I can do this? Here is my script:
while true
do
echo "Insert the command"
read comm
if [ "$(type -t $comm)" != "" ]; then
echo "Insert the parameters of the command ";
read par;
echo "Insert the time of watch";
read time;
if [ $t -le 0 ]; then
echo "Value not correct";
else
clear;
while true
do
echo "$comm"
date
echo ""
$comm $par
sleep $((time))
clear
done
fi;
else
echo "Command not found, retry.";
echo "";
fi
done
You can replace the command invocation with this:
if ! $comm $par; then
exit 1
fi
to make it stop after an error. Also there is already a tool called watch but I think you already know this.