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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1215538/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 18:22:10  来源:igfitidea点击:

Extract parameters before last parameter in "$@"

bashparameters

提问by user148813

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:

我正在尝试创建一个 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

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?

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

回答by Krzysztof Klimonda

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

要从数组中删除最后一项,您可以使用以下内容:

#!/bin/bash

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

Even shorter way:

更短的方法:

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

But arays are a Bashism, try avoid using them :(.

但是,一阳指是Bashism,尽量避免使用它们:(。

回答by user148813

last_arg="${!#}" 

回答by ePi272314

Portable and compact solutions

便携紧凑的解决方案

This is how I do in my scripts

这就是我在我的脚本中所做的

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

EDIT
According to some comments (see below), this solution is more portable than others.
Please read Michael Dimmitt's commentary for an explanation of how it works.

编辑
根据一些评论(见下文),这个解决方案比其他解决方案更便携。
请阅读 Michael Dimmitt 的评论以了解其工作原理。

回答by Adam Rosenfield

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

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

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

回答by user148813

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

谢谢大家,搞定了,这是最终的 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

回答by bashist

Just dropping the lengthvariable used in Krzysztof Klimonda's solution:

只需删除lengthKrzysztof Klimonda 解决方案中使用的变量:

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

回答by CermakM

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:

正如@func 所说:

last_arg="${!#}"

last_arg="${!#}"

How it works:

这个怎么运作:

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

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

Consider following execution:

考虑以下执行:

$./myscript.sh p1 p2 p3

And in the myscript.sh

在 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 3and holds the positional argument $3

因此,您可以将${!#}视为上述 eval 用法的快捷方式,它完全符合上述方法 - 评估存储在给定参数中的值,此处参数为3并保存位置参数$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'.

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

Hence in our script:

因此在我们的脚本中:

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



You can read something in here: Parameter expansion

您可以在这里阅读一些内容:参数扩展

回答by pevik

Array without last parameter:

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

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

But it's a bashism:(. Proper solutions would involve shift and adding into variable as others use.

但这是一种bashism:(。适当的解决方案将涉及转移和添加到其他人使用的变量中。

回答by Anders

Alternative way to pull the last parameter out of the argument list:

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

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

回答by jsight

#!/bin/bash

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

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