5

This gives me an error that says too many arguments:

if [ $( file -b $i ) == "directory" ]

But when I tried this

name=$( file -b $i )
if [ name == "directory" ]

It seems to work just fine.

Can someone explain this or point out in the docs an explanation?

7

Couple of issues:

  • ] indicates the end of arguments for [ (test), and it must be the last argument; you have couple of ]s, which is wrong; presumably you meant to use:

    if [ $( file -b $i ) == "directory" ]
    
  • If you had used the above, you would get bash: [: too many arguments, because word splitting would be done upon on the output of the variable expansion ($i), and then command substitution, $() (file command) and [ will see multiple words before =, leading to the error message. You need to quote the variable expansion, and command substitution:

    [ "$(file -b "$1")" == "directory" ]
    

As a side note, you should use the bash keyword [[, instead of [ as the former will handle word splitting (and pathname expansion) for you.

  • Thanks, the [[ tip works well. Also "$()" makes sense too. – Script Kitty Jan 15 '17 at 3:33
  • @ScriptKitty Note the "$i" as well. – heemayl Jan 15 '17 at 3:34
  • @ScriptKitty did $i have spaces in filename ? – Sergiy Kolodyazhnyy Jan 15 '17 at 4:01
  • @Serg Why not follow the usual and safer route rather than confirming? :) – heemayl Jan 15 '17 at 4:02
  • 1
    @heemayl by "safe and usual" route , I assume you mean quoting the variables - with which I absolutely agree , and that should be done in first place. I'm just curious what's user's specific case is :) – Sergiy Kolodyazhnyy Jan 15 '17 at 4:04
4
if [ $( file -b $i ) == "directory" ]

Two issues here:

  • Use single = for string comparison. See man test for proper syntax( note, that [ in many cases has shell-specific implementation, so see your shell's man page if you don't have documentation for test). If you absolutely need == , use [[ instead, which is feature of many bourne-like shells, including bash,ksh,zsh. NOTE: while == exists in bash since version 2.0, " = should be used with the test command for POSIX conformance." ( bash man page).

  • Quote all your variables as "$()" . Specifically of interest is $i. Filenames with space will break $i into multiple words due to shell's word expansion.

Example:

bash-4.3$ mkdir with\ space
bash-4.3$ i="./with space"
bash-4.3$ set -x
bash-4.3$ [ $( file -b $i ) == "directory" ] && echo "YES"
++ file -b ./with space
+ '[' cannot open '`./with'\''' '(No' such file or 'directory)' cannot open '`space'\''' '(No' such file or 'directory)' == directory ']'
bash: [: too many arguments

name=$( file -b $i )
if [ name == "directory" ]

Issues here:

  • name is not expanded to variable, it's just a string "name" here. You need "$name" and again, single =

Also, it cannot have possibly have worked , since exit status of test is returned as false ( exit status 1)

$ name=$(file -b /etc)
$ set -x
$ [ name == "directory" ]
+ '[' name '==' directory ']'
$ echo $?
+ echo 1
1

The above tested on bash and mksh shells.

  • Since [ / test is a bash built-in, it's better to use help test, not man test. – Patrick Jan 15 '17 at 3:32
  • @Patrick true, although it depends on the OS person uses. It might not have any documentation at all. See this post from a few days ago : unix.stackexchange.com/q/336521/85039 – Sergiy Kolodyazhnyy Jan 15 '17 at 3:34
  • Actually it doesn't depend on the OS. The builtin [ and test commands will always take precedence over the OS ones. – Patrick Jan 15 '17 at 3:35
  • @Patrick What I mean is presence of documentation for standard test command depends on the OS. – Sergiy Kolodyazhnyy Jan 15 '17 at 3:38
  • @Patrick by the way, this slightly overdue of a comment ( I'm not the fastest guy on U&L ) , but bash isn't the only shell that uses test, so I'm not sure if it's OK to make assumptions for what's OP is using. – Sergiy Kolodyazhnyy Jan 15 '17 at 5:06
4

There are lots of issues! Let’s take this part which is "working":

 name=$( file -b $i )
 if [ name == "directory" ]

This assigns the output of the file command to the variable called name, but doesn't use it; instead, it runs the [ command with 3 parameters: name, ==, and directory. Accepting == is a bash extension.

If this was corrected to use $name rather than name you would again get a too many arguments problem for many cases. This is because file returns multiple word results like ASCII text. So after the command has run you get

if [ ASCII text == directory ]

and now it is obvious that the command is missing some grouping.

if [ "$(file -b -- "$i")" = "directory" ]

is probably what you want: = rather than == for portability, and quoting the result of command substitution which you almost always want to do.

  • I actually like this answer too because it spelled out the unquoted part – Script Kitty Jan 15 '17 at 5:17
  • Of course, as the other answers have said, it is also necessary to put the $i in quotes: "$(file -b "$i")". This may appear to be an improper nesting of quotes; see Bash quotes unescaped on command substitution. – Scott Jan 15 '17 at 20:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.