web-dev-qa-db-fra.com

Quel est le but d'utiliser shift dans les scripts Shell?

Je suis tombé sur ce script:

#! /bin/bash                                                                                                                                                                                           

if (( $# < 3 )); then
  echo "$0 old_string new_string file [file...]"
  exit 0
else
  ostr="$1"; shift
  nstr="$1"; shift  
fi

echo "Replacing \"$ostr\" with \"$nstr\""
for file in $@; do
  if [ -f $file ]; then
    echo "Working with: $file"
    eval "sed 's/"$ostr"/"$nstr"/g' $file" > $file.tmp 
    mv $file.tmp $file
  fi  
done

Quelle est la signification des lignes où ils utilisent shift? Je suppose que le script doit être utilisé avec au moins des arguments alors ...?

134
Patryk

shift est un bash intégré qui sorte de supprime les arguments du début de la liste des arguments. Étant donné que les 3 arguments fournis au script sont disponibles dans $1, $2, $3, puis un appel à shift fera $2 le nouveau $1. UNE shift 2 changera de deux en créant de nouveaux $1 l'ancien $3. Pour plus d'informations, voir ici:

159
humanityANDpeace

Comme le décrivent les commentaires de Goldilocks et les références de l'humanité, shift réattribue les paramètres de position ($1, $2, Etc.) afin que $1 Prenne l'ancienne valeur de $2, $2 prend la valeur de $3, etc.*L'ancienne valeur de $1 Est supprimée. ($0 N'est pas modifié.) Voici quelques raisons pour cela:

  • Il vous permet d'accéder plus facilement au dixième argument (s'il y en a un). $10 Ne fonctionne pas - il est interprété comme $1 Concaténé avec un 0 (Et peut donc produire quelque chose comme Hello0). Après un shift, le dixième argument devient $9. (Cependant, dans la plupart des shells modernes, vous pouvez utiliser ${10}.)
  • Comme le montre le Bash Guide for Beginners , il peut être utilisé pour parcourir les arguments. IMNSHO, c'est maladroit; for est bien mieux pour ça.
  • Comme dans votre exemple de script, il est facile de traiter tous les arguments de la même manière, à l'exception de quelques-uns. Par exemple, dans votre script, $1 Et $2 Sont des chaînes de texte, tandis que $3 Et tous les autres paramètres sont des noms de fichier.

Voici comment ça se passe. Supposons que votre script s'appelle Patryk_script Et qu'il s'appelle comme

Patryk_script USSR Russia Treaty1 Atlas2 Pravda3

Le script voit

$1 = USSR
$2 = Russia
$3 = Treaty1
$4 = Atlas2
$5 = Pravda3

L'instruction ostr="$1" Définit la variable ostr sur USSR. La première instruction shift modifie les paramètres de position comme suit:

$1 = Russia
$2 = Treaty1
$3 = Atlas2
$4 = Pravda3

L'instruction nstr="$1" Définit la variable nstr sur Russia. La deuxième instruction shift modifie les paramètres de position comme suit:

$1 = Treaty1
$2 = Atlas2
$3 = Pravda3

Et puis la boucle for change USSR ($ostr) En Russia ($nstr) Dans les fichiers Treaty1, Atlas2 Et Pravda3.



Il y a quelques problèmes avec le script.

  1. pour le fichier dans $ @; faire

    Si le script est appelé en tant que

    Patryk_script URSS Traité de Russie1 "Atlas mondial2" Pravda3

    il voit

    1 $ = URSS 
     2 $ = Russie 
     3 $ = Traité1 
     4 $ = Atlas mondial2 
     5 $ = Pravda3

    mais, parce que $@ n'est pas cité, l'espace dans World Atlas2 n'est pas cité et la boucle for pense qu'elle a quatre fichiers: Treaty1, World, Atlas2 et Pravda3. Cela devrait être soit

    pour le fichier dans "$ @"; faire

    (pour citer tout caractère spécial dans les arguments) ou simplement

    pour le fichier faire

    (ce qui équivaut à la version plus longue).

  2. eval "sed 's /" $ ostr "/" $ nstr "/ g' $ file"

    Il n'est pas nécessaire que ce soit un eval, et le passage d'une entrée utilisateur non contrôlée à un eval peut être dangereux. Par exemple, si le script est appelé en tant que

    Patryk_script "'; rm *;'" Russia Treaty1 Atlas2 Pravda3

    il exécutera rm *! C'est une grande préoccupation si le script peut être exécuté avec des privilèges supérieurs à ceux de l'utilisateur qui l'invoque; par exemple, s'il peut être exécuté via Sudo ou appelé à partir d'une interface Web. Ce n'est probablement pas si important si vous l'utilisez comme vous-même, dans votre répertoire. Mais il peut être changé en

    sed "s/$ ostr/$ nstr/g" "$ file"

    Cela comporte encore certains risques, mais ils sont beaucoup moins graves.

  3. if [ -f $file ], > $file.tmp Et mv $file.tmp $file Doivent être respectivement if [ -f "$file" ], > "$file.tmp" Et mv "$file.tmp" "$file" Pour gérer les noms de fichiers qui peut contenir des espaces (ou d'autres personnages amusants). (La commande eval "sed … Modifie également les noms de fichiers contenant des espaces.)


* shift prend un argument facultatif: un entier positif qui spécifie le nombre de paramètres à décaler. La valeur par défaut est un (1). Par exemple, shift 4 Fait que $5 Devient $1, $6 Devient $2, Etc. (Notez que l'exemple dans le Bash Guide for Beginners est faux.) Et donc votre script pourrait être modifié pour dire

ostr="$1"
nstr="$2"
shift 2

qui pourrait être considéré comme plus clair.



Note de fin /Avertissement:

Le langage d'invite de commandes Windows (fichier de commandes) prend également en charge une commande SHIFT, qui fait essentiellement la même chose que la commande shift dans les shells Unix, avec une différence frappante, que je cacherai essayez d'éviter que les gens ne soient déroutés:

  • Une commande comme SHIFT 4 Est une erreur, générant un message d'erreur "Paramètre non valide pour la commande SHIFT".
  • SHIFT /n, Où n est un entier compris entre 0 et 8, est valide - mais il ne change pas n fois. Il change une fois, en commençant par l'argument n e. Ainsi SHIFT /4 Fait que %5 (Le cinquième argument) devient %4,%6 Devient %5, Et ainsi de suite, en laissant les arguments 0 à 3 seul.
37
Scott

L'explication la plus simple est la suivante. Considérez la commande:

/bin/command.sh SET location Cebu
/bin/command.sh SET location Cebu, Philippines 6014 

Sans l'aide de shift, vous ne pouvez pas extraire la valeur complète de l'emplacement car cette valeur pourrait devenir arbitrairement longue. Lorsque vous déplacez deux fois, les arguments SET et location sont supprimés de telle sorte que:

x="$@"
echo "location = $x"

Prenez un très long regard sur le $@ chose. Cela signifie qu'il peut enregistrer l'emplacement complet dans la variable x malgré les espaces et les virgules dont il dispose. Donc en résumé, nous appelons shift puis nous récupérons plus tard la valeur de ce qui reste de la variable $@.

UPDATE J'ajoute ci-dessous un extrait très court montrant le concept et l'utilité de shift sans lequel, il serait très, très difficile d'extraire correctement les champs.

#!/bin/sh
#

[ $# -eq 0 ] && return 0

a=$1
shift
b=$@
echo $a
[ -n "$b" ] && echo $b

Explication: Après le shift, la variable b doit contenir le reste des éléments transmis, peu importe les espaces ou etc. Le [ -n "$b" ] && echo $b est une protection telle que nous n'imprimons b que s'il a un contenu.

3
typelogic

shift traite les arguments de ligne de commande comme une file d'attente FIFO, il popleft chaque fois qu'il est invoqué.

array = [a, b, c]
shift equivalent to
array.popleft
[b, c]
$1, $2,$3 can be interpreted as index of the array.
$# is the length of array
2
Calculus