File operator command

Hey all,

I not able to find it what -n stand operator stands for.

 
[[ -n $* ]] && print -u2 "ERROR: $*"
 

could you please let me know why we are using -n in the code.

Check out man test

Hope that helps
Regards
Peasant.

2 Likes

thanks a lot.

It checks whether the length of the string s1 is non-zero or not.

The test man page says absolutely nothing about how the [[ expr ]] compound command behaves in various shells. In some shells, [[ is an unknown command; in others it is a keyword in the syntax for that shell. There is a huge difference between the way a shell handles an unquoted $* when arguments are part of the shell's syntax versus when arguments are being parsed as arguments for a utility to be invoked (even if it is a built-in utility as in the case of test and [ ).

Thank you Don for further explanation, but the OP asked about -n operator specifically which is covered in man test.

He did not specify shell or operating system to make further assumptions.

Regards
Peasant.

Yes, but it is a different command altogether, in fact [[..]] is part of the shell syntax, whereas [ .. ] is not and using a regular test command with single brackets would not work properly in this case. Even if the -n operators happen to have a similar meaning, better advise would be to use the man page of the shell..

IMHO one should quote $*, to avoid substitution by file names and eventual "too many tokens"

[[ -n "$*" ]]

Looks different here:

0 ~ $ [[ -n "$*" ]]

1 ~ $ [ -n "$*" ]

1 ~ $ set something

0 ~ $ [ -n "$*" ]

0 ~ $ [[ -n "$*" ]]

0 ~ $ [ -n $* ]

0 ~ $ [[ -n $* ]]

0 ~ $ set ""

0 ~ $ [ -n $* ]

0 ~ $ [[ -n $* ]]

1 ~ $ $SHELL --version
GNU bash, Version 4.3.39(1)-release (x86_64-unknown-linux-gnu)

The only difference i figured is that the double brackets handles unquoted variables, which is bad coding style, whereas single bracket requires them to be quoted.

Cheers

$* doesn't expand to a list of files; it expands to the list of positional parameters. If there is one positional parameter and it is an asterisk, then that would expand to a list of files in the current directory in the command:

[ -n $* ]

but in the command:

[[ -n $* ]]

it just expands to an asterisk. Try using:

set -- "*"
set -x
[ -n $* ]
[[ -n $* ]]

to see the difference.

That is the difference between being a parameter expanded by the shell when invoking a utility (even if it is a built-in) and recognizing a parameter when it is recognized as an argument to the of the shell [[ keyword.

1 Like

Why is that bad coding style? I certainly think you should quote variable expansions, but only in cases where it is actually subject to field splitting and/or globbing, which is not the case here (it certainly is in the case of single brackets). It also is not needed for the word in a case statement, for example, or with variable assignments.

----

It does not make a difference in the case of double brackets..

$ set --  ;  [[ -n $* ]] && echo hello1 ; [[ -n "$*" ]] && echo hello2
$ set "2 3"  ;  [[ -n $* ]] && echo hello1 ; [[ -n "$*" ]] && echo hello2
hello1
hello2
$ set 1 "2 3"  ;  [[ -n $* ]] && echo hello1 ; [[ -n "$*" ]] && echo hello2
hello1
hello2
$ set 1 "*"  ;  [[ -n $* ]] && echo hello1 ; [[ -n "$*" ]] && echo hello2
hello1
hello2
1 Like

Thanks Don and Scrutinizer for pointing that out; the bash man page was not clear for me.
Anyway, I would prefer [[ 0 -ne $# ]] or [ 0 -ne $# ] that would also recognize empty arguments:

set -- ""
[[ -n $* ]] && echo non-empty arg "$1"
[[ 0 -ne $# ]] && echo arg "$1"

Or alternatively, there is also numerical comparison:

(( $#>0 ))

or

(( $# ))

--
But I would also prefer:

[ $# -ne 0 ]

or

[ $# != 0 ]

Can anybody tell me more about this on linux operating systems (the why and the what) :

me@glitch:~$ which "["
/usr/bin/[
me@glitch:~$ ls -dl /usr/bin/[
-rwxr-xr-x 1 root root 39464 Mar 14 16:47 /usr/bin/[
me@glitch:~$ md5sum /usr/bin/test
5ff3a64fba15e925aefe88865efa1931  /usr/bin/test
me@glitch:~$ md5sum /usr/bin/[
cf8b60a5e80fb68e7ab58454f74b7a6d  /usr/bin/[
me@glitch:~$ man -f "[" && man -f "test"
[ (1)                - check file types and compare values
test (1)             - check file types and compare values

On HPUX or Solaris this does not exists.

This may explain the difference:

From the GNU(linux) man test:

NOTE: [ honors the --help and --version options, but test does not.  test treats each of those as it treats any other nonempty STRING.
$ ls -l $(which test [)
-rwxr-xr-x. 1 root root 41448 Jun 10  2014 /usr/bin/[
-rwxr-xr-x. 1 root root 37288 Jun 10  2014 /usr/bin/test

Whereas on some systems these are hard links:

$ ls -li $(which test [)
288077518 -rwxr-xr-x  2 root  wheel  18480 Sep 10  2014 /bin/[
288077518 -rwxr-xr-x  2 root  wheel  18480 Sep 10  2014 /bin/test

or soft links:

ls -li $(which test [)
17108232 -rwxr-xr-x    1 root     sys        18688 Feb  7  2013 /sbin/test
33554580 lrwxr-xr-x    1 root     sys           15 Feb  6  2013 /usr/bin/[ -> ../../sbin/test

Anyway, it is unlikely that these commands are actually being used by your script, since in most shell these are shell builtins

$ type test [
test is a shell builtin
[ is a shell builtin

That's maybe why on some systems [ does not even exist:

$ type -a test [
test is a shell builtin
test is /usr/bin/test
[ is a shell builtin

$ which test [
/usr/bin/test
no [ in /usr/bin /usr/sbin
1 Like

Thanks, the NOTE explains the difference in md5.

Note that $* expanding to a non-empty list says absolutely nothing about the contents of the 1st positional parameter. With a default IFS, it doesn't even tell you if any of the positional parameters are empty. (In that case, it just tells you that there are two or more positional parameters. With the default IFS, $* expands to a list of positional parameters separated by spaces.) The following, when run with ksh93 :

set -- "" ""
set -xv
unset IFS
echo no IFS
[[ -n $* ]] && echo non-empty arg list \"$*\" || echo empty arg list
echo default IFS
IFS=$' \t\n'
[[ -n $* ]] && echo non-empty arg list \"$*\" || echo empty arg list
echo empty IFS
IFS=
[[ -n $* ]] && echo non-empty arg list \"$*\" || echo empty arg list

produces the output:

unset IFS
+ unset IFS
echo no IFS
+ echo no IFS
no IFS
[[ -n $* ]] && echo non-empty arg list \"$*\" || echo empty arg list
+ [[ -n ' ' ]]
+ echo non-empty arg list '"' '"'
non-empty arg list " "
echo default IFS
+ echo default IFS
default IFS
IFS=$' \t\n'
+ IFS=$' \t\n'
[[ -n $* ]] && echo non-empty arg list \"$*\" || echo empty arg list
+ [[ -n ' ' ]]
+ echo non-empty arg list '"' '"'
non-empty arg list " "
echo empty IFS
+ echo empty IFS
empty IFS
IFS=
+ IFS=''
[[ -n $* ]] && echo non-empty arg list \"$*\" || echo empty arg list
+ [[ -n '' ]]
+ echo empty arg list
empty arg list

You get similar output with bash , but the trace output is slightly different.