Processando variável bash com sed

16

variável bash LATLNG contém um valor de latitude e longitude entre parênteses como

(53.3096,-6.28396)

Eu quero analisá-los em uma variável chamada LAT e LON, que estou tentando fazer via sed assim

LAT=$(sed "s/(\(.*\),\(.*\))/\1/g" "$LATLNG")
LON=$(sed "s/(\(.*\),\(.*\))/\2/g" "$LATLNG")

No entanto, eu recebo o seguinte erro:

sed: can't read (53.3096,-6.28396): No such file or directory

Conorgriffin
fonte
1
Aprenda uma linguagem de script como Python, Ruby, talvez até PERL, para que você não precise forçar o shell a fazer tudo por você. Você pode até fazer o Javascript (Rhino) rodar em um sistema UNIX e quase todo mundo precisa saber um pouco de Javascript.
Michael Dillon
De onde vêm esses valores? Seria mais eficiente analisar os valores de latitude e longitude da fonte original do que adicionar uma etapa extra colocando-os em uma bashvariável.
Kusalananda

Respostas:

18

Isso pode ser resolvido através da sintaxe pura do shell. Requer uma variável temporária devido aos parênteses (colchetes), embora:

#!/bin/bash

LATLNG="(53.3096,-6.28396)"

tmp=${LATLNG//[()]/}
LAT=${tmp%,*}
LNG=${tmp#*,}

Como alternativa, você pode fazê-lo de uma só vez, brincando IFSe usando o readbuiltin:

#!/bin/bash

LATLNG="(53.3096,-6.28396)"

IFS='( ,)' read _ LAT LNG _ <<<"$LATLNG"
SiegeX
fonte
O primeiro exemplo funcionará apenas no bash, não no shell POSIX.
gelraen
1
@gelraen daí o#!/bin/bash
SiegeX
Veja mais expansões aqui: gnu.org/software/bash/manual/…
Ricardo Stuven
22

A resposta do SiegeX é melhor para este caso em particular, mas você também deve saber como passar texto arbitrário sed.

sedespera nomes de arquivos como seu segundo, terceiro, etc. parâmetros e, se não encontrar nenhum nome de arquivo, ele lê sua entrada padrão. Portanto, se você tiver um texto que deseja processar que não esteja em um arquivo, precisará canalizá-lo sed. A maneira mais direta é esta:

echo "blah blah" | sed 's/blah/blam/g'

Portanto, seu exemplo se tornaria:

LAT=$(echo "$LATLNG" | sed 's/(\(.*\),\(.*\))/\1/g')
LON=$(echo "$LATLNG" | sed 's/(\(.*\),\(.*\))/\2/g')

Métodos alternativos (melhores, porém mais obscuros)

Se você acha que há alguma chance de $LATLNGcomeçar com um traço, ou se você quer ser pedante, useprintf vez de echo:

printf '%s' "$LATLNG" | sed 's/foo/bar/g'

Ou um "documento aqui", mas isso pode ser um pouco estranho com a construção que você está usando:

LAT=$(sed 's/foo/bar/g' <<END
$LATLNG
END
)

Ou, se você estiver usando bashe não estiver preocupado com a portabilidade, poderá usar uma "string aqui":

sed 's/foo/bar/g' <<< "$LATLNG"
Jander
fonte
3

Aqui está uma solução que funcionará em qualquer shell POSIX:

parse_coordinates () {
  IFS='(), '  # Use these characters as word separators
  set -f      # Disable globbing
  set $1      # Split $1 into separate words
  set +f      # Restore shell state
  unset IFS
  LAT=$2      # $1 is the empty word before the open parenthesis
  LON=$3
}
parse_coordinates "$LATLNG"

Aqui está outra solução igualmente portátil que analisa a sintaxe específica usada.

LAT=${LATLNG%\)}    # strip final parenthesis
LAT=${LAT#\(}       # strip initial parenthesis
LON=${LAT##*[, ]}   # set LON to everything after the last comma or space
LAT=${LAT%%[, ]*}   # set LAT to everything before the first comma or space
Gilles 'SO- parar de ser mau'
fonte
-1
LATLNG="(53.3096,-6.28396)"
set ${LATLNG//[(,)]/ }
LAT=$1
LON=$2
2nil
fonte
Você provavelmente deveria fazer set -- ...Há uma chance muito boa de o primeiro personagem ser -.
precisa