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.


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.


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.


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!


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)

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.


5
`ls | sed ...`

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


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


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)"

lub użyj grawisów:. / dev / stdin <<< `grep '^ alias' ~ / .profile`
William H. Hooper

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.