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

时间:2009-08-01 01:09:52

标签: bash parameters

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

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

15 个答案:

答案 0 :(得分:88)

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

#!/bin/bash

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

更短的方式:

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

但是arays是Bashism,请尽量避免使用它们:(。

答案 1 :(得分:19)

last_arg="${!#}" 

答案 2 :(得分:11)

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

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

答案 3 :(得分:10)

便携式和紧凑型解决方案

这就是我在脚本中的行为

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

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

答案 4 :(得分: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

答案 5 :(得分:3)

删除Krzysztof Klimonda解决方案中使用的length变量:

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

答案 6 :(得分:3)

我会将此作为评论添加,但没有足够的声誉,无论如何答案都要长一点。希望它不介意。

正如@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} ,其中符号表示'删除字符串'末尾的最短匹配模式。

因此在我们的剧本中:

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


您可以在此处阅读:Parameter expansion

答案 7 :(得分:1)

#!/bin/bash

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

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

答案 8 :(得分:1)

尝试:

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

答案 9 :(得分:1)

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

alias compact="tar -czvpf"

用法是:

compact ARCHIVENAME FILES...

FILES可以是file1 file2,也可以是*.html

答案 10 :(得分:1)

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

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

但它是bashism :(。正确的解决方案将涉及转移并添加到其他人使用的变量。

答案 11 :(得分:1)

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

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

答案 12 :(得分:0)

#!/bin/sh

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

echo $list $last

答案 13 :(得分:0)

我找不到在$@上使用array-subscript表示法的方法,所以这是我能做的最好的事情:

#!/bin/bash

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

答案 14 :(得分:0)

此脚本可能适合您 - 它返回参数的子范围,可以从另一个脚本调用。

它运行的例子:

$ 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