Jak wykonać wyjście polecenia w bieżącej powłoce?

89

Doskonale znam narzędzie source(aka .), które pobierze zawartość z pliku i uruchomi ją w bieżącej powłoce.

Teraz przekształcam tekst w polecenia powłoki, a następnie uruchamiam je w następujący sposób:

$ ls | sed ... | sh

lsto tylko przypadkowy przykład, oryginalny tekst może być dowolny. sedtakże przykład przekształcania tekstu. Ciekawe jest to sh. Piszczę wszystko, co mam, shi to działa.

Mój problem polega na tym, że oznacza to uruchomienie nowej podpowłoki. Wolałbym, aby polecenia były uruchamiane w mojej obecnej powłoce. Jakbym był w stanie zrobić source some-file, gdybym miał polecenia w pliku tekstowym.

Nie chcę tworzyć pliku tymczasowego, ponieważ jest brudny.

Alternatywnie, chciałbym rozpocząć moją subpowłokę z dokładnie takimi samymi cechami, jak moja obecna powłoka.

aktualizacja

Ok, rozwiązania korzystające z backticka z pewnością działają, ale często muszę to robić, gdy sprawdzam i zmieniam dane wyjściowe, więc wolałbym, aby istniał sposób, aby ostatecznie przekuć wynik na coś.

smutna aktualizacja

Ach, /dev/stdinrzecz wyglądała tak ładnie, ale w bardziej złożonym przypadku nie działała.

Więc mam to:

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/git mv -f $1 $2.doc/i' | source /dev/stdin

Co zapewnia, że ​​wszystkie .docpliki mają małe litery rozszerzenia.

I z którym, nawiasem mówiąc, można sobie poradzić xargs, ale nie o to chodzi.

find . -type f -iname '*.doc' | ack -v '\.doc$' | perl -pe 's/^((.*)\.doc)$/$1 $2.doc/i' | xargs -L1 git mv

Więc kiedy uruchomię ten pierwszy, od razu się zamknie, nic się nie dzieje.

kch
źródło
Czy twoje złożone polecenie działa, gdy najpierw przesyłasz potokiem do pliku tymczasowego, a następnie go źródłowym? Jeśli nie, jaki jest problem z wygenerowanymi danymi wyjściowymi? Dane wyjściowe twojego polecenia nie będą działać, jeśli twoje nazwy plików zawierają spacje lub jeśli niektóre sekwencje nie zostaną poprawnie zmienione. Chciałbym dodać cytaty co najmniej około 1 USD i 2 USD za dokument.
Kaleb Pederson
Czy jest jakiś dobry powód, aby uruchamiać to w oryginalnej powłoce? - te przykłady nie manipulują bieżącą powłoką, więc nic nie zyskujesz na tym. Szybkim rozwiązaniem jest przekierowanie wyjścia do pliku i jego źródło
nr
@kaleb wyjście działa poprawnie. w tym konkretnym przypadku, nawet jeśli potokuję do sh. nazwy plików są bezpieczne dla spacji, ale dziękuję za uwagę. @nos git zmienne środowiskowe w oryginalnej powłoce. i znowu są to tylko przykłady. pytanie dotyczy życia.
kch
source / dev / stdin nie działało dla mnie, gdy potrzebowałem przypisanych zmiennych do pozostania. geirha na freenode bash wskazał mi mywiki.wooledge.org/BashFAQ/024 i zasugerował, żebym spróbował źródła zastępowania procesu <(polecenie), które działało dla mnie
srcerer

Odpowiedzi:

85
$ ls | sed ... | source /dev/stdin

UPDATE: Działa w bash 4.0, a także w tcsh i dash (jeśli zmienisz sourcena .). Najwyraźniej był to błąd w bashu 3.2. Z informacji o wydaniu bash 4.0 :

Naprawiono błąd, który powodował `. ' aby nie odczytać i wykonać poleceń z niestandardowych plików, takich jak urządzenia lub potoki nazwane.

mark4o
źródło
Ok. W takim razie na bash 4.0.
kch
Aha, a ponieważ wymieniasz powłoki, działa również w zsh.
kch
1
Myślałem, że to genialne, dopóki nie wypróbowałem tego w msys / mingw (gdzie nie ma folderu / dev (ani nawet urządzeń)! Próbowałem wielu kombinacji eval, $ () i lewych apostrofów ''. Nic nie działało , więc w końcu przekierowałem dane wyjściowe do pliku tymczasowego, pobrałem plik tymczasowy i usunąłem go. Zasadniczo „sed ...> /tmp/$$.tmp &&. /tmp/$$.tmp && rm / tmp / $$. tmp ”. Ktoś ma rozwiązanie msys / mingw bez plików tymczasowych ???
chriv
10
Tylko uwaga: ten wydaje się nie obsługiwać oświadczeń eksportowych zgodnie z oczekiwaniami. Jeśli potokowany łańcuch zawiera instrukcję eksportu, zmienna exportet nie jest później dostępna w środowisku terminala, podczas gdy w przypadku eval export działa również doskonale.
Phil
1
Biorąc pod uwagę, że MacOS X zwykle ma bash 3.2 (może Yosemite ma 4.0?) I potrzebę sztuczek lastpipe w moim przypadku użycia (eksportowanie wszystkich zmiennych w pliku przez wstępne przetwarzanie każdej linii za pomocą seda, aby dodać `` eksport '') wybrałem za eval "$(sed ...)"podejście - ale dzięki za informację o błędzie 3.2!
Alex Dupuy
133

Plik evalPolecenie istnieje w tym samym celu.

eval "$( ls | sed... )"

Więcej z podręcznika basha :

eval

          eval [arguments]

Argumenty są łączone w jedno polecenie, które jest następnie odczytywane i wykonywane, a jego kod zakończenia jest zwracany jako kod zakończenia eval. Jeśli nie ma argumentów lub są tylko puste argumenty, zwracany kod wynosi zero.

Juliano
źródło
8
Jedynym problemem jest to, że może być konieczne wstawienie; w celu oddzielenia poleceń. Sam używam tej metody do pracy w systemach AIX, Sun, HP i Linux.
Tanktalus
Tanktalus, dzięki za ten komentarz, dzięki temu mój skrypt zadziałał. Na moim komputerze eval nie oddziela poleceń w nowych wierszach, a używanie kodu źródłowego nie działa nawet ze średnikami. Rozwiązaniem jest wartość Eval z średnikami. Chciałbym móc podać kilka punktów.
Milan Babuškov
2
@ MilanBabuškov: Umieściłem go w rankingu ;-)
Phil
3
To jest doskonałe. Jednak w moim przypadku użycia musiałem umieścić cudzysłowy wokół mojej $ (), w ten sposób: eval "$( ssh remote-host 'cat ~/.bash_profile' )"Zauważ, że rzeczywiście używam basha 3.2.
Zachary Murray
3
To działa dla mnie tam, gdzie zaakceptowana odpowiedź nie.
Dan Tenenbaum
37

Wow, wiem, że to stare pytanie, ale ostatnio napotkałem ten sam problem (tak się tutaj dostałem).

Zresztą - odpowiedź mi się nie podoba source /dev/stdin, ale myślę, że znalazłem lepszą. W rzeczywistości jest to zwodniczo proste:

echo ls -la | xargs xargs

Fajnie, prawda? Właściwie to nadal nie robi tego, co chcesz, ponieważ jeśli masz wiele wierszy, połączy je w jedno polecenie zamiast uruchamiać każde polecenie osobno. Tak więc rozwiązanie, które znalazłem, to:

ls | ... | xargs -L 1 xargs

-L 1opcja oznacza użyć (co najwyżej) 1 linia na wykonanie polecenia. Uwaga: jeśli twoja linia kończy się spacją na końcu, zostanie połączona z następną linią! Dlatego upewnij się, że każda linia kończy się spacją.

Wreszcie możesz to zrobić

ls | ... | xargs -L 1 xargs -t

aby zobaczyć, jakie polecenia są wykonywane (-t jest gadatliwe).

Mam nadzieję, że ktoś to przeczyta!

rabensky
źródło
30

Spróbuj użyć podstawiania procesu , które zastępuje wynik polecenia plikiem tymczasowym, z którego można następnie pobrać:

source <(echo id)
rishta
źródło
7
Jaki to rodzaj czarów?
paulotorrens
To też była moja pierwsza myśl. Chociaż rozwiązanie eval podoba mi się bardziej. @PauloTorrens <(xyz) po prostu wykonuje xyz i zastępuje <(xyz) nazwą pliku, który zapisze dane wyjściowe xyz. Naprawdę łatwo jest zrozumieć, jak to działa, wykonując na przykład: echo <(echo id)podając wynik /dev/fd/12(12 to przykład) cat <(echo id), podając wynik id, a następnie source <(echo id)podając ten sam wynik, co po prostu napisanieid
netigger
2
@PauloTorrens, nazywa się to zastępowaniem procesów . Zobacz powiązane dokumenty, aby uzyskać oficjalne wyjaśnienia, ale krótka odpowiedź jest taka, że ​​„<()” to specjalna składnia, która została zaprojektowana z myślą o takich przypadkach.
Mark Stosberg,
1
@MarkStosberg, czy wiesz, czy jest to specjalna składnia, czy tylko (...)przekierowana podpowłoka ?
paulotorrens
2
@PauloTorrens, jest to specjalna składnia służąca tylko do podstawiania procesów.
Mark Stosberg
6

Uważam, że to „właściwa odpowiedź” na pytanie:

ls | sed ... | while read line; do $line; done

Oznacza to, że można podłączyć rurę do whilepętli; readkomend zajmuje jedną linię z jego stdini przypisuje go do zmiennej $line. $linenastępnie staje się poleceniem wykonywanym w pętli; i kontynuuje, dopóki nie będzie żadnych dalszych wierszy na wejściu.

To nadal nie zadziała z niektórymi strukturami sterującymi (takimi jak inna pętla), ale w tym przypadku pasuje do rachunku.

Geoff Nixon
źródło
5
`ls | sed ...`

Wydaje mi się, że ls | sed ... | source -byłoby ładniej, ale niestety sourcenie rozumiem, co -to znaczy stdin.

chaos
źródło
po zobaczeniu odpowiedzi mark4o, czy nie wydaje się, że przez cały ten czas było to właściwe na naszych twarzach?
kch
Heh, yeah. Nigdy nie pamiętam, że takie rzeczy istnieją.
chaos
1

Myślę, że twoim rozwiązaniem jest zastępowanie poleceń lewymi znakami: http://tldp.org/LDP/Bash-Beginners-Guide/html/sect_03_04.html

Patrz sekcja 3.4.5

Eric Wendelin
źródło
3
Jeśli używasz basha, $( )preferowana może być składnia.
`` Cykle kursora
6
$ (polecenie) i $ ((1 + 1)) działają we wszystkich powłokach posix. Myślę, że wielu jest zniechęconych przez vim oznaczający je jako błędy składniowe, ale to tylko dlatego, że vim podświetla oryginalną powłokę Bourne'a, której bardzo niewielu używa. Aby vim poprawnie podświetlał, umieść to w swoim .vimrc: niech g: is_posix = 1
pixelbeat
1

Aby użyć rozwiązania mark4o w bash 3.2 (macos), zamiast potoków, jak w tym przykładzie, można użyć ciągu tutaj:

. /dev/stdin <<< "$(grep '^alias' ~/.profile)"
ish-west
źródło
lub użyj grawisów:. / dev / stdin <<< `grep '^ alias' ~ / .profile`
William H. Hooper
0

Dlaczego więc nie użyć source?

$ ls | sed ... > out.sh ; source out.sh
Kaleb Pederson
źródło
Wspomniał, że pliki tymczasowe są obrzydliwe.
chaos
O tak, przegapiłem to :(.
Kaleb Pederson