Sparametryzuj powiązane połączenia z programem narzędziowym w Bash

12

Mam program UNIX czarnej skrzynki używany w powłoce Bash, która odczytuje kolumny danych ze standardowego wejścia, przetwarza je (stosując efekt wygładzania), a następnie wysyła do standardowego wyjścia. Używam go przez potoki UNIX, jak

generate | smooth | plot  

Aby uzyskać bardziej wygładzanie, mogę powtórzyć wygładzanie, aby można je było wywołać z wiersza poleceń Bash jako

generate | smooth | smooth | plot   

lub nawet

generate | smooth | smooth | smooth | smooth | smooth | smooth | smooth | smooth | smooth | smooth | plot

To staje się coraz trudniejsze. Chciałbym, aby otoki Bash były w stanie wpakować smoothi przekazać dane wyjściowe z powrotem do nowej instancji smoothdowolnej liczby razy, coś w rodzaju

generate | newsmooth 5 | plot

zamiast

generate | smooth | smooth | smooth | smooth | smooth | plot

Moją pierwszą próbą był skrypt Bash, który wygenerował pliki tymczasowe w bieżącym katalogu i usunął je, ale okazał się brzydki, gdy nie było mnie w katalogu z dostępem do zapisu, a także pozostawił pliki śmieci po przerwaniu.

Brak argumentów do smoothprogramu.

Czy istnieje bardziej elegancki sposób na „zawinięcie” takiego programu w celu sparametryzowania liczby połączeń?

Diane Wilbor
źródło
1
Mam nadzieję, że twój przykład jest wymuszoną sprawą ze względu na pytanie, a nie rzeczywistą potrzebą
arielnmz

Odpowiedzi:

18

Możesz zawinąć go w funkcję rekurencyjną:

smooth() {
  if [[ $1 -gt 1 ]]; then # add another call to function
    command smooth | smooth $(($1 - 1)) 
  else
    command smooth # no further 
  fi
}

Użyłbyś tego jako

generate | smooth 5 | plot

co byłoby równoważne z

generate | smooth | smooth | smooth | smooth | smooth | plot
muru
źródło
Jest to idealne, zachowuje się dokładnie tak, jak potrzeba. A teraz dowiedziałem się o słowie kluczowym „polecenie” bash.
Diane Wilbor,
2
Nawiasem mówiąc, jest to to samo podejście, którego używam w Jak zakodować arbitralnie długi łańcuch rur? - i na długo przed tym, w obsłudze długich list edycji w xmlstarlet .
Charles Duffy
5

Jeśli możesz sobie pozwolić na wpisanie tylu przecinków, ile smoothchcesz, możesz skorzystać z oddzielonego przecinkami rozszerzenia nawiasów.

TL; DR

Cały wiersz polecenia dla przykładowego przypadku to:

generate | eval 'smooth |'{,,,,} plot

Uwaga:

  • dodaj lub usuń przecinki, jeśli chcesz więcej lub mniej powtórzeń smooth |
  • nie ma go |wcześniej, plotponieważ jest to uwzględnione w ostatnim smooth |ciągu wyprodukowanym przez Brace Expansion
  • możesz także podać argumenty smooth, pod warunkiem, że umieścisz je poprawnie w cytowanej części stałej, która poprzedza otwarty nawias klamrowy; w każdym razie pamiętaj, że będziesz je przekazywał we wszystkich powtórzeniach polecenia

Jak to działa

Rozwinięcie nawiasu rozdzielanego przecinkami umożliwia dynamiczne tworzenie ciągów, z których każdy składa się z określonej części stałej i określonych części zmiennych. Wytwarza tyle łańcuchów, ile jest wskazanych części zmiennych, takich jak a{b,c,d}produkuje ab ac ad.

Mała sztuczka polega na tym, że jeśli raczej utworzysz listę pustych części zmiennych, tj. Z tylko przecinkami w nawiasach klamrowych, rozszerzenie nawiasów spowoduje tylko utworzenie kopii części stałej. Na przykład:

smooth{,,,,}

będzie produkować:

smooth smooth smooth smooth smooth

Zauważ, że 4 przecinki dają 5 smoothciągów. Właśnie tak działa ten Brace Expansion: tworzy ciągi znaków tyle przecinków plus jeden.

Oczywiście w twoim przypadku potrzebujesz również |każdego oddzielającego smooth, więc po prostu dodaj go do części stałej, ale pamiętaj o prawidłowym cytowaniu, aby powłoka nie interpretowała go od razu. To jest:

'smooth|'{,,,,}

będzie produkować:

'smooth|' 'smooth|' 'smooth|' 'smooth|' 'smooth|'

Zadbaj o to, aby zawsze umieszczać nieruchomą część bezpośrednio przylegającą do otwartej klamry, tzn. Nie odstępować między nią ' a {.

(Zauważ również, że aby utworzyć część stałą, możesz również użyć podwójnych cudzysłowów zamiast pojedynczych cudzysłowów, jeśli chcesz rozwinąć zmienne powłoki w stałej części. Wystarczy zadbać o dodatkowe znaki ucieczki, które są wymagane, gdy pojawią się znaki specjalne niektórych powłok wewnątrz ciągu podwójnego cudzysłowu).

W tym momencie musisz eval zastosować ten ciąg, aby powłoka ostatecznie zinterpretowała go jako polecenie potokowe, jakie powinno być.

Tak więc, podsumowując wszystko, cały wiersz polecenia dla twojego przykładowego przypadku wyglądałby następująco:

generate | eval 'smooth |'{,,,,} plot
LL3
źródło
1
Istnieją poważne obawy dotyczące bezpieczeństwa, jeśli jest to używane w miejscach, w których połączenie jest parametryzowane. Zobacz moją odpowiedź na temat funkcji rekurencyjnej bash vs. iteracyjne budowanie ciągów „eval”: Który działa lepiej? po przepełnieniu stosu.
Charles Duffy
1
@CharlesDuffy W pełni zgadzam się z twoimi obawami dotyczącymi domniemanego ryzyka związanego z używaniem, evalgdy ktoś dostarcza niezaufane, niezanieczyszczone ciągi do oceny, to znaczy, gdy jest używany ze zmiennymi, które mogą zawierać „nieznane” treści, takie jak przypadek, który podłączyłeś. Z drugiej strony, evalmoże być również bardzo przydatny do szybkiego „hydraulicznego” wykonywania poleceń, zwłaszcza gdy jest używany w wierszu poleceń, tak jak wydaje się w omawianym przypadku, gdzie evaldane wejściowe byłyby tylko literalnym ciągiem znaków wpisanym ręcznie przez użytkownika w osoba
LL3
Jak już widzieliśmy gdzie indziej, zawsze możesz zastąpić eval strcoś pretensjonalnego i głupiego . /dev/stdin <<<str. Nie tylko wywrze to wrażenie na głupcach, ale również powstrzyma @CharlesDuffy od twoich pleców ;-)
pizdelect,
1
@pizdelect, możesz uważnie przeczytać poprzedni komentarz LL3 - jest zrównoważony, dopracowany i mądry. (Rzeczywiście, mój własny wstępny komentarz zawierał niuanse, które wydajesz się ignorować; „jeśli jest używany w przypadkach, gdy wywołanie jest sparametryzowane” jest krytycznym rozróżnieniem: wystąpienie LL3 nie jest sparametryzowane, dzięki czemu jest bezpieczne).
Charles Duffy,