Przetwarzanie zmiennej bash z sed

16

zmienna bash LATLNG zawiera wartość szerokości i długości geograficznej w podobnych nawiasach

(53.3096,-6.28396)

Chcę parsować je do zmiennej o nazwie LAT i LON, którą próbuję wykonać za pomocą sed tak

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

Jednak pojawia się następujący błąd:

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

conorgriffin
źródło
1
Naucz się języka skryptowego, takiego jak Python, Ruby, a może nawet PERL, abyś nie musiał zmuszać powłoki do robienia wszystkiego za Ciebie. Możesz nawet uruchomić Javascript (Rhino) w systemie UNIX i prawie każdy musi znać Javascript.
Michael Dillon
Skąd pochodzą te wartości? Bardziej efektywne byłoby przeanalizowanie wartości szerokości i długości geograficznej z oryginalnego źródła niż dodanie dodatkowego kroku poprzez umieszczenie ich w bashzmiennej.
Kusalananda

Odpowiedzi:

18

Można to rozwiązać za pomocą czystej składni powłoki. Wymaga jednak zmiennej temp z powodu nawiasów (nawiasów):

#!/bin/bash

LATLNG="(53.3096,-6.28396)"

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

Alternatywnie możesz to zrobić za jednym razem, grając IFSi używając readwbudowanego:

#!/bin/bash

LATLNG="(53.3096,-6.28396)"

IFS='( ,)' read _ LAT LNG _ <<<"$LATLNG"
SiegeX
źródło
Pierwszy przykład działa tylko w bash, a nie w powłoce POSIX.
gelraen
1
@gelraen stąd#!/bin/bash
SiegeX 30.01.11
Zobacz więcej rozszerzeń tutaj: gnu.org/software/bash/manual/…
Ricardo Stuven
22

Odpowiedź SiegeX jest lepsza w tym konkretnym przypadku, ale powinieneś także wiedzieć, jak przekazać dowolny tekst sed.

sedoczekuje nazw plików jako drugiego, trzeciego itd. parametru, a jeśli nie znajdzie żadnych nazw plików, odczyta ze swojego standardowego wejścia. Więc jeśli masz tekst, który chcesz przetworzyć, którego nie ma w pliku, musisz go potokować sed. Najprostszym sposobem jest to:

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

Zatem twój przykład wyglądałby następująco:

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

Alternatywne (lepsze, ale bardziej niejasne) metody

Jeśli uważasz, że jest jakaś szansa $LATLNG zacząć od kreski lub jeśli chcesz być pedantyczny, powinieneś użyć printfzamiast echo:

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

Lub „tutaj dokument”, ale może to być trochę niezręczne w przypadku używanej konstrukcji:

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

Lub jeśli używasz bashi nie martwisz się o przenośność, możesz użyć ciągu „tutaj”:

sed 's/foo/bar/g' <<< "$LATLNG"
Jander
źródło
3

Oto rozwiązanie, które będzie działać w dowolnej powłoce 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"

Oto inne równie przenośne rozwiązanie, które analizuje zastosowaną składnię.

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- przestań być zły”
źródło
-1
LATLNG="(53.3096,-6.28396)"
set ${LATLNG//[(,)]/ }
LAT=$1
LON=$2
2nil
źródło
Prawdopodobnie powinieneś to zrobić. set -- ...Jest bardzo duża szansa, że ​​pierwsza postać jest -.
mikeserv