web-dev-qa-db-fra.com

Extraire les paramètres avant le dernier paramètre dans "$ @"

J'essaie de créer un script Bash qui extraira le dernier paramètre donné à partir de la ligne de commande dans une variable à utiliser ailleurs. Voici le script sur lequel je travaille:

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

Comme les premiers paramètres peuvent être n’importe quel nombre, je dois trouver un moyen d’extraire le dernier paramètre (par exemple, compact fichier.a fichier.b fichier.d fichiers-a-b-d.tar.gz). Comme il est maintenant, le nom de l'archive sera inclus dans les fichiers à compacter. Y a-t-il un moyen de faire cela?

55
user148813

Pour supprimer le dernier élément du tableau, vous pouvez utiliser quelque chose comme ceci:

#!/bin/bash

length=$(($#-1))
array=${@:1:$length}
echo $array
87
Krzysztof Klimonda
last_arg="${!#}" 
19
func

Plusieurs solutions ont déjà été postées. Cependant, je vous conseillerais de restructurer votre script afin que le nom de l'archive soit le paramètre first plutôt que le dernier. Ensuite, c’est très simple, puisque vous pouvez utiliser le shift intégré pour supprimer le premier paramètre:

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

Solutions portables et compactes

C'est comme ça que je fais dans mes scripts

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

MODIFIER
Selon certains commentaires (voir ci-dessous), cette solution est plus portable que d'autres.
Veuillez lire le commentaire de Michael Dimmitt pour une explication de son fonctionnement.

10
ePi272314

Merci les gars, l'avoir fait, voici le dernier 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
5
user148813

J'ajouterais cela à titre de commentaire, mais je n'ai pas assez de réputation et la réponse a été un peu plus longue de toute façon. J'espère que ça ne te dérange pas.

Comme @func a déclaré:

last_arg = "$ {! #}"

Comment ça marche: 

$ {! PARAM} indique le niveau d'indirection. Vous ne faites pas référence à PARAM, mais à la valeur stockée dans PARAM (pensez à PARAM en tant que pointeur sur value). 
$ {#} augmente le nombre de paramètres (Remarque: $ 0 - le nom du script - n’est pas compté ici). 

Considérez l'exécution suivante:

$./myscript.sh p1 p2 p3

Et dans le 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

Vous pouvez donc considérer $ {! #} Comme un raccourci pour l’utilisation de l’évaluation ci-dessus, qui applique exactement l’approche décrite ci-dessus - évalue la valeur stockée dans le paramètre donné, le paramètre étant 3 et contient l'argument de position $ 3

Maintenant, si vous voulez tous les paramètres sauf le dernier, vous pouvez utiliser la suppression de sous-chaîne $ {PARAM% PATTERN}% signe signifie 'supprimez le motif correspondant le plus court à la fin de la chaîne '

D'où dans notre script:

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


Vous pouvez lire quelque chose ici: Développement de paramètres

3
CermakM

Supprimez simplement la variable length utilisée dans la solution de Krzysztof Klimonda:

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

Êtes-vous sûr que ce script sophistiqué est meilleur qu'un simple alias pour tar?

alias compact="tar -czvpf"

L'utilisation est:

compact ARCHIVENAME FILES...

Où les fichiers peuvent être file1 file2 ou des globes comme *.html

1
MestreLion

Autre moyen d'extraire le dernier paramètre de la liste des arguments:

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

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

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

Essayer:

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

Ce script peut fonctionner pour vous - il renvoie une sous-gamme d'arguments et peut être appelé à partir d'un autre script.

Exemples de fonctionnement:

$ 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
Brad Parks

Tableau sans dernier paramètre:

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

Mais c’est un bashism :(. Des solutions correctes impliqueraient un décalage et l’ajout de variables comme le font d’autres 

0
pevik
 #!/bin/sh 

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

 echo $ list $ last 

0
William Pursell

Je n'arrive pas à trouver un moyen d'utiliser la notation tableau-indice sur $@, c'est donc le mieux que je puisse faire:

#!/bin/bash

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