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?


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

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.


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.


( 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!


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.


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ń.


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 \nsą interpretowane . 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.

4

Spróbuj tego:

echo "$variable" | command

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


-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.


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
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.