在“$ @”中的最后一个参数之前提取参数

我正在尝试创build一个Bash脚本,将从命令行中提取的最后一个参数提取到其他地方使用的variables中。 这是我正在处理的脚本:

#!/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 

由于第一个参数可以是任何数字,我必须find一种方法来提取最后一个参数(例如,compact file.a file.b file.d files-abd.tar.gz)。 由于现在档案名称将被包含在文件中以进行压缩。 有没有办法做到这一点?

要从数组中删除最后一项,你可以使用这样的东西:

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

已经发布了几个解决scheme; 但是我会build议重构您的脚本,以便存档名称是第一个参数,而不是最后一个。 那么这很简单,因为你可以使用内build的shift来删除第一个参数:

 ARCHIVENAME="$1" shift # Now "$@" contains all of the arguments except for the first 

这是我的脚本

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

我喜欢因为是一个非常紧凑的解决scheme

谢谢你们,把它完成,inheritance人最后的bash脚本:

 #!/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 

只需要删除Krzysztof Klimonda解决scheme中使用的lengthvariables:

 ( set -- 1 2 3 4 5 echo "${@:1:($#-1)}" # 1 2 3 4 echo "${@:(-$#):($#-1)}" # 1 2 3 4 ) 
 #!/bin/bash lastidx=$# lastidx=`expr $lastidx - 1` eval last='$'{$lastidx} echo $last 

尝试:

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

我会join这个评论,但没有足够的声誉,答案有点长。 希望它不介意。

正如@func所述:

说明:last_arg = “$ {#!}”

怎么运行的:

$ {!PARAM}表示间接级别。 您没有引用PARAM本身,而是存储在PARAM中的值(将PARAM视为指向值的指针)。
$ {#}扩展为参数的数量(注意: $ 0 – 脚本名称 – 在这里不计算在内)。

考虑下面的执行:

 $./myscript.sh p1 p2 p3 

并在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 

因此,您可以将$ {!#}想象为上述eval用法的快捷方式,这正好是上述方法 – 评估存储在给定参数中的值,此处参数为3,并保存位置参数$ 3

现在如果你想要除了最后一个之外的所有参数,你可以使用子串删除$ {PARAM%PATTERN} ,其中符号的意思是“从string的末尾删除最短匹配模式”

因此在我们的脚本中:

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

你可以在这里读到一些东西: 参数扩展

 #!/ bin / sh的

 eval last ='$'$#
同时testing$#-gt 1; 做
     list =“$ list $ 1”
    转移
 DONE

 echo $ list $ last

我找不到在$@上使用数组下标符号的方法,所以这是我可以做的最好的:

 #!/bin/bash args=("$@") echo "${args[$(($#-1))]}" 

你确定这个奇特的脚本比tar的简单别名更好吗?

 alias compact="tar -czvpf" 

用法是:

 compact ARCHIVENAME FILES... 

其中FILES可以是file1 file2或像*.html这样的*.html

这个脚本可能适用于你 – 它返回参数的子范围,并可以从另一个脚本调用。

它运行的例子:

 $ args_get_range 2 -2 yab "c 1" defg '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} ... eg # 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