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?

Rui F Ribeiro
  • 55,929
  • 26
  • 146
  • 227
Script Kitty
  • 183
  • 6
  • 1
    Right from the start your square brackets are not balanced in the first case. (one too many `]`) – Julie Pelletier Jan 15 '17 at 03: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 – Script Kitty Jan 15 '17 at 03: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. – Julie Pelletier Jan 15 '17 at 03:28
  • @JuliePelletier $i is in a `for i in ...` loop and echoes alright in scope. You're right that $i has been the problem. Thanks for helping yall – Script Kitty Jan 15 '17 at 03:37
  • 1
    look for a tool called **shellcheck** to test your own scripts, it's s great learning tool for basic mistakes like this – Patrick Trentin Jan 15 '17 at 09:12
  • @muru you just changed the question (to fix the code), but now it saw this perfectly good code gives an error (that it does not). – ctrl-alt-delor Jan 15 '17 at 14:27
  • @richard OP said in a comment that was a typo and that the extra ] is not in the actual code – muru Jan 15 '17 at 14:28
  • 1
    Background reading: [Why does my shell script choke on whitespace or other special characters?](http://unix.stackexchange.com/questions/131766/why-does-my-shell-script-choke-on-whitespace-or-other-special-characters), [What is the difference between `[[ $a == z* ]]` and `[ $a == z* ]`?](http://unix.stackexchange.com/questions/56655/what-is-the-difference-between-a-z-and-a-z) – Gilles 'SO- stop being evil' Jan 15 '17 at 21:55

3 Answers3

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.

heemayl
  • 54,820
  • 8
  • 124
  • 141
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.

Sergiy Kolodyazhnyy
  • 16,187
  • 11
  • 53
  • 104
  • Since `[` / `test` is a bash built-in, it's better to use `help test`, not `man test`. – phemmer Jan 15 '17 at 03: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 : http://unix.stackexchange.com/q/336521/85039 – Sergiy Kolodyazhnyy Jan 15 '17 at 03:34
  • Actually it doesn't depend on the OS. The builtin `[` and `test` commands will always take precedence over the OS ones. – phemmer Jan 15 '17 at 03:35
  • @Patrick What I mean is presence of documentation for standard `test` command depends on the OS. – Sergiy Kolodyazhnyy Jan 15 '17 at 03: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 05:06
  • @Serg lol I'm now on mac, but want to use the script on ubuntu. TIL what test is – Script Kitty Jan 15 '17 at 05:15
  • @ScriptKitty Glad I could assist in learning :D If you just write the script in `bash` it should work. Are you going to be using any special path names at all ? I know Macs don't have `/proc` directory. There's `/Volumes` also, which exists on Mac but not on Linux. Not sure about other pitfalls,though, but in general if you're not using anything mac-specific, it should work. – Sergiy Kolodyazhnyy Jan 15 '17 at 05:21
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.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501
icarus
  • 17,420
  • 1
  • 37
  • 54
  • I actually like this answer too because it spelled out the unquoted part – Script Kitty Jan 15 '17 at 05: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](//superuser.com/q/909764/150988). – Scott - Слава Україні Jan 15 '17 at 20:43