I've written this script:
#!/bin/sh
DEVICE=`sysctl hw.machine`
if [ $DEVICE = "hw.machine: iPhone3,1" ]
then
echo "Test Done"
else
echo "Test failed"
fi
After I run it I've got a message: ./test: line 5: [: too many arguments why isn't it working ?
You should always quote your expansions. [ is an alias for the test command. Just like any other command it takes arguments. The $DEVICE variable is expanded prior to the command running.
If $DEVICE contained whitespace, the command would look like this:
[ foo bar = "hw.machine: iPhone3,1" ]
In this example test is getting the arguments "foo" and "bar" before the comparison operator "=".
You need to quote the expansion:
if [ "$DEVICE" = "hw.machine: iPhone3,1" ]
Another note is that if using [[ in bash, this is not an issue as word splitting does not occur inside of [[.
See the following for more information on quoting: http://mywiki.wooledge.org/Quotes
Related
I'm trying to make a script which starts with the shebang #!/bin/sh, and which, amongst other things, loops through the script's arguments, in order to check whether a given argument is present.
Here's what I've come up with so far:
#!/bin/sh
mother_flag="n"
virgin_flag="n"
reset_flag="n"
i=0
while [ $i -le $# ]
do
echo "${i}" # <<< This line isn't doing what I want it to do!
if [ ${i} = "--mother" ]; then
mother_flag="y"
elif [ ${i} = "--virgin" ]; then
virgin_flag="y"
elif [ ${i} = "--reset" ]; then
reset_flag="y"
fi
i=$((i+1))
done
echo "mother_flag = $mother_flag"
echo "virgin_flag = $virgin_flag"
echo "reset_flag = $reset_flag"
This runs, but it doesn't do what I want it to do. If I run sh my_script --mother, ${i} gives 0 and then 1. What should I put to make ${?} (or its replacement) give my_script and then --mother? Or is this impossible in shell script?
Or is there a better way of checking through one's arguments in shell script?
What I want to do is take a list of command-like arguments like abc "def ghi" "foo bar" baz (note that some arguments are quoted because they contain spaces), and separate them out into two lists of arguments which then get passed to other programs that are invoked by the script. For example, odd-numbered arguments to one program and even-numbered arguments to another program. It is important to preserve proper quoting.
Please note, I need a solution in pure Bourne Shell script (i.e., sh not bash or such). The way I'd do this in Bash would be to use arrays, but of course the Bourne Shell doesn't have support for arrays.
At the cost of iterating over the original arguments twice, you can define a function that can run a simple command using only the even or odd arguments. This allows us to use the function's arguments as an additional array.
# Usage:
# run_it <cmd> [even|odd] ...
#
# Runs <cmd> using only the even or odd arguments, as specified.
run_it () {
cmd=${1:?Missing command name}
parity=${2:?Missing parity}
shift 2
n=$#
# Collect the odd arguments by discarding the first
# one, turning the odd arguments into the even arguments.
if [ $# -ge 1 ] && [ $parity = odd ]; then
shift
n=$((n - 1))
fi
# Repeatedly move the first argument to the
# to the end of the list and discard the second argument.
# Keep going until you have moved or discarded each argument.
while [ "$n" -gt 0 ]; do
x=$1
if [ $n -ge 2 ]; then
shift 2
else
shift
fi
set -- "$#" "$x"
n=$((n-2))
done
# Run the given command with the arguments that are left.
"$cmd" "$#"
}
# Example command
cmd () {
printf '%s\n' "$#"
}
# Example of using run_it
run_it cmd even "$#"
run_it cmd odd "$#"
This might be what you need. Alas, it uses eval. YMMV.
#!/bin/sh
# Samples
foo() { showme foo "$#"; }
bar() { showme bar "$#"; }
showme() {
echo "$1 args:"
shift
local c=0
while [ $# -gt 0 ]; do
printf '\t%-3d %s\n' $((c=c+1)) "$1"
shift
done
}
while [ $# -gt 0 ]; do
foo="$foo \"$1\""
bar="$bar \"$2\""
shift 2
done
eval foo $foo
eval bar $bar
There's no magic here -- we simply encode alternating arguments with quote armour into variables so they'll be processed correctly when you eval the line.
I tested this with FreeBSD's /bin/sh, which is based on ash. The shell is close to POSIX.1 but is not necessarily "Bourne". If your shell doesn't accept arguments to shift, you can simply shift twice in the while loop. Similarly, the showme() function increments a counter, an action which can be achieved in whatever way is your favourite if mine doesn't work for you. I believe everything else is pretty standard.
I am using Fish shell....
Basically, to do something like this:
if (first argument == --r) {
do something
} else {
Do something
if (first argument == --n) {
do more
}
}
To achieve the first if statement I tried:
if test (count $argv) -eq 1 -a $argv[1] = '--r'
But that gives a message:
test: Missing argument at index 6
Functions in Fish don't require their parameters to be specified when you define the function. Any arguments sent to the function by the user are automatically stored in an array called argv. In order to determine whether arguments were sent, you can either count the number of elements in the array, or determine the length of the array as a string. I do the latter:
function my_func
if [ -z "$argv" ]; # No arguments
echo "No arguments supplied"
return
else # At least one argument
if [ "$argv[1]" = "--r" ];
echo "Excellent!"
return
end
end
end
If you prefer to use count, then it will look more like this:
function my_func
if [ (count $argv) -eq 1 -a "$argv[1]" = "--r" ];
# Exactly one argument with specified value "--r"
echo "Excellent!"
return
else # May have arguments, but none equal to "--r"
echo "Give me the right arguments"
return
end
end
Your use of set -q argv[1] is also a good option. But when you're checking for string equality, don't forget to surround your variable in quotes, like this: test "$argv[1]" = "--r".
Here's another method, using the switch...case conditional test:
function my_func
# No arguments
if [ -z "$argv" ]; and return
# At least one argument
switch $argv[1];
case --r;
# do some stuff
return
case "*";
# Any other arguments passed
return
end
end
end
This worked for me:
if set -q argv[1] ;and test $argv[1] = "--r"
Let's start with the error you get when executing this:
if test (count $argv) -eq 1 -a $argv[1] = '--r'
That happens because fish first expands $argv[1] then executes test. If argv has no values then that statement turns into
if test 0 -eq 1 -a = '--r'
Which isn't valid syntax for the test command. It doesn't matter that the first sub-expression evaluates to false since test parses the entire expression before evaluating it.
Rather than doing test (count $argv) -eq 1 just do set -q argv[1] to test if argv has at least one argument. Note the lack of a dollar-sign.
If you're using fish 2.7.0 or newer I recommend the new argparse builtin for handling arguments. Several of the standard functions that ship with fish use it so you can look at them, as well as man argparse, for examples of how to use it. Using argparse is almost always safer, less likely to result in bugs due to sloppy argument parsing using hand written fish script, and will provide argument parsing semantics identical to most commands including all the fish builtins. Including correctly handling short and long flags.
well if the argument is optional then you can do this by:
//check if variable exists
if (typeof variable === 'undefined'){
}
else{
if(typeof variable){}
}
I'm trying to get a not-so complex if-else to work on my FreeBSD box but I'm getting error with the second part of the condition.
basically, this is what I'm trying to do
if not file-exists or (file-exists and string exists in file) then
do this
else
do something else
this is the actual code I'm using
if [ ! -f /boot/loader.conf ] || [[ -f /boot/loader.conf ] && ! grep -Fqx "zfs_l
oad" /boot/loader.conf ]; then
echo "found"
else
echo "not found"
fi
It gives me an error about "[[". I tried adding/removing brackets to no avail.
I've also searched the net for similar examples but the ones I've seen are very simplistic (i.e. if var=value then do this)
I could separate the conditions into 2 "ifs" but I think it can be done in 1 and I'm want to know "advance" if-else in bourne as well. :)
Any help would be greatly appreciated.
thanks :)
Use { ... } for grouping without the overhead and side-effects of a subshell (as created by ( ... )). [[ ]] is a different syntax, only available in ksh derivatives such as bash, which replaces [ ... ] with a less-error-prone alternative; it isn't available in baseline POSIX shells.
[ ! -f /boot/loader.conf ] || \
{ [ -f /boot/loader.conf ] && ! grep -Fqx "zfs_load" /boot/loader.conf; }
I have a perl script that needs to check for an empty directory on a remote machine. Using ksh I can get the following shell script to work:
ksh# ssh user#host '[ "$(ls -A /empty/dir/* 2>/dev/null)" ] && echo "1" || echo "0"'
This correctly returns a "0" if the directory is empty or does not exist. It returns a "1" only if the directory contains something.
When I place this line inside of the perl script though like so:
#!/usr/bin/perl
print `ssh user\#host '[ "$(ls -A /empty/dir/* 2>/dev/null)" ] && echo "1" || echo "0"'`
No matter what I put in there it returns a "1", empty directory or not. I've checked env values compared to the normal shell and the perl script and they are the same.
Does anyone have any ideas why this command would return different results only in the perl script?
Both machines are AIX 6.1 with KSH as the default shell.
Text inside backticks is interpolated as if it were inside double quotes before being passed to the OS. Run
print qq`ssh user\#host '[ "$(ls -A /empty/dir/* 2>/dev/null)" ] && echo "1" || echo "0"'`
to see exactly what string is getting passed to the OS. I'll bet you'll at least have to escape the $.
A safer and saner way is to build your command first and run it inside backticks later:
# q{...} does no interpolation
my $cmd = q{ssh user\#host '[ "$(ls -A /empty/dir/* 2>/dev/null)" ] && echo "1" || echo "0"'};
print `$cmd`;
use Net::SFTP::Foreign;
my $s = Net::SFTP::Foreign->new('user#host');
my $empty = 1;
if (my $d = $s->opendir('/empty/dir')) {
if (defined $s->readdir($d)) {
$empty = 0
}
}