Why does my Bash script display the "too many arguments" error at the cp command?

6,111

It is not cp but [ aka test which gives you the error

Share:
6,111
Mike
Author by

Mike

Updated on September 18, 2022

Comments

  • Mike
    Mike over 1 year

    Here is my script, I get the error "line 33: [: too many arguments", I'm confused why, surely only 2 arguments are being provided to cp here?

    I am providing two directories to the script with no spaces in them, i.e. $1=dir1/ and $2=dir2/

    #!/bin/bash
    
    ### Assign suitable names to arguements. ###
    
    source=$1
    dest=$2
    
    ### Error handler for all script errors. ###
    
    function errorHandler {
        case $1 in
            ERRargs) printf "USAGE: e2backup source_dir dest_dir.\n"; exit 1;;
            ERRsource) printf "ERROR: Source does not exist or is not a directory.\n"; exit 2;;
            ERRdest) printf "ERROR: Destination does not exist or is not a directory.\n"; exit 3;;
            ERRempty) printf "ERROR: Destination is not empty.\n"; exit 4;;
        esac
    }
    
    ### Test num. of args, source/dest validity and empty dest. Then perform backup. ###
    
    if [ $# -ne 2 ]
      then
        errorHandler ERRargs
    elif [ ! -d $source ]
      then
        errorHandler ERRsource
    elif [ ! -d $dest ]
      then
        errorHandler ERRdest
    elif [ -n "$(ls -A $dest)" ] 
      then
        errorHandler ERRempty
    elif [ cp -R $source $dest ]
      then
        printf "Successfully backed-up from $source to $dest"; exit
    else
        printf "Back-up failed, please see e2backup.error"; exit 5
    fi
    
  • Mike
    Mike about 11 years
    And how can I mitigate this issue? I want to use the return status for the elif but carry out the cp at the same time...
  • Admin
    Admin about 11 years
    Let's say you want to keep this kind of bad coding (in my opinion) ... You need to have as output the return code: elif $(cp -R "$source" "$destination"; printf "%d" "$?"); then
  • garyjohn
    garyjohn about 11 years
    elif tests the return status of the following command. Usually the command it [, an alias for test. If you want to branch on the return status of cp, simply write elif cp -R $source $dest.
  • Admin
    Admin about 11 years
    +1 Gary, his version is correct too. I thought it my way, where I always separate commands and exit codes from tests.
  • Mike
    Mike about 11 years
    Nice one chaps. And why is this bad coding in your opinion? Any tips would be appreciated.
  • Admin
    Admin about 11 years
    Well, it's better to run commands separately from tests, so debugging is easier. You get to know exactly, like in your example, it's not a cp issue, but an if statement issue (the same goes for ls -A $dest you have there); also, you can have both the exit code and the output, so you can print the output as well for future debugging. Also, it's good to have any string parameter surrounded by double quotes, so if you have a space in it, bash won't think you have multiple params instead of just one for that string.
  • Admin
    Admin about 11 years
    Also, even though I like you're using printf and not echo, which is not POSIX, you are not using it the best way possible. If you have in your $source and $dest some special characters like %, printf will try to expand it because the first parameter for printf will expand formatting items like %d, %s, %.2f. So the best way to do it is to have all the variables as separate params of the print. In your example: printf "Successfully backed-up from '%s' to '%s'." "$source" "$dest" #simple quotes are just for aesthetics, so it's easier to know where the value ended
  • Mike
    Mike about 11 years
    OK sweet, cheers Radoo.
  • Admin
    Admin about 11 years
    Also, not exactly is a bad thing having then on a new line, but, it doesn't give you any advantage, just newlines in your code. You can just write if ... ; then
  • Nick Parsons
    Nick Parsons about 11 years
    Your recall is wrong. [] is equivalent with test. [[]] is a new compound introduced in bash since some version I don't recall, which extends test functionality with pattern matching. Even tough it's a very useful feature, this compound is not POSIX, which, in some cases, might not be desirable, like when you want your script to be distributed across diferent shells.
  • Huygens
    Huygens about 11 years
    At Radoo, thanks I will correct the answer. However, as this is a bash scrip (first line is #!/bin/bash) the '[[]]' will work on any platform which supports Bash. Even when trying to make a script as much agnostic to a specific shell, I never managed to have it portable without modification from let says BASH to KSH.
  • chepner
    chepner about 11 years
    There's no reason to capture the standard output of cp, since it generally doesn't produce any. Just us elif cp -R "$source" "$dest" to test the exit status of cp.
  • Huygens
    Huygens about 11 years
    @chepner I am not sure what I ate that day, must have been pretty bad ;-) I corrected it once more and even made this post a community wiki as my original answer is quite far from what the current is thanks to both Radoo and you!