Passando uma string com espaços como argumento de função no bash

173

Estou escrevendo um script bash onde preciso passar uma string contendo espaços para uma função no meu script bash.

Por exemplo:

#!/bin/bash

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

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

Quando executado, a saída que eu esperaria é:

firstString
second string with spaces
thirdString

No entanto, o que é realmente produzido é:

firstString
second
string

Existe uma maneira de passar uma seqüência de caracteres com espaços como argumento único para uma função no bash?

Grant Limberg
fonte
Funciona para mim ... Eu uso a sintaxe completa para funções, embora "function bla () {echo $ 1;}" não possa abreviar uma para uma linha. Não tenho certeza se isso faz diferença. Qual versão do bash?
30909 Eugene
8
tente echo "$@"ou for i in "$@"; do echo $i ; doneuse parâmetros entre aspas corretamente que contenham espaços. É o que é claramente mencionado em toda a bashdocumentação da positional parametersseção.
Samveen
1
Eu estava tendo um problema semelhante, tentando passar uma string entre aspas como parâmetro e apenas a primeira palavra da string sendo reconhecida como parte do parâmetro. A sugestão de Samveen de mudar $ 1 para $ @ funcionou para mim. Observe que eu estava passando apenas um parâmetro para a função, mas se eu estivesse passando mais usando a instrução for, seria necessário.
Erin Geyer
1
tente myFunction "$@"
vimjet 22/09

Respostas:

176

você deve colocar aspas e também, sua declaração de função está errada.

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

E, como os outros, funciona para mim também. Diga-nos qual versão do shell você está usando.

ghostdog74
fonte
3
Isso funciona muito bem para mim também. Se estivermos chamando outra função dentro de myFunction, passe argumentos com aspas. Cheers :)
minhas23
2
Você pode explicar por que precisamos de aspas? Eu tentei com e sem, e não funcionou para mim. Estou usando o Ubuntu 14.04, GNU bash, versão 4.3.11 (1) -release (x86_64-pc-linux-gnu). O que funciona para mim é usar $ @ (com ou sem aspas).
Kyle Baker
O @KyleBaker, sem as aspas, $@se comporta da mesma forma que sem aspas $*- os resultados são divididos por sequência e depois expandidos individualmente; portanto, se você tiver guias, elas serão convertidas em espaços, se você tiver palavras que possam ser avaliadas como expressões globais. será, etc.
Charles Duffy
17

Outra solução para o problema acima é definir cada string como uma variável, chamar a função com variáveis ​​indicadas por um cifrão literal \$. Em seguida, na função use evalpara ler a variável e a saída conforme o esperado.

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

A saída é então:

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

Ao tentar resolver um problema semelhante a esse, estava encontrando o problema do UNIX pensando que minhas variáveis ​​eram delimitadas por espaço. Eu estava tentando passar uma string delimitada por canal para uma função usando awkpara definir uma série de variáveis ​​usadas posteriormente para criar um relatório. Inicialmente, tentei a solução postada por ghostdog74, mas não consegui fazê-la funcionar, pois nem todos os meus parâmetros estavam sendo passados ​​entre aspas. Após adicionar aspas duplas a cada parâmetro, ele começou a funcionar conforme o esperado.

Abaixo está o estado anterior do meu código e funcionando totalmente após o estado.

Antes - Código que não funciona

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

Código de funcionamento após

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

A solução mais simples para esse problema é que você só precisa usar \"argumentos separados por espaço ao executar um script de shell:

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

Sua definição de myFunction está errada. Deveria ser:

myFunction()
{
    # same as before
}

ou:

function myFunction
{
    # same as before
}

Enfim, parece bom e funciona bem para mim no Bash 3.2.48.

R Samuel Klatchko
fonte
5

Estou com 9 anos de atraso, mas uma maneira mais dinâmica seria

function myFunction {
   for i in "$*"; do echo "$i"; done;
}
remykarem
fonte
2
Ah, ótimo! isto é exatamente o que eu procurava há algum tempo em muitas perguntas. Obrigado!
Prosoitos 23/11/19
2

Solução simples que funcionou para mim - citado $ @

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

Eu pude verificar o comando grep real (graças ao conjunto -x).

Bin TAN - Victor
fonte
-1

Você pode ter uma extensão desse problema caso o texto inicial tenha sido definido em uma variável do tipo string, por exemplo:

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
}

Nesse caso, se você não passar a variável status_message para frente como string (cercada por ""), ela será dividida em uma montagem de argumentos diferentes.

"$ variable" : a faixa atual é CDE no DEF da ABC

variável $ : O

helmedeiros
fonte
OP usado myFunction "firstString" "second string with spaces" "thirdString"e não funcionou para ele. Portanto, o que você propõe não se aplica a essa pergunta.
DoubleDown
-2

Tinha o mesmo tipo de problema e, de fato, o problema não era a função nem a chamada de função, mas o que eu passava como argumentos para a função.

A função foi chamada a partir do corpo do script - o 'main' -, então eu passei "st1 a b" "st2 c d" "st3 ef" da linha de comando e passei para a função usando myFunction $ *

O $ * causa o problema à medida que se expande para um conjunto de caracteres que serão interpretados na chamada para a função usando o espaço em branco como delimitador.

A solução foi alterar a chamada para a função no tratamento explícito de argumentos do 'main' para a função: a chamada seria então myFunction "$ 1" "$ 2" "$ 3", que preservará o espaço em branco nas cadeias, conforme as aspas delimitarão os argumentos ... Portanto, se um parâmetro pode conter espaços, ele deve ser tratado explicitamente em todas as chamadas de funções.

Como esse pode ser o motivo de longas pesquisas de problemas, pode ser aconselhável nunca usar $ * para passar argumentos ...

Espero que isso ajude alguém, algum dia, em algum lugar ...

Jan
fonte
A resposta correta é "$@", nem todos citado "$1", "$2"... paramters posicionais nem $*.
Samveen