Passer une chaîne avec des espaces comme argument de fonction dans bash

173

J'écris un script bash dans lequel je dois passer une chaîne contenant des espaces à une fonction de mon script bash.

Par exemple:

#!/bin/bash

myFunction
{
    echo $1
    echo $2
    echo $3
}

myFunction "firstString" "second string with spaces" "thirdString"

Lors de l'exécution, la sortie à laquelle je m'attendais est:

firstString
second string with spaces
thirdString

Cependant, ce qui est réellement produit est:

firstString
second
string

Existe-t-il un moyen de passer une chaîne avec des espaces comme argument unique à une fonction dans bash?

Grant Limberg
la source
Fonctionne pour moi ... J'utilise la syntaxe complète pour les fonctions bien que "function bla () {echo $ 1;}", ne puisse pas en faire une courte en une seule ligne. Je ne suis pas sûr que cela fasse une différence. Quelle version de bash?
Eugene
8
essayez echo "$@"ou for i in "$@"; do echo $i ; donepour utiliser des paramètres correctement cités contenant des espaces. C'est le très clairement mentionné dans toute la bashdocumentation sous la positional parameterssection.
Samveen le
1
J'avais un problème similaire, en essayant de passer une chaîne entre guillemets en tant que paramètre et seul le premier mot de la chaîne étant reconnu comme faisant partie du paramètre. La suggestion de Samveen de changer $ 1 en $ @ a fonctionné pour moi. Notez que je ne passais qu'un seul paramètre à la fonction, mais si j'en avais passé plus, l'utilisation de l'instruction for aurait été nécessaire.
Erin Geyer
1
essayer myFunction "$@"
vimjet

Réponses:

176

vous devriez mettre des guillemets et aussi, votre déclaration de fonction est fausse.

myFunction()
{
    echo "$1"
    echo "$2"
    echo "$3"
}

Et comme les autres, ça marche aussi pour moi. Dites-nous quelle version de shell vous utilisez.

ghostdog74
la source
3
Cela fonctionne très bien pour moi aussi. Si nous appelons une autre fonction dans myFunction, passez des arguments avec des guillemets. Cheers :)
minhas23
2
Pouvez-vous expliquer pourquoi on a besoin de citations? J'ai essayé les deux avec et sans, et cela n'a pas fonctionné pour moi. J'utilise Ubuntu 14.04, GNU bash, version 4.3.11 (1) -release (x86_64-pc-linux-gnu). Ce qui fonctionne pour moi, c'est d'utiliser $ @ (avec ou sans guillemets).
Kyle Baker
@KyleBaker, sans les guillemets, $@se comporte comme sans guillemets $*- les résultats sont séparés par une chaîne, puis individuellement développés dans le monde, donc si vous avez des tabulations, ils seront convertis en espaces, si vous avez des mots qui peuvent être évalués comme des expressions globales, ils sera, etc.
Charles Duffy
17

Une autre solution au problème ci-dessus consiste à définir chaque chaîne sur une variable, appelez la fonction avec des variables désignées par un signe dollar littéral \$. Ensuite, dans la fonction, utilisez evalpour lire la variable et la sortie comme prévu.

#!/usr/bin/ksh

myFunction()
{
  eval string1="$1"
  eval string2="$2"
  eval string3="$3"

  echo "string1 = ${string1}"
  echo "string2 = ${string2}"
  echo "string3 = ${string3}"
}

var1="firstString"
var2="second string with spaces"
var3="thirdString"

myFunction "\${var1}" "\${var2}" "\${var3}"

exit 0

La sortie est alors:

    string1 = firstString
    string2 = second string with spaces
    string3 = thirdString

En essayant de résoudre un problème similaire à celui-ci, je tombais sur le problème d'UNIX en pensant que mes variables étaient délimitées par des espaces. J'essayais de passer une chaîne délimitée par un tube à une fonction en utilisant awkpour définir une série de variables utilisées plus tard pour créer un rapport. J'ai d'abord essayé la solution publiée par ghostdog74 mais je n'ai pas pu la faire fonctionner car tous mes paramètres n'étaient pas passés entre guillemets. Après avoir ajouté des guillemets à chaque paramètre, il a alors commencé à fonctionner comme prévu.

Vous trouverez ci-dessous l'état avant de mon code et fonctionne pleinement après l'état.

Avant - Code non fonctionnel

#!/usr/bin/ksh

#*******************************************************************************
# Setup Function To Extract Each Field For The Error Report
#*******************************************************************************
getField(){
  detailedString="$1"
  fieldNumber=$2

  # Retrieves Column ${fieldNumber} From The Pipe Delimited ${detailedString} 
  #   And Strips Leading And Trailing Spaces
  echo ${detailedString} | awk -F '|' -v VAR=${fieldNumber} '{ print $VAR }' | sed 's/^[ \t]*//;s/[ \t]*$//'
}

while read LINE
do
  var1="$LINE"

  # Below Does Not Work Since There Are Not Quotes Around The 3
  iputId=$(getField "${var1}" 3)
done<${someFile}

exit 0

Après - Code de fonctionnement

#!/usr/bin/ksh

#*******************************************************************************
# Setup Function To Extract Each Field For The Report
#*******************************************************************************
getField(){
  detailedString="$1"
  fieldNumber=$2

  # Retrieves Column ${fieldNumber} From The Pipe Delimited ${detailedString} 
  #   And Strips Leading And Trailing Spaces
  echo ${detailedString} | awk -F '|' -v VAR=${fieldNumber} '{ print $VAR }' | sed 's/^[ \t]*//;s/[ \t]*$//'
}

while read LINE
do
  var1="$LINE"

  # Below Now Works As There Are Quotes Around The 3
  iputId=$(getField "${var1}" "3")
done<${someFile}

exit 0
TheBanjoMinnow
la source
7

La solution la plus simple à ce problème est qu'il vous suffit d'utiliser \"des arguments séparés par des espaces lors de l'exécution d'un script shell:

#!/bin/bash
myFunction() {
  echo $1
  echo $2
  echo $3
}
myFunction "firstString" "\"Hello World\"" "thirdString"
Piyush Aggarwal
la source
5

Votre définition de myFunction est erronée. Ça devrait être:

myFunction()
{
    # same as before
}

ou:

function myFunction
{
    # same as before
}

Quoi qu'il en soit, cela a l'air bien et fonctionne bien pour moi sur Bash 3.2.48.

R Samuel Klatchko
la source
5

J'ai 9 ans de retard mais une manière plus dynamique serait

function myFunction {
   for i in "$*"; do echo "$i"; done;
}
remykarem
la source
2
Ah super! c'est exactement ce que je recherchais depuis quelque temps sur de nombreuses questions. Je vous remercie!
prosoitos
2

Solution simple qui a fonctionné pour moi - cité $ @

Test(){
   set -x
   grep "$@" /etc/hosts
   set +x
}
Test -i "3 rb"
+ grep -i '3 rb' /etc/hosts

J'ai pu vérifier la commande grep réelle (grâce à set -x).

Bin TAN - Victor
la source
-1

Vous pourriez avoir une extension de ce problème dans le cas où votre texte initial a été défini dans une variable de type chaîne, par exemple:

function status(){    
  if [ $1 != "stopped" ]; then
     artist="ABC";
     track="CDE";
     album="DEF";
     status_message="The current track is $track at $album by $artist";
     echo $status_message;
     read_status $1 "$status_message";
  fi
}

function read_status(){
  if [ $1 != "playing" ]; then
    echo $2
  fi
}

Dans ce cas, si vous ne transmettez pas la variable status_message sous forme de chaîne (entourée de ""), elle sera divisée en un montage d'arguments différents.

"$ variable" : La piste actuelle est CDE at DEF by ABC

$ variable : La

Helmedeiros
la source
OP utilisé myFunction "firstString" "second string with spaces" "thirdString"et cela n'a pas fonctionné pour lui. Donc, ce que vous proposez ne s'applique pas à cette question.
doubleDown
-2

J'ai eu le même genre de problème et en fait le problème n'était pas la fonction ni l'appel de fonction, mais ce que j'ai passé en arguments à la fonction.

La fonction a été appelée à partir du corps du script - le 'main' - donc j'ai passé "st1 a b" "st2 c d" "st3 e f" à partir de la ligne de commande et je l'ai passée à la fonction en utilisant myFunction $ *

Le $ * cause le problème car il se développe en un jeu de caractères qui sera interprété dans l'appel à la fonction en utilisant des espaces comme délimiteur.

La solution était de changer l'appel à la fonction dans la gestion explicite des arguments du 'main' vers la fonction: l'appel serait alors myFunction "$ 1" "$ 2" "$ 3" qui conservera les espaces à l'intérieur des chaînes car les guillemets délimiteront les arguments ... Donc si un paramètre peut contenir des espaces, il doit être traité explicitement à travers tous les appels de fonctions.

Comme cela peut être la raison de longues recherches sur des problèmes, il peut être judicieux de ne jamais utiliser $ * pour passer des arguments ...

J'espère que cela aidera quelqu'un, un jour, quelque part ... Jan.

Jan
la source
La bonne réponse est "$@", pas tous cités "$1", "$2"... paramters de position ni $*.
Samveen le