Wiele poleceń sed w Bash

5

Mam nazwy użytkownika pliku i hasła w formacie JSON, które chcę przekonwertować do przetworzenia.

Użyłem sedw różnych poleceń, aby go przetworzyć, ale to, co chciałbym wiedzieć, jak lump wszystkie trzy polecenia do jednego na przyszłość.

Oryginalny format

    { "user.name1" : "hashed_password",
"user.name2" : "hashed_password" }

Pożądane wyjście

user.name:hashed_password

Są to komendy wpadłem, jednak nie udało mi się je ze sobą za pomocą łańcucha albo rur lub po prostu łącząc je tam, gdzie pojawia się błąd, sed: -e expression #1, char 8: unknown option to 's'.

Obraźliwe polecenie ...

sed -i 's/\"//g/s/\,/\n/g/\s//g' input_file 
sed: -e expression #1, char 8: unknown option to `s'

Jak można połączyć poniższe polecenia w jedno?

Polecenia Usuń podwójne cudzysłowy

sed -i 's/\"//g' input_file

Zastąp przecinek nową linią

sed -i 's/\,/\n/g' input_file

Usuń białe znaki

sed -i 's/\s//g input_file

3therk1ll
źródło

Odpowiedzi:

18

Aby umieścić wiele sedpoleceń w jednym „ skrypcie ”, możesz użyć wielu -eflag (które są przenośne):

sed -i -e 's/\"//g' -e 's/\,/\n/g' -e 's/\s//g' input_file

Lub separator średników (który nie jest dostępny we wszystkich implementacjach):

sed -i 's/\"//g;s/\,/\n/g;s/\s//g' input_file

Trzeba też dodać obsługę nawiasów klamrowych - {}...


Powiedziawszy to, aby poprawnie parsować i obsługiwać JSON, nie powinieneś tak naprawdę używać sed... może spróbuj jq!

jq -r 'keys[] as $k | "\($k):\(.[$k])"' input_file

Wydajność:

user.name1:hashed_password
user.name2:hashed_password
  • keys[] as $k przejdzie przez każdy klucz, przechowując jego wartość w $k
    • tj user.name1,user.name2
  • "\($k):\(.[$k])"utworzy ciąg, podstawiając w $ki.[$k]
  • Użycie -rusuwa cudzysłowy z ciągów wyjściowych ( tryb raw )

Używanie seddo przetwarzania JSON otworzy cię na wszelkiego rodzaju problemy ... na przykład, jak poradziłbyś sobie z następującymi (całkowicie poprawnymi JSON) danymi wejściowymi?

{
    "user.name1" :
        "hashed_password",
    "user.name2" :
        "hashed_password"
}
Attie
źródło
Jeśli chcesz, aby twoje pierwsze polecenie sed było jeszcze bardziej przenośne, powinieneś zrobić to, sed -i '' -e …aby zrobić to poprawnie na BSD. W przeciwnym razie zostanie zapisany plik kopii zapasowej o nazwieinput_file-e
Tonin
3

Kiedy masz do czynienia ze standardowymi danymi wejściowymi, takimi jak JSON, ogólnie lepiej jest użyć odpowiedniego parsera niż wyrażenia regularnego. Na przykład poprawnie skonwertujesz sekwencje specjalne (choć może to nie być możliwe w przypadku danych wejściowych!).

Niestety nie ma świetnych narzędzi do radzenia sobie z JSON w coreutils. Attie jest dostępna jq jako przyzwoita opcja, jeśli możesz swobodnie instalować pakiety.

Jeśli nie możesz zainstalować dodatkowych pakietów, nie jest to szczególnie trudne w Pythonie. Weźmy na przykład ten skrypt:

import json,sys
for (k, v) in json.load(sys.stdin):
    print(k + ":" + v)

Które można skompresować do jednej linii:

cat inputdata | python -c 'import json,sys;print("\n".join((k + ":" + v) for (k, v) in json.load(sys.stdin).items()))'
Kok
źródło
0

Dla prostego usuwania znaków, które wykonujesz za pomocą tych sedpoleceń, poleciłbym zamiast tego użyć tr, którego jedynym celem jest usuwanie, wyciskanie lub zastępowanie pojedynczych znaków, w tym znaków nowej linii ( sedjest oparty na wyrażeniach regularnych, które zwykle polegają na znakach nowej linii jako separatorach buforowych, więc używanie sed do modyfikowania nowych linii jest trudne). Myślę, że to trpolecenie robi wszystko, czego szukasz:

cat json_filename | tr -d "{}\" \012\011\015" | tr "," "\012"

Pierwsze trpolecenie usuwa wszystkie nawiasy klamrowe, podwójne cudzysłowy, spacje, znaki powrotu karetki (ósemkowe 012, ascii 10), tabulatory (ósemkowe 011, ascii 9 i linefeed (ósemkowe 015, ascii 13). Drugie trpolecenie zastępuje wszystkie przecinki zwraca karetkę. Tak długo, jak nazwy zmiennych i wartości pliku JSON nie zawierają przecinków, polecenia te pozwoliłyby uniknąć potrzeby dedykowanego analizatora składni JSON.

To powiedziawszy, jeśli masz zestaw sedpoleceń, z których każde działa niezależnie, ich połączenie można najłatwiej wykonać za pomocą opcji „-f” seddo odczytania osobnych poleceń z pliku. Wystarczy umieścić ciągi s /.../.../ g w pliku, każdy ciąg w osobnym wierszu, a następnie podać nazwę pliku po opcji „-f”. Na przykład, jeśli trzy sedwymienione polecenia są zadowalające, możesz umieścić je w pliku o nazwie „json.convert.sed”, który po prostu zawiera:

s/\"//g 
s/\,/\n/g
s/\s//g

Następnie wywołałbyś sedten plik poleceń za pomocą:

sed -f json.convert.sed

To powiedziawszy, te sedpolecenia nie działają dla mnie, aby osiągnąć to, czego chcesz, i nie jestem pewien, czy kiedykolwiek możesz sedzmodyfikować znaki nowego wiersza. Wynika to z faktu, że sedoparty jest na starym edytorze linii „ed”, przeznaczonym do edycji pojedynczych linii naraz (jego wersja obsługiwana przez „skrypt”), więc każdy wiersz danych wejściowych jest „analizowany” przy użyciu znaku nowej linii jako separatora, a następnie linia (bez nowej linii) jest przekazywana do silnika edycji, stosowane są polecenia edycji, a następnie edytowana linia jest wypisywana z nową linią. Następnie pętla się powtarza. Byłem w stanie tylko użyć seddo modyfikacji nowej linii, najpierw zmieniając nową linię na jakiś odrębny znak (który inaczej nie pojawia się na wejściu) przy użyciu tr. Tam'trtrzrobi to za ciebie. Ale jeśli na przykład chcesz przekonwertować znaki nowej linii na średniki ze spacją końcową, jednym ze sposobów jest:

cat input_file | tr "\012" "%" | sed "s/%/; /g"

( znaki nowej linii są konwertowane na% przez tr, a następnie sedkonwertują wszystkie% znaków na pary znaków „;”).

J. Hansen
źródło
0

Sed radzi sobie z edytowaniem wielu wierszy, ale zgadzam się z Attie i Bobem, parsowanie Jsona z wyrażeniem regularnym może stać się koszmarem.

sed -nr '/\{/ b Load ; d
: Load
/\}/ b Edit ; N ; b Load
: Edit ; s/[^"]+"([^"]+)"[^"]+"([^"]+)"(.*)/\1:\2\n\3/ ; t Print ; d
: Print ; P ; s/[^\n]+\n// ; t Edit' <<'eof'
{
    "user.name1" :
        "hashed_password1",
    "user.name2" :
        "hashed_password2"
}
    { "user.name3" : "hashed_password3",
"user.name4" : "hashed_password4" }

{ "user.name5":"hashed_password5"}
eof

user.name1:hashed_password1
user.name2:hashed_password2
user.name3:hashed_password3
user.name4:hashed_password4
user.name5:hashed_password5
Paulo
źródło
-1

Możesz to połączyć w następujący sposób:

sed -i 's/\"//g;s/\,/\n/g;s/\s//g' input_file

Zapomniałeś dodać usunięcie {}. Więc prawdopodobnie chcesz:

sed -i 's/\"//g;s/\,/\n/g;s/\s//g;s/{//g;s/}//g' input_file

Eloy
źródło