Używanie podstawiania parametrów w tablicy Bash

Mam plik file.txt, który muszę wczytać do tablicy Bash. Następnie muszę usunąć spacje, podwójne cudzysłowy i wszystkie oprócz pierwszego przecinka w każdym wpisie . Oto „jak daleko” dotarłem:

$ cat file.txt 10,this 2 0 , i s 30,"all" 40,I 50,n,e,e,d,2 60",s e,e" $ cat script.sh #!/bin/bash readarray -t ARRAY<$1 ARRAY=( "${ARRAY[@]// /}" ) ARRAY=( "${ARRAY[@]//\"/}" ) for ELEMENT in "${ARRAY[@]}";do echo "|ELEMENT|$ELEMENT|" done $ ./script.sh file.txt |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,n,e,e,d,2| |ELEMENT|60,se,e| 

Co działa świetnie z wyjątkiem sytuacji z przecinkami. Zdaję sobie sprawę, że istnieje wiele sposobów skórowania tego kota, ale ze względu na większy skrypt, którego jest częścią, naprawdę chciałbym użyć zastępowania parametrów, aby dostać się tutaj:

|ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see| 

Czy jest to możliwe poprzez podstawianie parametrów?

Komentarze

  • Czy jest jakiś powód, dla którego powinieneś zachować tekst w tablicę i dlaczego nie możesz ' np. awk lub sed czy przetwarzane są dane?
  • @Jeff – pętla po tablicy będzie koszmar do zaimplementowania w większym skrypcie, nad którym pracuję '.
  • @JonRed Nie ' nie wiem nad czym co robisz, więc ' jest całkowicie możliwe, że możesz nie mieć wyboru w tej sprawie, ale generalnie, gdy wykonujesz tak złożone akrobacje ze strunami w powłoce, ' to bardzo dobra wskazówka, że powinieneś używać rzeczywistego języka programowania. Powłoka nie została zaprojektowana jako język programowania i chociaż może być używana jako jeden, to naprawdę nie jest ' dobrym pomysłem na bardziej złożone rzeczy. Gorąco zachęcam do rozważenia przejścia na perl, python lub inny język skryptowy.
  • @terdon To ' to zabawne, właśnie skończyłem mówić prawie dokładnie to samo do mojego kolegi, zanim przeczytam ten post. Zasadniczo powiedziałem, że jest to ostateczna wersja tego skryptu i że wszelkie dalsze wymagania będą wymagały ponownego napisania w Perlu. Więc tak, zdecydowanie się zgadzam

Odpowiedź

Usunąłbym to, co musisz usunąć, używając sed przed załadowaniem do tablicy (zwróć także uwagę na nazwy zmiennych z małych liter, generalnie najlepiej jest unikać zmiennych pisanych wielkimi literami w skryptach powłoki):

#!/bin/bash readarray -t array< <(sed "s/"//g; s/ *//g; s/,/"/; s/,//g; s/"/,/" "$1") for element in "${array[@]}";do echo "|ELEMENT|$element|" done 

Daje to następujące wyniki w pliku przykładowym:

$ foo.sh file |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see| 

Jeśli naprawdę musisz użyć parametru podstawienie, spróbuj coś takiego:

#!/bin/bash readarray -t array< "$1" array=( "${array[@]// /}" ) array=( "${array[@]//\"/}" ) array=( "${array[@]/,/\"}" ) array=( "${array[@]//,/}" ) array=( "${array[@]/\"/,}" ) for element in "${array[@]}"; do echo "|ELEMENT|$element|" done 

Komentarze

  • @JonRed Dodałem wersję z parametrem podstawienie, ale jest ' skomplikowane, kłopotliwe i brzydkie. Robienie tego typu rzeczy w powłoce jest bardzo rzadko dobrym pomysłem.
  • Zauważ, że jeśli ' usuniesz spacje i podwójne cudzysłowy, te znaki staną się dostępne używać zamiast RANDOMTEXTTHATWILLNEVERBEINTHEFILE.
  • @Kusalananda tak, właśnie przeczytałem twoją odpowiedź. Powinienem był o tym pomyśleć! Dzięki 🙂
  • Bezpośrednio odpowiada na pytanie, ilustruje, dlaczego moje preferowane rozwiązanie nie jest ' t idealne i zapewnia najbardziej realną alternatywę. Wygrałeś, najlepsza odpowiedź.

Odpowiedź

O ile widzę, nie ma potrzeby wczytaj go do tablicy bash, aby utworzyć wynik:

$ sed "s/[ "]//g; s/,/ /; s/,//g; s/ /,/; s/.*/|ELEMENT|&|/" <file |ELEMENT|10,this| |ELEMENT|20,is| |ELEMENT|30,all| |ELEMENT|40,I| |ELEMENT|50,need2| |ELEMENT|60,see| 

sed wyrażenie usuwa spacje i podwójne cudzysłowy, zastępuje pierwszy przecinek spacją (w tym miejscu nie ma innych spacji w ciągu), usuwa wszystkie pozostałe przecinki, przywraca pierwszy przecinek, a poprzedza i dołącza dodatkowe dane .

Alternatywnie, z GNU sed:

sed "s/[ "]//g; s/,//2g; s/.*/|ELEMENT|&|/" <file 

(standard nie obsługuje kombinacji 2 i g jako flag do s polecenie).

Komentarze

  • W przypadku GNU sed można użyć 's/,//2g aby usunąć przecinki, zaczynając od drugiego
  • Ostatnie 2 s /// polecenia mogą być s/.*/|ELEMENT|&|/ ale to może być bardziej wymagające dla seda.
  • @glennjackman Możliwe, ale wygląda raczej zgrabnie.
  • Tak, to jest część większego skryptu. Tablica jest niezbędna nie tylko dla danych wyjściowych. Stąd moje zainteresowanie substytucją parametrów. Mógłbym za pomocą tego zapętlić tablicę, ale będzie to koszmar do wdrożenia. Terndon dostarczył rozwiązanie wolne od pętli, używając seda, które ' prawdopodobnie powróci, jeśli podstawianie parametrów okaże się niemożliwe.
  • Jeśli nie ' związany z użyciem tablicy, jednak byłoby to najlepsze rozwiązanie.

Odpowiedź

ELEMENT="50,n,e,e,d,2" IFS=, read -r first rest <<<"$ELEMENT" printf "%s,%s\n" "$first" "${rest//,/}" 
50,need2 

Pozbądź się nawyku używania nazw zmiennych ALLCAPS. W końcu zderzysz się z kluczową zmienną „systemową”, taką jak PATH, i zepsujesz swój kod.

Komentarze

  • Brak zastępowania parametrów. ALE, nie byłem świadomy tego, że nazwy zmiennych ALLCAPS były złym nawykiem w Bash. Masz rację, którą pobieżne wyszukiwanie w Google zdecydowanie potwierdza. Dziękuję za ulepszenie mojego stylu! 🙂
  • Odpowiedziałem ' na pytania, w których dana osoba napisała PATH=something; ls $PATH, a potem zastanawiała się nad ls: command not found.
  • Istnieje prawie sto wbudowanych zmiennych, które mają nazwy wielkimi literami (kliknij tę stronę podręcznika link ), aby zobaczyć …

Odpowiedź

[Jest to zasadniczo bardziej rozbudowana wersja odpowiedzi glenn jackmann „]

Tworzenie tablicy asocjacyjnej na podstawie usuniętego klucza i wartości, używając pierwszego przecinka jako separatora:

declare -A arr while IFS=, read -r k v; do arr["${k//[ \"]}"]="${v//[ ,\"]}"; done < file.txt for k in "${!arr[@]}"; do printf "|ELEMENT|%s,%s|\n" "$k" "${arr[$k]}" done |ELEMENT|20,is| |ELEMENT|10,this| |ELEMENT|50,need2| |ELEMENT|40,I| |ELEMENT|60,see| |ELEMENT|30,all| 

Odpowiedź

Możesz zapętlić tablicę i użyć zmiennej pośredniej:

for((i=0; i < "${#ARRAY[@]}"; i++)) do rest="${ARRAY[i]#*,}" ARRAY[i]="${ARRAY[i]%%,*}","${rest//,/}" done 

Przypisuje to rest część po pierwszym przecinku; następnie łączymy trzy części z powrotem w oryginał zmienna:

  • część przed pierwszym przecinkiem
  • przecinek
  • zamiana w rest każdego przecinka bez niczego

Komentarze

  • To była moja pierwsza myśl i jest wystarczająco prosta dla przykładu, ale jest to część większego skryptu, w którym tablica jest ogromna, a ' już się zapętla i to by była cała rzecz. To z pewnością zadziałałoby, ale byłoby bardzo kłopotliwe we wdrożeniu w większym projekcie, nad którym ' pracuję.
  • W porządku; Po prostu próbowałem odpowiedzieć w ramach ograniczeń (tylko rozszerzenie parametrów).

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *