Przekazywanie ciągu znaków ze spacjami jako argumentu funkcji w bash

173

Piszę skrypt bash, w którym muszę przekazać ciąg zawierający spacje do funkcji w moim skrypcie bash.

Na przykład:

#!/bin/bash

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

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

Po uruchomieniu oczekiwany wynik to:

firstString
second string with spaces
thirdString

Jednak w rzeczywistości otrzymujemy:

firstString
second
string

Czy istnieje sposób na przekazanie ciągu znaków ze spacjami jako pojedynczego argumentu do funkcji w bash?

Grant Limberg
źródło
Działa dla mnie ... Używam pełnej składni dla funkcji, chociaż "function bla () {echo $ 1;}", nie mogę przekształcić krótkiej w jedną linijkę. Nie wiem, czy to ma znaczenie. Jaka wersja basha?
Eugene
8
spróbuj echo "$@"lub for i in "$@"; do echo $i ; doneużyj poprawnie cytowanych parametrów zawierających spacje. Jest to bardzo wyraźnie wymienione w całej bashdokumentacji w positional parameterssekcji.
Samveen
1
Miałem podobny problem, próbując przekazać jeden cytowany ciąg jako parametr i tylko pierwsze słowo ciągu było rozpoznawane jako część parametru. Sugestia Samveena, aby zmienić 1 $ na $ @, zadziałała dla mnie. Zwróć uwagę, że przekazywałem tylko jeden parametr do funkcji, ale gdybym przekazywał więcej za pomocą instrukcji for, byłoby konieczne.
Erin Geyer
1
spróbuj myFunction "$@"
vimjet

Odpowiedzi:

176

powinieneś umieścić cudzysłowy, a także deklaracja funkcji jest nieprawidłowa.

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

I tak jak inne, działa również na mnie. Powiedz nam, jakiej wersji powłoki używasz.

ghostdog74
źródło
3
U mnie to również działa bardzo dobrze. Jeśli wywołujemy inną funkcję wewnątrz myFunction, przekaż argumenty w cudzysłowie. Pozdrawiam :)
minhas23
2
Czy możesz wyjaśnić, dlaczego potrzebne są cytaty? Próbowałem zarówno z, jak i bez, ale to nie zadziałało. Używam Ubuntu 14.04, GNU bash, wersja 4.3.11 (1) -release (x86_64-pc-linux-gnu). Co czyni pracę dla mnie jest za pomocą $ @ (z lub bez cudzysłowów).
Kyle Baker
@KyleBaker, bez cudzysłowów, $@zachowuje się jak bez cudzysłowu $*- wyniki są dzielone na ciągi, a następnie indywidualnie rozwijane globalnie, więc jeśli masz tabulatory, zostaną one zamienione na spacje, jeśli masz słowa, które można obliczyć jako wyrażenia glob, będzie itp.
Charles Duffy
17

Innym rozwiązaniem powyższego problemu jest ustawienie każdego ciągu na zmienną, wywołanie funkcji ze zmiennymi oznaczonymi literowym znakiem dolara \$. Następnie w funkcji użyj, evalaby odczytać zmienną i wyprowadzić zgodnie z oczekiwaniami.

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

Wynik jest zatem:

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

Próbując rozwiązać problem podobny do tego, natknąłem się na problem UNIX, myśląc, że moje zmienne są rozdzielane spacjami. Próbowałem przekazać ciąg znaków rozdzielonych potokami do funkcji używającej awkdo ustawienia serii zmiennych, które później zostały użyte do utworzenia raportu. Początkowo wypróbowałem rozwiązanie opublikowane przez ghostdog74, ale nie mogłem go uruchomić, ponieważ nie wszystkie moje parametry były przekazywane w cudzysłowie. Po dodaniu podwójnych cudzysłowów do każdego parametru zaczął on działać zgodnie z oczekiwaniami.

Poniżej znajduje się stan mojego kodu przed i w pełni funkcjonujący po stanie.

Przed - niedziałający kod

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

Po - kod działający

#!/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
źródło
7

Najprostszym rozwiązaniem tego problemu jest to, że wystarczy użyć \"argumentów oddzielonych spacjami podczas uruchamiania skryptu powłoki:

#!/bin/bash
myFunction() {
  echo $1
  echo $2
  echo $3
}
myFunction "firstString" "\"Hello World\"" "thirdString"
Piyush Aggarwal
źródło
5

Twoja definicja myFunction jest błędna. Powinno być:

myFunction()
{
    # same as before
}

lub:

function myFunction
{
    # same as before
}

W każdym razie wygląda dobrze i działa dobrze dla mnie na Bash 3.2.48.

R Samuel Klatchko
źródło
5

Spóźniłem się 9 lat, ale byłby bardziej dynamiczny sposób

function myFunction {
   for i in "$*"; do echo "$i"; done;
}
remykarem
źródło
2
Świetnie! właśnie tego szukałem przez jakiś czas w wielu pytaniach. Dziękuję Ci!
prosoitos
2

Proste rozwiązanie, które zadziałało dla mnie - cytat $ @

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

Mogłem zweryfikować faktyczne polecenie grep (dzięki set -x).

Bin TAN - Victor
źródło
-1

Możesz mieć rozszerzenie tego problemu w przypadku, gdy twój początkowy tekst został ustawiony w zmiennej typu łańcuchowego, na przykład:

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
}

W tym przypadku, jeśli nie przekażesz zmiennej status_message naprzód w postaci łańcucha (otoczonego ""), zostanie ona podzielona na szereg różnych argumentów.

„$ variable” : bieżący utwór to CDE w DEF autorstwa ABC

$ zmienna :

helmedeiros
źródło
OP używany myFunction "firstString" "second string with spaces" "thirdString"i nie działał dla niego. Więc to, co proponujesz, nie dotyczy tego pytania.
doubleDown
-2

Wystąpił ten sam rodzaj problemu iw rzeczywistości problemem nie była funkcja ani wywołanie funkcji, ale to, co przekazałem jako argumenty do funkcji.

Funkcja została wywołana z treści skryptu - 'main' - więc przekazałem "st1 a b" "st2 c d" "st3 e f" z linii poleceń i przekazałem do funkcji używając myFunction $ *

$ * Powoduje problem, ponieważ rozwija się do zestawu znaków, które zostaną zinterpretowane w wywołaniu funkcji przy użyciu białych znaków jako separatora.

Rozwiązaniem była zmiana wywołania funkcji w jawnej obsłudze argumentów z 'main' na funkcję: wywołanie byłoby wtedy myFunction "$ 1" "$ 2" "$ 3", co zachowa spacje wewnątrz łańcuchów, ponieważ cudzysłowy będą ograniczały argumenty ... Więc jeśli parametr może zawierać spacje, powinien być obsługiwany jawnie we wszystkich wywołaniach funkcji.

Ponieważ może to być przyczyną długich poszukiwań problemów, rozsądne może być nigdy nie używanie znaku $ * do przekazywania argumentów ...

Mam nadzieję, że to pomoże komuś kiedyś, gdzieś ... Jan.

Jan
źródło
Prawidłowa odpowiedź to "$@"nie wszystkie podane "$1", "$2"... pozycyjne paramters ani $*.
Samveen