Extraer parámetros antes del último parámetro en "$@"


Estoy tratando de crear un script Bash que extraerá el último parámetro dado desde la línea de comandos en una variable para ser utilizada en otro lugar. Aquí está el guión en el que estoy trabajando:

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

Dado que los primeros parámetros pueden ser de cualquier número, tengo que encontrar una manera de extraer el último parámetro, (por ejemplo, compact file.archivo.archivo b.d files-a-b-d.tar.gz). Como es ahora el nombre del archivo se incluirá en los archivos para compactar. ¿Hay alguna manera de hacer esto?

Author: Tshepang, 2009-08-01

14 answers

Para eliminar el último elemento del array se puede usar algo como esto:

#!/bin/bash

length=$(($#-1))
array=${@:1:$length}
echo $array
 85
Author: Krzysztof Klimonda,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-01 01:33:38
last_arg="${!#}" 
 18
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-01 09:28:03

Ya se han publicado varias soluciones; sin embargo, aconsejaría reestructurar su script para que el nombre del archivo sea el primer parámetro en lugar del último. Entonces es muy simple, ya que se puede utilizar el shift builtin para eliminar el primer parámetro:

ARCHIVENAME="$1"
shift
# Now "$@" contains all of the arguments except for the first
 11
Author: Adam Rosenfield,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-01 21:55:14

Soluciones portátiles y compactas

Así es como lo hago en mis scripts

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

Me gusta porque es una solución muy compacta.

 9
Author: ePi272314,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-13 14:52:36

Gracias chicos, lo tengo hecho, aquí está el guión final de 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
 4
Author: user148813,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-01 21:36:31

Simplemente eliminando la variable length utilizada en la solución de Krzysztof Klimonda:

(
set -- 1 2 3 4 5
echo "${@:1:($#-1)}"       # 1 2 3 4
echo "${@:(-$#):($#-1)}"   # 1 2 3 4
)
 3
Author: bashist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-06 17:47:27

Agregaría esto como un comentario, pero no tengo suficiente reputación y la respuesta se hizo un poco más larga de todos modos. Espero que no le importe.

Como @ func declaró:

Last_arg=" {{!#}"

Cómo funciona:

${!PARAM} indica el nivel de indirección. No estás haciendo referencia a PARAMen sí, sino al valor almacenado en PARAM (piensa en PARAM como puntero al valor ).
${#} se expande al número de parámetros (Nota: $0 - el nombre del script - no se cuenta aquí).

Considere la siguiente ejecución:

$./myscript.sh p1 p2 p3

Y en el 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

Por lo tanto usted puede pensar en {{!#} como un atajo para el uso de eval anterior, que hace exactamente el enfoque descrito anteriormente-evalúa el valor almacenado en el parámetro dado, aquí el parámetro es 3 y sostiene el argumento posicional$3

Ahora si quieres todos los parámetros excepto el último, puede usar la eliminación de subcadenas {{PARAM % PATTERN} donde % signo significa 'eliminar el patrón coincidente más corto del final de la cadena'.

Por lo tanto en nuestro guión:

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


Puedes leer algo aquí: Expansión de parámetros

 3
Author: CermakM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-25 08:46:13
#!/bin/bash

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

eval last='$'{$lastidx}
echo $last
 1
Author: jsight,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-01 01:23:56

¿Está seguro de que este script sofisticado es mejor que un simple alias para alquitranar?

alias compact="tar -czvpf"

El uso es:

compact ARCHIVENAME FILES...

Donde los ARCHIVOS pueden ser file1 file2 o globs como *.html

 1
Author: MestreLion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-08-06 11:42:46

Intenta:

if [ "$#" -gt '0' ]; then
    /bin/echo "${!#}" "${@:1:$(($# - 1))}
fi
 1
Author: zorro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-16 18:10:10
#!/bin/sh

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

echo $list $last

 0
Author: William Pursell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-01 01:20:19

No puedo encontrar una manera de usar la notación de subíndice de matriz en $@, así que esto es lo mejor que puedo hacer:

#!/bin/bash

args=("$@")
echo "${args[$(($#-1))]}"
 0
Author: Norman Ramsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-08-01 01:41:26

Este script puede funcionar para usted - devuelve un subrango de los argumentos, y puede ser llamado desde otro script.

Ejemplos de ejecución:

$ 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
 0
Author: Brad Parks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-13 20:04:57

Array sin último parámetro:

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

Pero es un bashism : (. Las soluciones adecuadas implicarían el cambio y la adición en variable como otros utilizan.

 0
Author: pevik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-24 05:04:56