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?

8
  • 1
    Right from the start your square brackets are not balanced in the first case. (one too many ]) Commented Jan 15, 2017 at 3:18
  • 1
    @JuliePelletier Oops sorry that was a typo from the stackoverflow side. I will edit my question. I confirmed there is not an extra ] in my code and it still gives "too many arguments" on that line Commented Jan 15, 2017 at 3:22
  • 1
    On top of the two good answers you got, I'd also like to add that the only case that I was able to reproduce your error is when the file referred by $i doesn't exist, which denotes another thing you should check. Commented Jan 15, 2017 at 3:28
  • 1
    look for a tool called shellcheck to test your own scripts, it's s great learning tool for basic mistakes like this Commented Jan 15, 2017 at 9:12
  • 1
    Background reading: Why does my shell script choke on whitespace or other special characters?, What is the difference between [[ $a == z* ]] and [ $a == z* ]? Commented Jan 15, 2017 at 21:55

3 Answers 3

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.

6
  • Thanks, the [[ tip works well. Also "$()" makes sense too. Commented Jan 15, 2017 at 3:33
  • @ScriptKitty Note the "$i" as well. Commented Jan 15, 2017 at 3:34
  • @ScriptKitty did $i have spaces in filename ? Commented Jan 15, 2017 at 4:01
  • @Serg Why not follow the usual and safer route rather than confirming? :) Commented Jan 15, 2017 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 :) Commented Jan 15, 2017 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.

7
  • Since [ / test is a bash built-in, it's better to use help test, not man test. Commented Jan 15, 2017 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 Commented Jan 15, 2017 at 3:34
  • Actually it doesn't depend on the OS. The builtin [ and test commands will always take precedence over the OS ones. Commented Jan 15, 2017 at 3:35
  • @Patrick What I mean is presence of documentation for standard test command depends on the OS. Commented Jan 15, 2017 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. Commented Jan 15, 2017 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.

2
  • I actually like this answer too because it spelled out the unquoted part Commented Jan 15, 2017 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. Commented Jan 15, 2017 at 20:43

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.