views:

976

answers:

7

I'm trying to create a bash script that will extract the last parameter given from the command line into a variable to be used elsewhere. Here's the script I'm working on:

#!/bin/bash
# compact - archive and compact file/folder(s)

eval LAST=\$$#

FILES="$@"
NAME=$LAST

# Usage - display usage if no parameters are given
if [[ -z $NAME ]]; then
  echo "compact <file> <folder>... <compressed-name>.tar.gz"
  exit
fi

# Check if an archive name has been given
if [[ -f $NAME ]]; then
  echo "File exists or you forgot to enter a filename.  Exiting."
  exit
fi

tar -czvpf "$NAME".tar.gz $FILES

Since the first parameters could be of any number, I have to find a way to extract the last parameter, (e.g. compact file.a file.b file.d files-a-b-d.tar.gz). As it is now the archive name will be included in the files to compact. Is there a way to do this?

A: 
#!/bin/sh

eval last='$'$#
while test $# -gt 1; do
    list="$list $1"
    shift
done

echo $list $last

William Pursell
Thanks william, condenses the first parameters and extracts the last. Nice.
+1  A: 
#!/bin/bash

lastidx=$#
lastidx=`expr $lastidx - 1`

eval last='$'{$lastidx}
echo $last
jsight
ooh, this isn't exactly what you want... hmm...
jsight
Ok, fixed it. :)
jsight
Or shortened to eval "last=\$${#-1}"
nagul
+8  A: 

To remove the last item from the array you could use something like this:

#!/bin/bash

length=$(($#-1))
array=${@:1:$length}
echo $array
Krzysztof Klimonda
Bing! Very nice, just what I was looking for. Thankyou.
Would vote up but don't have the qualifications :)
A: 

I can't find a way to use array-subscript notation on $@, so this is the best I can do:

#!/bin/bash

args=("$@")
echo "${args[$(($#-1))]}"
Norman Ramsey
+2  A: 
last_arg="${!#}"
Ah this is a good way to extract the last parameter. Need still to extract the final paremeter from the array. Renamed post to explain better.
+1  A: 

Thanks guys, got it done, heres the final bash script:

#!/bin/bash
# compact - archive and compress file/folder(s)

# Extract archive filename for variable
ARCHIVENAME="${!#}"

# Remove archive filename for file/folder list to backup
length=$(($#-1))
FILES=${@:1:$length} 

# Usage - display usage if no parameters are given
if [[ -z $@ ]]; then
  echo "compact <file> <folder>... <compressed-name>.tar.gz"
  exit
fi

# Tar the files, name archive after last file/folder if no name given
if [[ ! -f $ARCHIVENAME ]]; then
  tar -czvpf "$ARCHIVENAME".tar.gz $FILES; else
  tar -czvpf "$ARCHIVENAME".tar.gz "$@"
fi
This has the usual problem of confusing spaces with separators between filenames; try it on files with spaces in the name, and it'll fail miserably. Use arrays: `FILES=("${@:1:$length}")`, and then `tar ... "${FILES[@]}"; else` should work much more reliably
Gordon Davisson
Hmm, not sure how this will recogznize a "\ ". I tried it, yeah, and am getting:line 9: {@:1:2}: command not found
Besides, how Krzysztof did this appears to work just fine.
+1  A: 

Several solutions have already been posted; however I would advise restructuring your script so that the archive name is the first parameter rather than the last. Then it's really simple, since you can use the shift builtin to remove the first parameter:

ARCHIVENAME="$1"
shift
# Now "$@" contains all of the arguments except for the first
Adam Rosenfield
I thought of this but basically I didn't want to do this because the typing syntax of 'compact file1 file4... archivename' just made better sense tome. Thanks though.