Jak przekazać wartość zmiennej na stdin polecenia?

105

Piszę skrypt powłoki, który powinien być nieco bezpieczny, tj. Nie przekazuje bezpiecznych danych przez parametry poleceń i najlepiej nie używa plików tymczasowych. Jak mogę przekazać zmienną do wejścia standardowego polecenia? A jeśli nie jest to możliwe, jak poprawnie wykorzystać pliki tymczasowe do takiego zadania?

Jonathan Leffler
źródło
Czy atakujący może się zmienić $PATH? Więc catmożna to wymienić/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

Odpowiedzi:

74

Coś tak prostego, jak:

echo "$blah" | my_cmd
Oliver Charlesworth
źródło
7
Uważaj na spacje w zmiennej: echo "$blah"jest lepsze.
Jonathan Leffler
13
Zobaczmy, jak sobie poradzić blah=-n, blah=-e... stosowanie printfzamiast. unix.stackexchange.com/questions/65803/…
Camilo Martin
1
wydaje się, że byłoby delikatne i podatne na błędy, nie?
ThorSummoner
20
6 lat później, gdybym mógł usunąć tę odpowiedź, zrobiłbym to (ale nie mogę, ponieważ została zaakceptowana ...)
Oliver Charlesworth
1
@OliverCharlesworth, dlaczego nie edytować go, aby użyć printf '%s\n' "$blah"? Z tą jedną zmianą (unikając wiele pułapek w echo„s specyfikacja, która doskonała odpowiedź Stephane idzie się szczegółowo na Dlaczego printflepiej echo? Na Unix i Linux , lub którym sekcja WNIOSEK korzystanie z echospecyfikacją dotyka więcej krótko) to doskonale rozsądna odpowiedź.
Charles Duffy,
195

Przekazywanie wartości stdinw bash jest tak proste, jak:

your-command <<< "$your_variable"

Zawsze upewnij się, że wokół wyrażeń zmiennych umieszczasz cudzysłowy!

Uważaj, bo to prawdopodobnie zadziała tylko bashi nie zadziała sh.

Jaskółka oknówka
źródło
39
Nie <<<ma gwarancji, że Herestrings będą dostępne, o ile wiem, nie ma ich w bazie POSIX. Będą działać zgodnie z oczekiwaniami, o ile tylko je uruchomisz bash. Wspominam o tym tylko dlatego, że OP powiedzieli „shell”, a nie konkretnie „bash”. Chociaż oznaczył pytanie tagiem bash... więc nadal jest to oczywiście akceptowalna odpowiedź.
JM Becker
Chociaż może się tak zdarzyć, wrażliwe informacje prawdopodobnie łatwiej wyciekną, jeśli użyje się tej echometody ze względu na utworzenie rury. Polecenie herestring zostanie przetworzone w całości przez bash, chociaż w tej sytuacji (jak wspomniano) lepiej wiedzieć, że będzie działać na twoim bash, w przeciwnym razie każdy komunikat o błędzie powstały w wyniku nieobsługiwania tutaj ciągów również wyciekłby wspomniane informacje.
Steven Lu
@StevenLu Wait, echojest wbudowany. Nie ma tam rury, prawda? To Unix, ale to nie może być tak dużo Unix .
Camilo Martin
4
@StevenLu printf '%s\n' "$var"daje takie same wyniki, jak, echo "$var"ale nie zepsuje się , np. var=-en, I nawet nie wymaga basha.
Camilo Martin
2
Zauważ również, że znak nowej linii jest dołączany do ciągu dla łańcuchów tutaj.
pdr
19

Zauważ, że echo "$var" | commandoperacja ' oznacza, że ​​standardowe wejście jest ograniczone do linii, której (-ych) echo (-ych) zawiera echo. Jeśli chcesz, aby terminal był podłączony, musisz być bardziej wyrafinowany:

{ echo "$var"; cat - ; } | command

( echo "$var"; cat -   ) | command

Oznacza to, że pierwsza linia (wiersze) będzie zawartością, $varale reszta będzie pochodzić z catodczytu jego standardowego wejścia. Jeśli polecenie nie robi nic zbyt wymyślnego (spróbuj włączyć edycję wiersza poleceń lub uruchom tak, jak vimrobi), to będzie dobrze. W przeciwnym razie musisz naprawdę polubić - myślę, że expectlub jedna z jego pochodnych może być odpowiednia.

Notacje wiersza poleceń są praktycznie identyczne - ale drugi średnik jest konieczny w nawiasach klamrowych, a nie w nawiasach.

Jonathan Leffler
źródło
( echo "$LIST"; cat - ) | sed 1qto działa dla mnie, ale muszę nacisnąć ctrl d po uruchomieniu tego skryptu?
Gert Cuykens
Tak; cat -nadal czytać z klawiatury aż do EOF lub przerwać, więc trzeba powiedzieć to EOF wpisując control-D.
Jonathan Leffler
czy jest sposób na obejście EOF? sed potrzebuje kota
Gert Cuykens
Nie musisz catw ogóle używać, jeśli nie chcesz wprowadzania danych przez terminal, jak w pierwszej linii. Lub możesz użyć catdo wyświetlenia pliku. Lub ... Jeśli chcesz, aby polecenie odczytywało wejście terminala, musisz powiedzieć mu, kiedy osiągnęło koniec wejścia. Lub możesz użyć ( echo "$var"; sed /quit/q - ) | command; trwa to do momentu wpisania wiersza zawierającego „quit”. Możesz być nieskończenie pomysłowy, jak sobie z tym poradzisz. Uważaj na starą miejską legendę o programie, który przestał działać, gdy użytkownicy zaczęli pracować z Ekwadorem. Wpisywali nazwę stolicy, Quito, i program kończył pracę.
Jonathan Leffler,
Ok, jeśli tak mówisz. Ale dlaczego nie tylko echo "$LIST" | sed 1q | ...? Wszystko zależy od tego, o co ci chodzi. <<EOFZapis powinien wymagać EOF na początku linii na własnej gdzieś pliku. Myślę, że nie użyłbym tego - ale nadal nie jestem pewien, co próbujesz osiągnąć. Prosty echo "$LIST" | commandlub echo $LIST | commandprawdopodobnie wystarczy w praktyce (ważne jest, abyś wiedział, jaka jest różnica między nimi).
Jonathan Leffler
16

Podobała mi się odpowiedź Martina, ale ma pewne problemy w zależności od tego, co jest w zmiennej. To

your-command <<< """$your_variable"""

jest lepsze, jeśli zmienna zawiera „lub!

Robert Jacobs
źródło
4
Ale dlaczego? Ponadto nie mogę odtworzyć żadnych problemów: foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"dla mnie działa dobrze. Co dokładnie robi ta trójka "? AFAIK po prostu dodajesz i dołączasz pusty ciąg.
phk
Spróbuj czegoś prawdziwego. Jak twoja komenda to ssh somehost. a twoja zmienna to skrypt powłoki.
Robert Jacobs
16
(cat <<END
$passwd
END
) | command

Nie catjest to naprawdę potrzebne, ale pomaga w lepszej strukturze kodu i umożliwia użycie większej liczby poleceń w nawiasach jako danych wejściowych do polecenia.

PoltoS
źródło
Ale ta metoda pozwala na przekazanie wielu wierszy do twojego polecenia, a także zezwala na spacje w$passwd
PoltoS
4
Jak dotąd jest to najlepsza odpowiedź, która nie powoduje wycieku zmiennej zawartości do potoków lub podsłuchiwania procesów.
user2688272
8

Zgodnie z odpowiedzią Martina istnieje funkcja bash o nazwie Here Strings (która sama w sobie jest wariantem szerzej obsługiwanej funkcji Here Documents ).

http://www.gnu.org/software/bash/manual/bashref.html#Here-Strings

3.6.7 Tutaj Ciągi

Odmiana tutaj dokumentów, format:

<<< word

Słowo jest interpretowane i dostarczane do polecenia na jego standardowym wejściu.

Zauważ, że Here Strings wydaje się być tylko bashem, więc dla lepszej przenośności prawdopodobnie lepiej byłoby, gdybyś miał oryginalną funkcję Here Documents, zgodnie z odpowiedzią PoltoS:

( cat <<EOF
$variable
EOF
) | cmd

Lub prostszy wariant powyższego:

(cmd <<EOF
$variable
EOF
)

Możesz pominąć (i ), chyba że chcesz, aby to było dalej przekierowywane do innych poleceń.

cnst
źródło
5

Ten solidny i przenośny sposób pojawił się już w komentarzach. Powinna to być samodzielna odpowiedź.

printf '%s' "$var" | my_cmd

lub

printf '%s\n' "$var" | my_cmd

Uwagi:

  • To lepsze niż echo, powody są tutaj: Dlaczego jest printflepsze niż echo?
  • printf "$var"jest źle. Pierwszym argumentem jest format, w którym różne sekwencje, takie jak %slub \ninterpretowane . Aby przekazać zmienną w prawo, nie można jej interpretować jako formatu.
  • Zwykle zmienne nie zawierają końcowych znaków nowej linii. Poprzednie polecenie (z %s) przekazuje zmienną taką, jaka jest. Jednak narzędzia, które pracują z tekstem, mogą ignorować lub narzekać na niekompletną linię (zobacz Dlaczego pliki tekstowe powinny kończyć się znakiem nowej linii? ). Więc możesz chcieć tego ostatniego polecenia (z %s\n), które dodaje znak nowego wiersza do zawartości zmiennej. Nieoczywiste fakty:

    • Tutaj ciąg w Bash ( <<<"$var" my_cmd) dołącza nową linię.
    • Każda metoda, która dołącza znak nowego wiersza, skutkuje niepustym stdin of my_cmd, nawet jeśli zmienna jest pusta lub niezdefiniowana.
Kamil Maciorowski
źródło
4

Spróbuj tego:

echo "$variable" | command
unbeli
źródło
ale czy zawartość zmiennej $ nie pojawiłaby się np. w wyniku działania ps -uecha?
2
nie, nie będzie. echojest wbudowany, więc nie ma procesu do wyświetlenia w ps
unbeli
2
Uważaj na spacje w zmiennej: echo "$variable"jest lepsze.
Jonathan Leffler
Zgadza się
-1

Po prostu zrób:

printf "$my_var" | my_cmd

Jeśli zmienna nie zawiera spacji, cudzysłowy można pominąć.
Jeśli używasz basha, możesz również:

echo -n "$my_var" | my_cmd

Unikaj używania echo bez -n, ponieważ spowoduje to potokowanie vraiable z dodanym podziałem wiersza na końcu.

Clox
źródło
1
nie działa jeśli $ my_var to %s, printf niczego nie wydrukuje. my_var="%s"; printf "$my_var"; - może spróbować printf "%s" "$my_var" | my_cmd ?
hanshenrik