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

我正在尝试创建一个 Bash 脚本,它将从命令行中提取最后一个参数到一个变量中,以便在其他地方使用。这是我正在写的剧本:

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

因为第一个参数可以是任意数字,所以我必须找到一种方法来提取最后一个参数(例如,压缩 file.a file.b file.d files-a-b-d.tar.gz)。现在,归档名称将包含在要压缩的文件中。有办法吗?

35257 次浏览
#!/bin/sh


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


echo $list $last


#!/bin/bash


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


eval last='$'{$lastidx}
echo $last

要从数组中删除最后一项,可以使用如下方法:

#!/bin/bash


length=$(($#-1))
array=${@:1:$length}
echo $array

更简单的方法:

array=${@:1:$#-1}

但是数组是 巴什主义,尽量避免使用它们: (。

我找不到在 $@上使用数组下标符号的方法,所以我只能这样做:

#!/bin/bash


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

谢谢大家,搞定了,这是最后一个 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

已经发布了几个解决方案; 但是我建议重新调整脚本,使归档名称为 第一参数,而不是最后一个。然后就非常简单了,因为您可以使用 内置 shift来删除第一个参数:

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

试试:

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

您确定这个花哨的脚本比一个简单的用于 tar 的别名好吗?

alias compact="tar -czvpf"

用法是:

compact ARCHIVENAME FILES...

其中文件可以是 file1 file2或类似于 *.html的球体

只是去掉了克里蒙达解决方案中使用的 length变量:

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

便携式和紧凑型解决方案

我在剧本里就是这么写的

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

剪辑
根据一些评论(见下文) ,这个解决方案比其他方案更具可移植性。
请阅读迈克尔 · 迪米特的评论,了解它是如何工作的。

这个脚本可能对你有用——它返回参数的一个子区域,并且可以从另一个脚本调用。

例如:

$ 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

我想添加这个作为一个评论,但没有足够的声誉和答案得到了一点长反正。希望它不介意。

正如@func 所说:

Last _ arg = “ ${ ! # }”

工作原理:

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

考虑执行以下操作:

$./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 },其中 %符号表示 “删除字符串末尾的最短匹配模式”

因此,在我们的剧本中:

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


你可以在这里读到: 参数展开

没有最后一个参数的数组:

array=${@:1:$#-1}

但它是一个 抨击: (。正确的解决方案将涉及移位和增加到变量,因为其他人使用。

从参数列表中拉出最后一个参数的替代方法:

eval last="\$$#"
eval set -- `awk 'BEGIN{for(i=1;i<'$#';i++) printf " \"$%d\"",i;}'`

这对我很有效,使用 sh 和 bash:

last=${*##* }
others=${*%${*##* }}