Extract parameters before last parameter in “$@”

匿名 (未验证) 提交于 2019-12-03 02:20:02

问题:

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 ... .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?

回答1:

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

#!/bin/bash  length=$(($#-1)) array=${@:1:$length} echo $array 


回答2:

last_arg="${!#}"  


回答3:

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 


回答4:

This is how I do in my scripts

last=${@:$#} # last parameter  other=${*%${!#}} # all parameters except the last 

I like because is a very compact solution



回答5:

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 ... .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 


回答6:

Just dropping the length variable used in Krzysztof Klimonda's solution:

( set -- 1 2 3 4 5 echo "${@:1:($#-1)}"       # 1 2 3 4 echo "${@:(-$#):($#-1)}"   # 1 2 3 4 ) 


回答7:

I would add this as a comment, but don't have enough reputation and the answer got a bit longer anyway. Hope it doesn't mind.

As @func stated:

last_arg="${!#}"

How it works:

${!PARAM} indicates level of indirection. You are not referencing PARAM itself, but the value stored in PARAM ( think of PARAM as pointer to value ).
${#} expands to the number of parameters (Note: $0 - the script name - is not counted here).

Consider following execution:

$./myscript.sh p1 p2 p3 

And in the myscript.sh

#!/bin/bash  echo "Number of params: ${#}"  # 3 echo "Last parameter using '\${!#}': ${!#}"  # p3 echo "Last parameter by evaluating positional value: $(eval LASTP='$'${#} ; echo $LASTP)"  # p3 

Hence you can think of ${!#} as a shortcut for the above eval usage, which does exactly the approach described above - evaluates the value stored in the given parameter, here the parameter is 3 and holds the positional argument $3

Now if you want all the params except the last one, you can use substring removal ${PARAM%PATTERN} where % sign means 'remove the shortest matching pattern from the end of the string'.

Hence in our script:

echo "Every parameter except the last one: ${*%${!#}}" 


You can read something in here: Parameter expansion



回答8:

#!/bin/bash  lastidx=$# lastidx=`expr $lastidx - 1`  eval last='$'{$lastidx} echo $last 


回答9:

Try:

if [ "$#" -gt '0' ]; then     /bin/echo "${!#}" "${@:1:$(($# - 1))} fi 


回答10:

 #!/bin/sh  eval last='$'$# while test $# -gt 1; do     list="$list $1"     shift done  echo $list $last  


回答11:

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))]}" 


回答12:

Are you sure this fancy script is any better than a simple alias to tar?

alias compact="tar -czvpf" 

Usage is:

compact ARCHIVENAME FILES... 

Where FILES can be file1 file2 or globs like *.html



回答13:

This script may work for you - it returns a subrange of the arguments, and can be called from another script.

Examples of it running:

$ args_get_range 2 -2 y a b "c 1" d e f g                           'b' 'c 1' 'd' 'e'  $ args_get_range 1 2 n arg1 arg2                                    arg1 arg2  $ args_get_range 2 -2 y arg1 arg2 arg3 "arg 4" arg5                 'arg2' 'arg3'  $ args_get_range 2 -1 y arg1 arg2 arg3 "arg 4" arg5                 'arg2' 'arg3' 'arg 4'  # You could use this in another script of course  # by calling it like so, which puts all # args except the last one into a new variable # called NEW_ARGS  NEW_ARGS=$(args_get_range 1 -1 y "$@") 

args_get_range.sh

#!/usr/bin/env bash  function show_help() {   IT="   Extracts a range of arguments from passed in args   and returns them quoted or not quoted.    usage: START END QUOTED ARG1 {ARG2} ...    e.g.     # extract args 2-3    $ args_get_range.sh 2 3 n arg1 arg2 arg3   arg2 arg3    # extract all args from 2 to one before the last argument    $ args_get_range.sh 2 -1 n arg1 arg2 arg3 arg4 arg5   arg2 arg3 arg4    # extract all args from 2 to 3, quoting them in the response   $ args_get_range.sh 2 3 y arg1 arg2 arg3 arg4 arg5   'arg2' 'arg3'    # You could use this in another script of course    # by calling it like so, which puts all   # args except the last one into a new variable   # called NEW_ARGS    NEW_ARGS=\$(args_get_range.sh 1 -1 \"\$@\")    "   echo "$IT"   exit }  if [ "$1" == "help" ] then   show_help fi if [ $# -lt 3 ] then   show_help fi  START=$1 END=$2 QUOTED=$3 shift; shift; shift;  if [ $# -eq 0 ] then   echo "Please supply a folder name"   exit; fi  # If end is a negative, it means relative # to the last argument. if [ $END -lt 0 ] then   END=$(($#+$END)) fi  ARGS=""  COUNT=$(($START-1)) for i in "${@:$START}" do   COUNT=$((COUNT+1))    if [ "$QUOTED" == "y" ]   then     ARGS="$ARGS '$i'"   else     ARGS="$ARGS $i"   fi    if [ $COUNT -eq $END ]   then     echo $ARGS     exit;   fi done echo $ARGS 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!