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

use*_*813 62 parameters bash

我正在尝试创建一个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
Run Code Online (Sandbox Code Playgroud)

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

小智 93

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

#!/bin/bash

length=$(($#-1))
array=${@:1:$length}
echo $array
Run Code Online (Sandbox Code Playgroud)

  • 很酷,你甚至可以一次完成所有工作:`echo"$ {@:1:$(($# - 1))}"`这是一些非常难的核心脚本. (18认同)
  • 更短:`array = $ {@:1:$# - 1}` (12认同)
  • 在 `bash` 中 `"${@:1:$#-1}"` 有效,但对于 `zsh` 来说,`#` 和 `-` 之间需要一个空格:`"${@:1:$# - 1}“`. (2认同)

小智 19

last_arg="${!#}" 
Run Code Online (Sandbox Code Playgroud)

  • @LukeDavis我认为基本上将`!#`扩展为相当于`$#`,因为:"如果参数的第一个字符是感叹号(!),并且参数不是nameref,它引入了一个变量间接的级别".https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html (2认同)

ePi*_*314 14

便携紧凑的解决方案

这就是我在脚本中的做法

last=${@:$#} # last parameter 
other=${*%${!#}} # all parameters except the last
Run Code Online (Sandbox Code Playgroud)

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

  • "other"具有意外的副作用,即删除与最后一个元素匹配的所有参数,而不仅仅是最后一个元素.使用匹配via(%)的方法并不严格正确.`例子:(item1 item2 item2)=>(item1)` (2认同)
  • TLDR,https://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html。变扩为赢!最后一条命令使用$ @,它是所有参数的数组。$#这是参数的数量。然后冒号本身在变量扩展中就意味着偏移。总体而言,该命令意味着。将args $ @数组偏移args $#的数量,并由于变量扩展而将数组转换为字符串。它是最可移植的,因为在GNU Coreutils中指定了环境变量扩展。 (2认同)

Ada*_*eld 11

已经发布了几种解决方案; 但是我会建议重构你的脚本,以便存档名称是第一个参数而不是最后一个.然后它非常简单,因为您可以使用shift内置函数删除第一个参数:

ARCHIVENAME="$1"
shift
# Now "$@" contains all of the arguments except for the first
Run Code Online (Sandbox Code Playgroud)

  • 我想到了这一点,但基本上我不想这样做,因为“compact file1 file4... archivename”的打字语法更有意义。不过谢谢。 (2认同)

use*_*813 5

谢谢你们,完成它,继承人最后的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
Run Code Online (Sandbox Code Playgroud)

  • 这有一个常见的问题,即在文件名之间混淆空格和分隔符; 尝试在名称中带有空格的文件上,它会失败地失败.使用数组:`FILES =("$ {@:1:$ length}")`,然后`tar ..."$ {FILES [@]}"; 否则`应该更可靠地工作 (4认同)