Cannot understand why the function myfunc fails. I put a string in src and dst, and I get the message
src is blank
dst is blank
Here is the code
zblank ()
{
local s="$1"
declare -i f=1 num=0
[[ $s =~ ^[[:space:]]*$ ]] && num=1
return $num
}
myfunc
{
src="angio"
dst="angio"
(
function myzblank() { zblank "$#"; }
myzblank "$src" && printf '%s\n' "src is blank"
myzblank "$dst" && printf '%s\n' "dst is blank"
)
}
When you use return $num, the result is interpreted as an exit status.
Exit status 0 means success.
Every nonzero exit status means failure.
Thus, when you set num=1 only if [[ $s =~ ^[[:space:]]*$ ]], you're reporting success unless that happens, so the && branch is executed.
If you want zblank to return success only if the variable you pass it is blank, it should instead look like:
zblank() { [[ $1 =~ ^[[:space:]]*$ ]]; }
You don't need return: By default the return value of a function is that of the last command it ran.
You don't need $s: it doesn't make things any terser or clearer than $1.
You don't need declare -i: When a variable is used in an arithmetic context its value is treated as numeric even if it was a string previously.
And if you wanted it to return success only in the case where $1 is not all-spaces, you could write it as:
znonblank() { ! [[ $1 =~ ^[[:space:]]*$ ]]; }
...addition of a leading ! being the only change needed.
All that said -- you don't need bash extensions like [[ ]] to do this at all. POSIX sh is powerful enough for what you're doing here:
zblank() {
case $1 in
*[![:space:]]*) return 1;; # At least one non-space character exists
*) return 0;; # No non-spaces exist; success.
esac
}
Related
I'd like to write a script that takes 2 parameters,
The first is parameter that could be "alpha/beta/tcp_friendliness/fast_convergence".
The Second should be a number for the alpha/beta cases and a 0/1 for the other 2.
for example: ./manage_cc alpha 512
Now i've wrote the following script that supposedly covers my cases, but it seems to go into all the conditionals. surely my syntax is broken so any help would be appreciated.
#!/bin/bash
echo -n $2 > /sys/module/tcp_tuner/parameters/$1
if [["$1" == ""] || ["$2" == ""]]
then
echo "You need to pass a property to modify as a first parameter and a value as the second"
fi
if [["$1" == "alpha"] || ["$1" == "beta"]]
then
echo -n $2 > /sys/module/tcp_tuner/parameters/$1
else
if [["$1" == "tcp_friendliness"] || ["$1" == "fast_convergence"]]
then
if [["$2" != "0"] && ["$2" != "1"]]
then
echo "This parameter only accepts a boolean value (0/1)"
exit 1
else
echo -n $2 > /sys/module/tcp_tuner/parameters/$1
fi
else
echo "The only accepted values for first parameter are alpha/beta/tcp_friendliness/fast_convergence"
exit 1
fi
fi
A rewrite of your code:
#!/usr/bin/env bash
write() {
printf "%s" "$2" > "/sys/module/tcp_tuner/parameters/$1"
}
die() {
echo "$*" >&2
exit 1
}
main() {
[[ -z $2 ]] && die "You need to pass a property to modify as a first parameter and a value as the second"
case $1 in
alpha|beta)
write "$1" "$2"
;;
tcp_friendliness|fast_convergence)
if [[ "$2" == "0" || "$2" == "1" ]]; then
write "$1" "$2"
else
die "This parameter only accepts a boolean value (0/1)"
fi
;;
*) die "The only accepted values for first parameter are alpha/beta/tcp_friendliness/fast_convergence"
;;
esac
}
main "$#"
Your condition syntax is wrong. When you start a condition with [[ you have to end it with ]], not just ]. They don't nest like parentheses.
if [[ "$1" == "alpha" || "$1" == "beta" ]]
I would like to ask you and request for the help with this particular part of the script - I would like to make it more compact as I feel it is too long, or there might me a shorter syntax alternatives...
This part is cut from my script for creation of X amount of files with descending date (1 day jumps, so each file is 1 day older that previous), which is already 3x longer than the whole "implementation" part.
This part's purpose is to maintain, check and correct (appropriately) the arguments defined with the script execution, which looks like
./script.sh X X
whereas script expects two arguments (number indicating amount of files to create and the path to the folder, where it is supposed to create those0:
- if there is just 1 it assumes the user wants to create files in the present folder, therefore it checks, if the one included argument is a number and if it is it will add output from pwd to the path (in this case to not have some sort of "cross-mount" accident with the system variable PATH conveniently named PATHX), so there will be two arguments at the end anyway
- if there are more than 2, it will terminate the script
If there are 2 arguments, it performs additional "tasks:
1. checks if there are just two numbers, if so, script ends
2. checks if there are just two words/letters, if so, script ends
3. if there are, let's say, just two dots as arguments, it will end anyway as there is one if, which always will need one argument to be just number
4. after first steps this will save the arguments to the variables (it is necessary as I have had some problems in the implementation part), this step even coves any possible combination of the positions of the arguments (it does not matter now, if the amount is first or second; same for the path)
5. this is a very last step, which just (for any case) covers the case, the path included has not been inserted with the slash in front (as this is mandatory for bash to recognise it indicates an absolute path) or at the end (important for the touch command in the implementation path as the command looks like "touch -t *TIMESTAMP* "$PATHXfile$i""); together with it it does provide an appropriate action, if the path is defined with ".","..","./","../" - if there would be a full path, not relative one
if [[ $# -eq 2 ]]
then
if [[ $1 == ?(-)+([0-9]) ]] && [[ $2 == ?(-)+([0-9]) ]]
then
echo "Invalid arguments. Did you include a slash at the start of the path (if the file name consists only of the numbers)? Well, try it again. Terminating script..."
exit
elif [[ $1 == ?(-)+([a-z]) ]] && [[ $2 == ?(-)+([a-z]) ]]
then
echo "Only characters in the arguments. Is this some sort of a joke? If you have tried some sick hexadecimal format of number (just A-F), it will not work, mate. Terminating script..."
exit
fi
if [[ $1 == ?(-)+([0-9]) ]]
then
AMOUNT=$1
PATHX=$2
else
AMOUNT=$2
if [[ $AMOUNT != ?(-)+([0-9]) ]]
then
echo "No argument with number/numbers-only as an amount of files to create. Terminating script..."
exit
fi
PATHX=$1
fi
else
if [[ $# -eq 1 ]]
then
if [[ $1 == ?(-)+([0-9]) ]]
then
AMOUNT=$1
PATHX=$(pwd)
else
echo "Please, include the argument with an amount of files to create. Terminating script..."
exit
fi
else
echo "2 Arguments are expected. Terminating script..."
exit
fi
fi
if [[ $PATHX != /* ]]
then
if [[ "$PATHX" == "." ]] || [[ "$PATHX" == ".." ]] || [[ "$PATHX" == "./"* ]] || [[ "$PATHX" == "../"* ]]
then
true
else
PATHX=$(echo "/$PATHX")
fi
fi
if [[ $PATHX != */ ]]
then
PATHX=$(echo "$PATHX/")
fi
Excuse this formatting, but without adding the description to the code sample it is just a mess of text...
Anyway, thank you, Guys, for any input.
It may be reduced to:
[[ $# -gt 2 ]] && { echo "Incorrect number of arguments"; exit 1; }
[[ $1 == ?(-)+([0-9]) ]] || { echo "First argument is not a number"; exit 2; }
[[ $2 == ?(-)+([0-9]) ]] && { echo "Both arguments are numbers"; exit 3; }
[[ $2 == +([a-z]) ]] || { echo "File name must be only letters"; exit 4; }
[[ ! -d $2 ]] && { echo "Path does not exist"; exit 5; }
n=$1
p=${2:-$PWD} # Use the PWD if path is missing.
if [[ $p != */ ]]; then
echo "Missing trailing slash; correcting";
p+=/
fi
if [[ $p != /* ]]; then
echo "Missing leading slash; correcting";
[[ $p == #(.|..|./*|../*) ]] || p=/"$p"
fi
#!/bin/bash
# Code to generate script usage
if [[ "$#" -ne 1 ]] && [[ "$#" -ne 2 ]]; then
flag=1;
elif ! [[ "$1" == "abcd" || "$1" == "dcba" ]]; then
echo "Invalid"
flag=1;
fi
while [ $# -gt 1 ]
do
case $2 in
'streams')
;;
*)
echo "unrecognised optional arg $2"; flag=1;
;;
esac
shift
done
if [ "$flag" == "1" ]; then
echo "Usage:"
exit
fi
function main {
arg1=$1
streams=$2
if [ "${streams}" == "streams" ]; then
echo entering here
else
echo entering there
fi
}
parent_dir=`pwd`
find $parent_dir -name "*" -type d | while read d; do
cd $denter code here
main $1 $2
done
Why the code does not enter "entering here" when script run with arguments "abcd" and "streams" ?
I feel that function having two arguments is causing the problem, code was working fine with one argument
Several things you might want to fix in your code, before attempts are made to find the specific problem. It is possible that it will disappear after modifying your script accordingly. If the problem is still alive, I'll edit my answer with a solution. If you decide to apply the following changes, please update your code in the question.
Consistent usage of either [[ or [. [[ is a Bash keyword similar to (but more powerful than) the [ command.
See
Bash FAQ 31
Tests And Conditionals
Unless you're writing for POSIX sh, I recommend [[.
Use (( for arithmetic expressions. ((...)) is an arithmetic command, which returns an exit status of 0 if the expression is nonzero, or 1 if the expression is zero. Also used as a synonym for let, if assignments are needed. See Arithmetic Expression.
Use the variable PWD instead of pwd. PWD is a builtin variable in all POSIX shells that contains the current working directory. pwd(1) is a POSIX utility that prints the name of the current working directory to stdout. Unless you're writing for some non-POSIX system, there is no reason to waste time executing pwd(1) rather than just using PWD.
The function keyword is not portable. I suggest you to avoid using it and simply write function_name() { your code here; } # Usage
$parent_dir is not double-quoted. "Double quote" every literal that contains spaces/metacharacters and every expansion: "$var", "$(command "$var")", "${array[#]}", "a & b". See
Quotes
Arguments
ShellCheck your code before uploading.
Replace the while condition logic with an if condition, so that shift is no longer required. Shift was the devil I was facing I found.
#!/bin/bash
# Code to generate script usage
if [[ "$#" -ne 1 ]] && [[ "$#" -ne 2 ]]; then
flag=1;
elif ! [[ "$1" == "abcd" || "$1" == "dcba" ]]; then
echo "Invalid"
flag=1;
fi
#while [[ $# -gt 1 ]]
#do
# case $2 in
# 'streams')
# ;;
# *)
# echo "unrecognised optional arg $2"; flag=1;
# ;;
# esac
# shift
#done
if [[ $2 == "streams" ]]; then
:
elif [[ (-z $2) ]]; then
:
else
echo "unrecognised optional arg $2"; flag=1;
fi
if [[ "$flag" == "1" ]]; then
echo "Usage:"
exit
fi
function main {
streams=$2
if [[ "${streams}" == "streams" ]]; then
echo entering here
else
echo entering there
fi
}
parent_dir=`pwd`
find $parent_dir -name "*" -type d | while read d; do
cd $d
main $1 $2
done
Overview:
I am trying to code an error for a function that requires multiple arguments.
example:
myfunction arg1 arg2 arg3 arg4
If any of these arguments are null I want to trow the user a syntax error.
Attempts:
Without writing the individual arguments I tried:
# Error Checking
for i in "{1.4}"
do
# Check for null values of Bash variables:
[[ -z $"$i" ]] && {
printf "Syntax Error: Missing Operator see:\nmyfunction --help\n"
exit
}
done
-and-
# Error Checking
for i in ${1..4}
do
# Check for null values of Bash variables:
[[ -z $i ]] && {
printf "Syntax Error: Missing Operator see:\nmyfunction --help\n"
exit
}
done
Attempted syntax check:
Question:
Is what I am trying to do possible with a for loop?
Any help would be much appreciated.
Thanks in advance!
Edit:
Also tried the following before I started messing with quotes and dollar signs:
# Error Checking
for i in {1..4}
do
[[ -z $"$i" ]] && {
printf "Syntax Error: Missing Operator see:\nansi-color --help\n"
exit
}
done
There are several ways to tackle this:
die () { echo "$#" >&2; exit 1; }
myfn () {
[[ $# == 4 ]] || die 'wrong number of args'
for d in "$#"; do [[ $d ]] || die 'null arg'; done
}
myfn2 () {
[[ $# == 4 ]] || die 'wrong number of args'
for ((i = 1; i <= $#; i++)); do
[[ ${!i} ]] || die 'null arg'
done
}
The "$#" is probably more idiomatic. The indirection with ! is very handy though not common.
Caleb Adams pointed out that I could try something like this:
# Error Checking
[[ $# != 4 ]] && {
printf "Syntax Error: Missing Operator see:\n\tansi-color --help\n"
exit
}
Personally, I'd use a bash array and then check its length:
args=($*)
echo ${#args[*]}
But you can do this:
myfunc() { for i in {1..4}; do
if [[ -z ${!i} ]]; then
echo error at $i
return -1
fi
done
...do other stuff
}
How can I check if a variable is empty in Bash?
In Bash at least the following command tests if $var is empty:
if [[ -z "$var" ]]; then
# $var is empty, do what you want
fi
The command man test is your friend.
Presuming Bash:
var=""
if [ -n "$var" ]; then
echo "not empty"
else
echo "empty"
fi
I have also seen
if [ "x$variable" = "x" ]; then ...
which is obviously very robust and shell independent.
Also, there is a difference between "empty" and "unset". See How to tell if a string is not defined in a Bash shell script.
if [ ${foo:+1} ]
then
echo "yes"
fi
prints yes if the variable is set. ${foo:+1} will return 1 when the variable is set, otherwise it will return empty string.
[ "$variable" ] || echo empty
: ${variable="value_to_set_if_unset"}
if [[ "$variable" == "" ]] ...
The question asks how to check if a variable is an empty string and the best answers are already given for that.
But I landed here after a period passed programming in PHP, and I was actually searching for a check like the empty function in PHP working in a Bash shell.
After reading the answers I realized I was not thinking properly in Bash, but anyhow in that moment a function like empty in PHP would have been soooo handy in my Bash code.
As I think this can happen to others, I decided to convert the PHP empty function in Bash.
According to the PHP manual:
a variable is considered empty if it doesn't exist or if its value is one of the following:
"" (an empty string)
0 (0 as an integer)
0.0 (0 as a float)
"0" (0 as a string)
an empty array
a variable declared, but without a value
Of course the null and false cases cannot be converted in bash, so they are omitted.
function empty
{
local var="$1"
# Return true if:
# 1. var is a null string ("" as empty string)
# 2. a non set variable is passed
# 3. a declared variable or array but without a value is passed
# 4. an empty array is passed
if test -z "$var"
then
[[ $( echo "1" ) ]]
return
# Return true if var is zero (0 as an integer or "0" as a string)
elif [ "$var" == 0 2> /dev/null ]
then
[[ $( echo "1" ) ]]
return
# Return true if var is 0.0 (0 as a float)
elif [ "$var" == 0.0 2> /dev/null ]
then
[[ $( echo "1" ) ]]
return
fi
[[ $( echo "" ) ]]
}
Example of usage:
if empty "${var}"
then
echo "empty"
else
echo "not empty"
fi
Demo:
The following snippet:
#!/bin/bash
vars=(
""
0
0.0
"0"
1
"string"
" "
)
for (( i=0; i<${#vars[#]}; i++ ))
do
var="${vars[$i]}"
if empty "${var}"
then
what="empty"
else
what="not empty"
fi
echo "VAR \"$var\" is $what"
done
exit
outputs:
VAR "" is empty
VAR "0" is empty
VAR "0.0" is empty
VAR "0" is empty
VAR "1" is not empty
VAR "string" is not empty
VAR " " is not empty
Having said that in a Bash logic the checks on zero in this function can cause side problems imho, anyone using this function should evaluate this risk and maybe decide to cut those checks off leaving only the first one.
This will return true if a variable is unset or set to the empty string ("").
if [ -z "$MyVar" ]
then
echo "The variable MyVar has nothing in it."
elif ! [ -z "$MyVar" ]
then
echo "The variable MyVar has something in it."
fi
You may want to distinguish between unset variables and variables that are set and empty:
is_empty() {
local var_name="$1"
local var_value="${!var_name}"
if [[ -v "$var_name" ]]; then
if [[ -n "$var_value" ]]; then
echo "set and non-empty"
else
echo "set and empty"
fi
else
echo "unset"
fi
}
str="foo"
empty=""
is_empty str
is_empty empty
is_empty none
Result:
set and non-empty
set and empty
unset
BTW, I recommend using set -u which will cause an error when reading unset variables, this can save you from disasters such as
rm -rf $dir
You can read about this and other best practices for a "strict mode" here.
To check if variable v is not set
if [ "$v" == "" ]; then
echo "v not set"
fi