skip to Main Content

I have a shell function to help me scp files to and from servers. Both servers are running bash on Ubuntu 22.04. Let’s call the server comp2 and pretend its IP address for is 192.168.1.2

On my server in the /home/.bashrc file I have this as my alias:

tocomp2() {
scp "$1" [email protected]:"$2"
}
fromcomp2() {
scp [email protected]:"$1" "$2"
}

Let’s pretend I have three files foo1 foo2 and foo3 in my working directory, and three files bar bar2 bar3 in the home directory of comp2. Odd things happen when I use the wildcard to move these files.

tocomp2 foo* /home/ only transfers foo1

tocomp2 foo* /home/ doesn’t find any files.

fromcomp2 bar* ./ or fromcomp2 bar* ./ transfers all three bar files

EDIT: Better question to ask is, how can I make tocomp2 act like fromcomp2 where the wildcard actually selects all foo files?

2

Answers


  1. Actually tocomp2 foo* /home/ expands to tocomp2 foo1 foo2 foo3 /home/ which copies foo1 but renames it to foo2, and ignores the remaining arguments.

    Writing functions where the first argument is special is simpler; with

    tocomp3(){
        local dest=$1
        shift
        scp "$@" "[email protected]:$dest"
    }
    

    you could get what you want with tocomp3 . foo* where . is the destination directory at the target.

    Login or Signup to reply.
  2. foo* expands to as many arguments as there are matching files (or the one literal argument foo* if no matches are found). If you have the files foo1 foo2 and foo3 in your directory then it expands to three arguments so tocomp2 foo* /home/ has four arguments in total and you are only referencing the first two in your code.

    foo* passes the literal argument foo*, and the quotes in "$1" disable the pathname expansion so it remains a literal argument.

    A common fix is to put the known arguments in front and the variable arguments at the end of the call, then you can use "$@" to reference them all without knowing the exact count.

    tocomp2() {
      dest="$1" ; shift # capture the known argument and then discard from the arg list
      scp "$@" [email protected]:"$dest"
    }
    tocomp2 /home/ foo*
    

    If you don’t like the reversed syntax then you can pull the last argument off the list and build a new array with everything except it:

    tocomp2() {
      dest="${@: -1}" # destination is the last argument
      files=( "${@:1:$(( $# - 1 ))}" )
      scp "${files[@]}" [email protected]:"$dest"
    }
    tocomp2 foo* /home/
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search