วิธีการส่งค่าของตัวแปรไปยัง stdin ของคำสั่ง?


105

ฉันกำลังเขียนเชลล์สคริปต์ที่ควรปลอดภัยเช่นไม่ส่งผ่านข้อมูลที่ปลอดภัยผ่านพารามิเตอร์ของคำสั่งและไม่ควรใช้ไฟล์ชั่วคราว ฉันจะส่งตัวแปรไปยัง stdin ของคำสั่งได้อย่างไร หรือถ้าเป็นไปไม่ได้จะใช้ไฟล์ชั่วคราวสำหรับงานดังกล่าวได้อย่างไร?


ผู้โจมตีสามารถเปลี่ยนแปลงได้$PATHหรือไม่? ที่catสามารถเปลี่ยนได้/bin/cat "$@" | tee /attacker/can/read/this/file
12431234123412341234123

คำตอบ:


74

สิ่งที่ง่ายพอ ๆ กับ:

echo "$blah" | my_cmd

7
ระวังช่องว่างในตัวแปรของคุณ: echo "$blah"ดีกว่า
Jonathan Leffler

13
มาดูกันว่าคุณจัดการblah=-nอย่างไรblah=-e... ใช้printfแทนกัน unix.stackexchange.com/questions/65803/…
Camilo Martin

1
ดูเหมือนว่ามันจะเปราะบางและเกิดข้อผิดพลาดได้ง่ายใช่ไหม
ThorSummoner

20
6 ปีต่อไปถ้าฉันสามารถลบคำตอบนี้ได้ (แต่ทำไม่ได้เพราะได้รับการยอมรับแล้ว ... )
Oliver Charlesworth

1
@OliverCharlesworth ทำไมไม่แก้ไขให้ใช้งานได้printf '%s\n' "$blah"ล่ะ? ด้วยการเปลี่ยนแปลงเพียงครั้งเดียวนั้น (หลีกเลี่ยงข้อผิดพลาดมากมายในechoข้อกำหนดของสเปคซึ่งคำตอบที่ยอดเยี่ยมของ Stephane ได้ลงรายละเอียดไว้ที่Why is printfbetter than echo? on Unix & Linuxหรือที่ส่วน APPLICATION USAGE ของสechoเปคสัมผัสในช่วงสั้น ๆ ) มันสมบูรณ์แบบ คำตอบที่สมเหตุสมผล
Charles Duffy

192

การส่งผ่านค่าไปstdinในbashนั้นทำได้ง่ายเพียงแค่:

your-command <<< "$your_variable"

ตรวจสอบให้แน่ใจเสมอว่าคุณใส่เครื่องหมายคำพูดรอบนิพจน์ตัวแปร!

ระมัดระวังว่านี่อาจจะทำงานเฉพาะในและจะไม่ทำงานในbashsh


39
<<<ไม่รับประกันว่าHerestrings จะพร้อมใช้งาน แต่ไม่ได้อยู่ในฐาน POSIX เท่าที่ฉันรู้ พวกเขาจะทำงานตามที่คาดไว้ตราบเท่าที่คุณใช้งานbashได้ ฉันพูดถึงมันเท่านั้นเพราะพวกเขา OP บอกว่า 'shell' ไม่ใช่เฉพาะ 'bash' แม้ว่าเขาจะติดแท็กคำถามด้วยbash... ดังนั้นนี่จึงเป็นคำตอบที่ยอมรับได้อย่างชัดเจน
JM Becker

ในขณะนี้ข้อมูลที่ละเอียดอ่อนมีแนวโน้มที่จะรั่วไหลได้ง่ายขึ้นหากใช้echoวิธีนี้เนื่องจากการสร้างท่อ คำสั่งต่อไปนี้จะได้รับการประมวลผลทั้งหมดโดยbashแม้ว่าในสถานการณ์นี้ (ตามที่ระบุไว้) คุณจะรู้ดีกว่าว่ามันจะใช้งานได้กับ bash ของคุณมิฉะนั้นข้อความแสดงข้อผิดพลาดใด ๆ ที่เกิดจากการไม่รองรับสตริงที่นี่ก็จะทำให้ข้อมูลดังกล่าวรั่วไหลเช่นกัน
Steven Lu

@StevenLu Wait echoเป็นบิ้วอิน. ไม่มีท่อใช่มั้ย? เป็น Unix แต่ไม่สามารถเป็นUnix ได้มากขนาดนั้น
Camilo Martin

4
@StevenLu printf '%s\n' "$var"ให้ผลลัพธ์เช่นเดียวกับecho "$var"แต่จะไม่แตกถ้าเช่นvar=-enและไม่จำเป็นต้องทุบตี
Camilo Martin

1
โปรดทราบด้วยว่าบรรทัดใหม่จะต่อท้ายสตริงสำหรับสตริงที่นี่
pdr

19

โปรดทราบว่าการecho "$var" | commandดำเนินการ' หมายความว่าอินพุตมาตรฐานถูก จำกัด ไว้ที่บรรทัดที่สะท้อน หากคุณต้องการให้เชื่อมต่อเทอร์มินัลด้วยคุณจะต้องเป็นคนที่เพ้อฝันมากขึ้น:

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

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

ซึ่งหมายความว่าบรรทัดแรกจะเป็นเนื้อหา$varแต่ส่วนที่เหลือจะมาจากcatการอ่านอินพุตมาตรฐาน หากคำสั่งไม่ได้ทำอะไรที่แปลกเกินไป (ลองเปิดการแก้ไขบรรทัดคำสั่งหรือเรียกใช้เช่นvimนั้น) ก็จะไม่เป็นไร มิฉะนั้นคุณจะต้องจินตนาการจริงๆ - ฉันคิดว่าexpectหรืออนุพันธ์อย่างใดอย่างหนึ่งน่าจะเหมาะสม

สัญกรณ์บรรทัดคำสั่งแทบจะเหมือนกัน - แต่เซมิโคลอนที่สองจำเป็นสำหรับการจัดฟันในขณะที่ไม่ได้อยู่ในวงเล็บ


( echo "$LIST"; cat - ) | sed 1qสิ่งนี้ใช้ได้กับฉัน แต่ฉันต้องกด ctrl d เมื่อฉันเรียกใช้สคริปต์นี้?
Gert Cuykens

ใช่; cat -ยังคงอ่านจากแป้นพิมพ์จนกว่า EOF หรือขัดจังหวะดังนั้นคุณจึงจำเป็นที่จะบอกว่ามัน EOF โดยพิมพ์ควบคุม-D
Jonathan Leffler

มีวิธีรอบ ๆ EOF หรือไม่? sed ต้องการแมว
Gert Cuykens

คุณไม่จำเป็นต้องใช้catเลยหากคุณไม่ต้องการอินพุตเทอร์มินัลเหมือนในบรรทัดแรก หรือคุณสามารถใช้catเพื่อแสดงรายการไฟล์ หรือ ... ถ้าคุณต้องการให้คำสั่งอ่านอินพุตเทอร์มินัลคุณต้องบอกเมื่อมันถึงจุดสิ้นสุดของอินพุต หรือคุณสามารถใช้( echo "$var"; sed /quit/q - ) | command; สิ่งนี้จะดำเนินต่อไปจนกว่าคุณจะพิมพ์บรรทัดที่มีคำว่า "ออก" คุณสามารถสร้างสรรค์ได้ไม่รู้จบด้วยวิธีจัดการ ระวังตำนานเมืองเก่าของโปรแกรมที่หยุดทำงานเมื่อผู้ใช้เริ่มทำงานกับเอกวาดอร์ พวกเขาจะพิมพ์ชื่อเมืองหลวงกีโตและโปรแกรมออก
Jonathan Leffler

ตกลงถ้าคุณพูดอย่างนั้น. แต่ทำไมไม่เพียงecho "$LIST" | sed 1q | ...? ทุกอย่างขึ้นอยู่กับว่าคุณเกี่ยวกับอะไร <<EOFสัญกรณ์ควรจะต้องมีการ EOF ในช่วงเริ่มต้นของบรรทัดในที่ใดที่หนึ่งของตัวเองลงแฟ้ม ฉันไม่ได้ใช้มันฉันคิดว่า - แต่ฉันยังไม่แน่ใจว่าคุณกำลังพยายามทำอะไรอยู่ วิธีง่ายๆecho "$LIST" | commandหรือecho $LIST | commandอาจจะพอเพียงในทางปฏิบัติ (และเป็นสิ่งสำคัญที่คุณต้องรู้ถึงความสำคัญของความแตกต่างระหว่างทั้งสอง)
Jonathan Leffler

16

ฉันชอบคำตอบของมาร์ติน แต่มีปัญหาบางอย่างขึ้นอยู่กับสิ่งที่อยู่ในตัวแปร นี้

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

จะดีกว่าถ้าคุณมีตัวแปร "หรือ!


4
แต่ทำไม? นอกจากนี้ฉันไม่สามารถทำซ้ำปัญหาใด ๆ ได้: foo1=-; foo2='"'; foo3=\!; cat<<<"$foo1"; cat<<<"$foo2"; cat<<<"$foo3"ใช้ได้ดีสำหรับฉัน ทั้งสามคน"ทำอะไรกันแน่? AFAIK คุณเป็นเพียงการเติมเงินล่วงหน้าและต่อท้ายสตริงว่าง
phk

ลองของจริง เช่นเดียวกับคำสั่งของคุณคือ ssh somehost และตัวแปรของคุณคือเชลล์สคริปต์
Robert Jacobs

16
(cat <<END
$passwd
END
) | command

catไม่จำเป็นจริงๆ แต่มันจะช่วยให้โครงสร้างรหัสที่ดีขึ้นและช่วยให้คุณสามารถใช้คำสั่งเพิ่มเติมในวงเล็บเป็น input เพื่อคำสั่งของคุณ


แต่วิธีนี้ช่วยให้คุณส่งผ่านหลายบรรทัดไปยังคำสั่งของคุณและยังอนุญาตให้มีช่องว่างใน$passwd
PoltoS

4
นี่เป็นคำตอบที่ดีที่สุดที่จะไม่ทำให้เนื้อหาตัวแปรรั่วไหลไปยังท่อหรือกระบวนการสอดแนม
user2688272

8

ตามคำตอบของมาร์ตินมีคุณลักษณะทุบตีที่เรียกว่าHere Strings (ซึ่งตัวเองเป็นตัวแปรของคุณสมบัติHere Documents ที่ได้รับการสนับสนุนอย่างกว้างขวางมากขึ้น)

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

3.6.7 นี่คือสตริง

รูปแบบของเอกสารที่นี่รูปแบบคือ:

<<< word

คำถูกขยายและป้อนให้กับคำสั่งในอินพุตมาตรฐาน

โปรดทราบว่า Here Strings ดูเหมือนจะเป็นการทุบตีเท่านั้นดังนั้นเพื่อความสะดวกในการพกพาที่ดีขึ้นคุณอาจจะดีกว่าด้วยคุณสมบัติ Here Documents ดั้งเดิมตามคำตอบของ PoltoS:

( cat <<EOF
$variable
EOF
) | cmd

หรือตัวแปรที่ง่ายกว่าจากข้างต้น:

(cmd <<EOF
$variable
EOF
)

คุณสามารถละเว้น(และ)เว้นแต่คุณต้องการให้สิ่งนี้เปลี่ยนเส้นทางไปยังคำสั่งอื่น ๆ เพิ่มเติม


5

วิธีที่ทนทานและพกพาได้นี้ได้ปรากฏในความคิดเห็น ควรเป็นคำตอบแบบสแตนด์อโลน

printf '%s' "$var" | my_cmd

หรือ

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

หมายเหตุ:

  • มันดีกว่าechoเหตุผลอยู่ที่นี่ทำไมprintfดีกว่าecho?
  • printf "$var"มันผิด. อาร์กิวเมนต์แรกเป็นรูปแบบที่ลำดับต่าง ๆ ชอบ%sหรือ\nมีการตีความ ในการส่งผ่านตัวแปรไปทางขวาจะต้องไม่ตีความเป็นรูปแบบ
  • โดยปกติตัวแปรจะไม่มีบรรทัดใหม่ต่อท้าย คำสั่งเดิม (with %s) ส่งผ่านตัวแปรตามที่เป็นอยู่ อย่างไรก็ตามเครื่องมือที่ทำงานกับข้อความอาจเพิกเฉยหรือบ่นเกี่ยวกับบรรทัดที่ไม่สมบูรณ์ (ดูเหตุใดไฟล์ข้อความจึงควรลงท้ายด้วยขึ้นบรรทัดใหม่ ) ดังนั้นคุณอาจต้องการคำสั่งหลัง (with %s\n) ซึ่งต่อท้ายอักขระขึ้นบรรทัดใหม่ในเนื้อหาของตัวแปร ข้อเท็จจริงที่ไม่ชัดเจน:

    • สตริงใน Bash ( <<<"$var" my_cmd) ต่อท้ายบรรทัดใหม่
    • วิธีการใด ๆ ที่ต่อท้ายบรรทัดใหม่จะทำให้ได้ stdin ของ non-empty my_cmdแม้ว่าตัวแปรจะว่างเปล่าหรือไม่ได้กำหนดไว้ก็ตาม

4

ลองสิ่งนี้:

echo "$variable" | command

แต่จะไม่แสดงเนื้อหา $ ตัวแปรเช่นเอาต์พุตps -uเมื่อ echo กำลังทำงานอยู่หรือไม่?

2
ไม่มันจะไม่ echoเป็นแบบในตัวดังนั้นจึงไม่มีกระบวนการที่จะแสดงใน ps
เชื่อ

2
ระวังช่องว่างในตัวแปรของคุณ: echo "$variable"ดีกว่า
Jonathan Leffler

ถูกต้องทุบตีจะกินช่องว่างสองเท่าหอยที่ฉลาดกว่าจะไม่
เชื่อ

-1

แค่ทำ:

printf "$my_var" | my_cmd

หาก var ไม่มีช่องว่างเครื่องหมายคำพูดอาจถูกละเว้น
หากใช้ bash คุณสามารถทำได้:

echo -n "$my_var" | my_cmd

หลีกเลี่ยงการใช้เสียงสะท้อนที่ไม่มี -n เพราะมันจะไปป์ vraiable พร้อมกับ linebreak ที่เพิ่มเข้ามาในตอนท้าย


1
ไม่ทำงานถ้า $ my_var คือ%sprintf จะไม่พิมพ์อะไรเลย my_var="%s"; printf "$my_var"; - อาจจะลองprintf "%s" "$my_var" | my_cmd ?
hanshenrik
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.