Czy podstawianie poleceń może być zagnieżdżone w podstawianiu zmiennych?

10

Chciałbym użyć podstawienia zmiennej na określonym ciągu, do którego uzyskuję dostęp za pomocą polecenia. Na przykład, jeśli skopiuję coś do schowka, mogę uzyskać do tego dostęp w ten sposób.

$ xclip -o -selection clipboard
Here's a string I just copied.

Jeśli przypiszę ją do zmiennej, będę mógł na niej podstawiać zmienne.

$ var=$(xclip -o -selection clipboard)
$ echo $var
Here's a string I just copied.
$ echo ${var/copi/knott}
Here's a string I just knotted.

Czy jednak istnieje sposób na podstawienie zmiennej bez przypisywania jej do zmiennej? Koncepcyjnie coś takiego.

$ echo ${$(xclip -o -selection clipboard)/copi/knott}
bash: ${$(xclip -o -selection clipboard)/copi/knott}: bad substitution

Ta składnia kończy się niepowodzeniem, ponieważ varpowinna to być nazwa zmiennej, a nie ciąg znaków.

Krogulec
źródło

Odpowiedzi:

6

Nie możesz. bashi większość innych powłok (oprócz zsh) nie zezwala na zagnieżdżone podstawianie.

Za pomocą zshmożesz wykonać zagnieżdżone podstawienie :

$ echo ${$(echo 123)/123/456}   
456
Cuonglm
źródło
Przyjmę tę odpowiedź, ponieważ zawiera ona pewne poszlaki, że nie jest to możliwe bash. (I popycha mnie ponownie w stronę migracji do zsh.)
Sparhawk
2

Tak, możesz to zrobić - w pewnym sensie. To naprawdę nie jest ładne. Jest bardziej podobny do wbudowanego niż zagnieżdżonego. Problem polega na tym, że musisz operować wartością parametru, który rozwijasz - jeśli ten parametr nie ma wartości, nie zrobisz wiele. Możesz więc przypisać wartość podczas jej rozwijania i nie jest to skrót.

v=; echo "${v:=${0##*["$0${v:=$(xsel -bo)}"]}${v/copi/knott}}"

Używam $0rozszerzenia param w łańcuchu, aby ukryć przypisanie. Przypisuje wartość var ​​w zagnieżdżonym rozszerzeniu przypisania. Zewnętrzne ma pierwszeństwo - ale ponieważ po prostu rozszerzyłoby się na cokolwiek wewnętrznego, to trudno powiedzieć. Jeśli jednak wyciszymy wewnętrzną ekspansję, a następnie zmodyfikujemy ją, możesz dostać to, czego chcesz. Po skopiowaniu łańcucha do mojego schowka (nie mam xclip- tylko xsel) drukuje:

Here's a string I just knotted.

Jest jednak trochę jaśniejsze, co się dzieje, jeśli $0się pominie:

v=; echo "${v:=${v:=$(xsel -bo)}${v/copi/knott}}"

To drukuje:

Here's a string I just copied.  Here's a string I just knotted.

... ponieważ przypisanie wewnętrzne występuje przed modyfikacją, ale, jak wspomniano, przypisanie zewnętrzne ma pierwszeństwo - i rozszerza się zarówno na rozszerzenie przypisania wewnętrznego, jak i na zmodyfikowane rozszerzenie wewnętrzne.

Oczywiście nic z tego nie działa, jeśli parametr docelowy jest już przypisany - więc możesz to zrobić tylko wtedy, gdy opróżnisz zmienną w pierwszej kolejności ... co, szczerze mówiąc, jest to prawdopodobnie najwygodniejszy czas na przypisanie jej w końcu .

mikeserv
źródło
+1 za obejście, chociaż, jak mówisz, jest to obejście, które prawdopodobnie jest gorsze niż przypisywanie zmiennej!
Sparhawk
@Sparhawk - tak, zdecydowanie gorzej. I tak naprawdę nie ma w tym nic złego - nie ma wiele do zyskania poza niepewnością. Możesz wymyślić jakąś aliaspośrednią funkcję, aby uczynić ją trochę wygodniejszą - ale jeśli jest to dla ciebie warte, powinieneś skonfigurować funkcję do obsługi bezpiecznego cytowania i robienia czegoś w / evallub czegoś podobnego. w / eval- jeśli możesz sprawić, że pierwsze znaki wyjściowego polecenia sub będą sprowadzały się do praktycznej składni rozszerzenia - to prawdopodobnie możesz dostać się znacznie dalej. Wiem, że taka rzecz byłaby łatwa w / xsel- wymaga stdin - ale xsel?
mikeserv
@Sparhawk - tak na marginesie, wiem tylko, jak to zrobić, ponieważ w niektórych sytuacjach może to być przydatne - na przykład szybkie lub dodatkowe rozszerzenia - w których nie można uzyskać bieżącego przypisania powłoki do zastosowania w inny sposób.
mikeserv
1

Jeśli nie chcesz tworzyć zmiennej, istnieją inne sposoby na zastąpienie łańcucha:

$ echo $(xclip -o -selection clipboard | sed 's/copi/knott/')
Here's a string I just knotted.
John1024
źródło
Dzięki, wiedziałem, że sedzamiast tego mogę użyć , ale miało to być bardziej ogólne pytanie dotyczące zagnieżdżania podstawień.
Sparhawk,
@Sparhawk O ile mi wiadomo, nie można podstawiać zmiennych bez posiadania zmiennej.
John1024,
Okej, to chyba odpowiedź. Pozostawię pytanie otwarte przez kilka dni, aby sprawdzić, czy ktoś ma odpowiedź, na którą się powołuję, a następnie zaakceptuję to inaczej. Dzięki.
Sparhawk
@Sparhawk Bardzo dobrze.
John1024,
+1, ale przyjmuję inną odpowiedź , ponieważ zapewnia ona nieco bardziej konkretne poszlaki, że nie działa w trybie bash.
Sparhawk