วิธีรันคำสั่งไลบรารี่จากเชลล์?


27

ฉันต้องการคำนวณความยาวของสตริง (นั่นคือค่าแฮช) ดังนั้นฉันเปิดเทอร์มินัลและทำสิ่งนี้:

$ apropos length

ที่ส่งคืนฉันด้วยพวงของคำสั่ง / ฟังก์ชั่นที่มี(3)หรือ(3ssl)ผนวกท้ายของพวกเขา ตอนนี้ผู้ชายคนให้ข้อมูลเกี่ยวกับความsection numbersหมายเหล่านี้ให้เรา

3   Library calls (functions within program libraries)

ด้วยความอยากรู้ฉันเพิ่งลองใช้คำสั่งเหล่านี้ทั้งหมด (หวังว่าอย่างน้อยจะใช้ได้)

strcspn (3)          - get length of a prefix substring
strlen (3)           - calculate the length of a string
strnlen (3)          - determine the length of a fixed-size string
strspn (3)           - get length of a prefix substring
wcslen (3)           - determine the length of a wide-character string
wcsnlen (3)          - determine the length of a fixed-size wide-character string

และไม่มีอะไรนอกจากข้อผิดพลาดเดียวกันสำหรับทุกคำสั่ง

$ strnlen HelloWorld 
$ strnlen: command not found

ดีฉันรู้วิธีการค้นหาความยาวของสตริงในเปลือกใช้wc -m, expr lengthและการแก้ไขปัญหาอื่น ๆ

แต่ฉันมี 2 คำถามที่นี่:

  1. วิธีการใช้งานใด ๆlibrary calls (3)ภายในเปลือก?
  2. วิธีการคำนวณความยาวสตริงโดยใช้แค่การเรียกไลบรารี่และไม่ใช่คำสั่งอื่น?

หมายเหตุ: คำถามเน้นทั่วไปlibrary callsและการใช้งานในเชลล์ นั่นทำให้คำถามแรกสำคัญกว่าที่จะตอบ


stackoverflow.com/q/49719554/841108เป็นสำเนาที่ใกล้เคียงกัน
Basile Starynkevitch

4
โดยพื้นฐานแล้วแม้ว่าคำตอบของ Michael จะเป็นแฮ็คที่ดี แต่คำตอบที่ตรงไปตรงมาก็คือคุณไม่ การเรียกไลบรารี่ไม่เกี่ยวข้องกับเชลล์ พวกเขาจะใช้สำหรับการเขียนโปรแกรมในภาษา C - โดยทั่วไปแล้วเมื่ออ่าน manpages ยกเว้นว่าคุณกำลังเขียนโปรแกรมอยู่ให้ละเว้นหัวข้อ 2, 3 และ 9
spectra

ปิดหัวข้อ แต่ OpenVMS มีฟังก์ชั่น "คำศัพท์" ซึ่งเป็นส่วนต่อประสานเชลล์กับฟังก์ชั่นไลบรารีระบบจำนวนมาก
RonJohn

คำตอบ:


39

คุณอาจไม่ควรทำสิ่งนี้ แต่ทำได้ คำตอบของ Kusalanandaดีกว่าสำหรับงานในมือและอธิบายปัญหา เนื่องจากคุณถามวิธีการใช้การเรียกใช้ไลบรารีใด ๆภายในเทอร์มินัลโดยเฉพาะนี่คือบางวิธี ...


จิ๋ว C คอมไพเลอร์ ( tcc) สนับสนุน-runธงที่ช่วยให้คุณ (ในผล) ตีความรหัส C โดยการเขียนโปรแกรมขนาดเล็กเพื่อให้คุณสามารถใช้โทรห้องสมุดใด ๆ ภายในอาคารผู้โดยสารผ่านการภาวนาเดียวว่า

คุณสามารถเรียกใช้strnlenฟังก์ชันดังนี้:

$ tcc -run <(echo '#include <stdio.h>'; echo '#include <string.h>'; echo 'void main(int argc, char **argv) {printf("%i\n", strnlen(argv[1], 1024));}') "Hello world"
11

ใช้การทดแทนกระบวนการจาก Bash, zsh และเชลล์อื่น ๆ ที่จะให้tccไฟล์อ่านที่ดูเหมือนจะมีผลลัพธ์ของechos ทั้งหมด มีตัวเลือกอื่น ๆ

คุณสามารถสร้างฟังก์ชั่นเพื่อสร้างสิ่งนี้สำหรับคุณ:

call_library_function_s_i() {
    func=$1
    shift
    tcc -run <(echo '#include <stdio.h>'; echo '#include <string.h>'; echo 'void main(int argc, char **argv) {printf("%i\n", '$func'(argv[1]));}') "$*"
}
$ call_library_function_s_i strlen hello world

(ฉันเคยใช้strlenที่นี่เพื่อที่จะเป็นสตริงฟังก์ชัน unary-> int - คุณต้องการฟังก์ชันแยกต่างหากสำหรับ arity และประเภทการส่งคืนที่แตกต่างกัน)


ตัวเลือกหนึ่งคือปลั๊กอินทุบตีโดย Tavis Ormandy ซึ่ง wraps ขึ้นและ นี่อาจเป็นการประมาณที่ใกล้เคียงที่สุดกับสิ่งที่คุณพยายาม คุณสามารถใช้ตัวอย่างเช่น:ctypes.shdlopendlsym

$ dlcall -r uint64 strlen "hello world"

และจะเรียกใช้ฟังก์ชันตามที่คาดไว้

นี่เป็นวิธีที่ตรงที่สุดในการทำ "จากเทอร์มินัล" แต่ไม่น่าจะเป็นแพคเกจการแจกจ่ายของคุณดังนั้นคุณต้องติดตั้งด้วยตนเอง (ซึ่งไม่สำคัญ) ต่อไปนี้เป็นคำพูดที่ให้ctypes.shข้อมูลจากเว็บไซต์ของตัวเองเพื่อสร้างความประทับใจโดยทั่วไปว่าผู้คนรู้สึกอย่างไรกับการทำสิ่งนี้:

  • "นั่นน่าขยะแขยง"
  • "นี่ต้องหยุด"
  • "คุณไปไกลเกินไปกับเรื่องนี้"

อาจมีเครื่องมือที่คล้ายกันสำหรับหอยอื่น ๆ แต่ฉันไม่รู้เกี่ยวกับพวกเขา ในทางทฤษฎีไม่มีเหตุผลที่จะไม่มีคำสั่งแบบสแตนด์อโลนที่ทำสิ่งนี้อย่างแน่นอนสำหรับกรณีง่าย ๆ แต่ฉันค่อนข้างแปลกใจที่ฉันไม่สามารถหา ...


... ดังนั้นฉันจึงได้ทำ! dlcallให้คุณเรียกใช้ฟังก์ชันไลบรารีจากบรรทัดคำสั่ง :

$ dlcall strnlen "hello world" 6
$ dlcall sin 2.5
$ dlcall strchr "hello world" -c ' '

สนับสนุนชุดฟังก์ชันต้นแบบที่ จำกัด ซึ่งยังไม่น่าเชื่อถือหรือมีความยืดหยุ่นอย่างมากในขณะนี้ แต่ตอนนี้มันมีอยู่แล้ว


ตัวอย่างเช่นคุณสามารถใช้Python และpython -c 'import ctypes; import sys; print(ctypes.cdll.LoadLibrary("libc.so.6").strlen(" ".join(sys.argv[1:])))' hello worldแน่นอนว่าไม่ใช่วิธีที่ง่ายที่สุดในการใช้งาน Perl, Ruby และภาษาอื่น ๆ มีคุณสมบัติคล้ายกันที่คุณสามารถใช้ได้


ดังนั้นคำตอบสำหรับคำถามของคุณคือ:

  1. ใช้วิธีใดวิธีหนึ่งข้างต้น
  2. คุณไม่จำเป็นต้องใช้คำสั่งอื่นบูตคุณเข้าสู่ห้องสมุดหรือชิ้นส่วนของซอฟต์แวร์ที่ตะขอเข้าไปในเปลือกของคุณ

โดยรวมแล้วคุณจะทำได้ดีกว่าอย่างอื่น


2
"dlcall" ทำให้ฉันนึกถึง (ไม่เหมือนกันมาก) Windows "rundll": support.microsoft.com/en-gb/help/164787/…
pjc50

1
@ pjc50: ประกาศบริการสาธารณะ: rundll32 คือไม่ได้เป็นเครื่องมืออเนกประสงค์สำหรับการโทรฟังก์ชั่นโดยพลการ มันสามารถใช้ในการเรียกฟังก์ชั่นที่มีลายเซ็นที่กำหนด นอกจากนี้ก็ไม่ได้ชะมัดหลักฐานในอนาคต
เควิน

29

aproposคำสั่งจะเป็นประโยชน์ในหลาย ๆ ด้าน แต่ก็ไม่ให้คุณมากของ "ขยะ" มากเกินไป สิ่งที่คุณแสดงรายการส่วนใหญ่คือรูทีนไลบรารี C (นี่คือส่วนที่ 3 ของคู่มือใช้) ซึ่งคุณไม่สามารถใช้โดยตรงจากเชลล์

ในการใช้งานคุณจะต้องเขียนโปรแกรม C ที่เรียกพวกเขา สิ่งนี้อยู่นอกช่วงของหัวข้อที่ครอบคลุมโดยไซต์นี้โดยเฉพาะ (จะอยู่ในหัวข้อที่StackOverflow )

ดังนั้นสิ่งเหล่านี้คือคำตอบสำหรับคำถามของคุณ:

  1. คุณทำไม่ได้มันเป็นกิจวัตรไลบรารี่ C
  2. คุณทำไม่ได้ยกเว้นว่าคุณจะเขียนโปรแกรม C

ฉันรู้ว่าคุณรู้เรื่องนี้ แต่เพื่อความสมบูรณ์: ในเปลือกถ้าคุณมีสตริงในตัวแปรstringคุณอาจทำ

string='hello world'
printf 'Length of string "%s" is %d\n' "$string" "${#string}"

สิ่งนี้จะพิมพ์Length of string "hello world" is 11ในเทอร์มินัลที่11มาจาก${#string}ที่ขยายไปตามความยาวของสตริง$stringที่ขยายความยาวของสตริงใน

ภายในเชลล์อาจใช้หนึ่งในการเรียกไลบรารีที่คุณทำรายการเพื่อทำการคำนวณความยาว

นี่เป็นวิธีที่มีประสิทธิภาพมากที่สุดในการรับความยาวของสตริงที่เก็บไว้ในตัวแปรเชลล์ในเชลล์

โปรดทราบด้วยว่านั่น${#string}คือการขยายพารามิเตอร์เชลล์ POSIXดังนั้นจึงสามารถพกพาได้ระหว่างเชลล์ทั้งหมดที่อ้างถึงความสอดคล้องของ POSIX


ส่วนเกี่ยวกับฟังก์ชั่นห้องสมุดเป็นคำอธิบายที่ดี แต่ส่วนที่เหลือของคำตอบนี้เป็นความเข้าใจผิดเล็กน้อย OP บอกว่า"ฉันต้องการคำนวณความยาวของสตริง"ไม่จำเป็นต้องใช้printfที่นี่ หากคุณต้องการพิมพ์ออกมาเป็นส่วนหนึ่งของประโยคนั่นอาจเป็นวิธีที่ดีที่สุด แต่คำตอบสำหรับคำถามของ "ฉันจะได้ความยาวของสตริงได้อย่างไร" เป็น${#string}ส่วนหนึ่งไม่ใช่ printf คุณสามารถเพียงแค่echo ${#string}ถ้าคุณต้องการที่จะส่งออกเพียงแค่ความยาวหรือกำหนดให้กับตัวแปรอื่นด้วยstring_length=${#string}หรือสิ่งที่คุณต้องการ printf ไม่เกี่ยวข้องจริง ๆ
อดัม

1
@ อดัมฉันได้ทำการแก้ไขเล็กน้อยสำหรับคำตอบ ฉันคิดว่ามันค่อนข้างชัดเจนว่าจะรับความยาวของสตริงได้อย่างไรและผู้ใช้ได้บอกไปแล้วว่าพวกเขารู้วิธีที่จะทำ โดยการใช้ความยาวของสตริงด้วยprintfฉันเพิ่มสิ่งอื่นนอกเหนือจากเพียงแค่พูดว่า "ใช้${#string}" (ซึ่งเป็นตัวเองชัดเจนจากตัวอย่าง)
Kusalananda

15

ฉันจะไม่ทำเช่นนี้เพียงเพื่อstrlen()แต่เป็นเคล็ดลับที่มีประโยชน์สำหรับการลองใช้รหัส C ในบางครั้ง

user @ host: ~ $ gdb gdb
(gdb) เริ่ม
จุดพักชั่วคราว 1, ... ใน main ()
(gdb) strlen พิมพ์ ("foobar")
$ 1 = 6

นี่gdbคือตัวดีบัก GNU และโดยปกติจะใช้ชื่อโปรแกรมเพื่อทำการดีบั๊กหลังจากนั้น เนื่องจากเราไม่มีเลยตัวอย่างนี้ให้ตัวเองเพื่อดีบัก จากนั้นstartเริ่มต้นโปรแกรมและหลังจากนั้นgdbสามารถใช้เพื่อเรียกใช้รหัส C โดยพลการ


2
เคล็ดลับดีที่จะใช้ gdb ยัง gdb เป็นส่วนหนึ่งของเครื่องมือสำหรับนักพัฒนาและถ้าคุณต้องการใช้ฟังก์ชั่นห้องสมุดคุณควรเตรียมตัวเรียนรู้การใช้เครื่องมือสำหรับนักพัฒนา
Dudi Boy

4

เครื่องมือที่สามารถใช้เรียกฟังก์ชั่นการโต้ตอบในไลบรารีที่แชร์: "Witchcraft Compiler Collection" https://github.com/endrazine/wcc

จากหน้า GitHub:

wsh: เปลือกคาถา

เชลล์คาถานั้นยอมรับ ELF shared library, ELF ET_DYN executables และ Witchcraft Shell Scripts ที่เขียนใน Punk-C เป็นอินพุต มันจะโหลดโปรแกรมปฏิบัติการทั้งหมดในพื้นที่ที่อยู่ของตัวเองและทำให้ API ของพวกเขาพร้อมใช้งานสำหรับการเขียนโปรแกรมในล่ามที่ฝังตัว สิ่งนี้มีไว้สำหรับฟังก์ชันการทำงานของไบนารีที่คล้ายกับที่มีให้ในภาษาต่างๆเช่น Java

ตัวอย่างการใช้ wsh คำสั่งต่อไปนี้โหลด/usr/sbin/apache2ไฟล์เรียกทำงานภายใน wsh เรียกใช้ap_get_server_banner()ฟังก์ชันภายใน apache เพื่อดึงข้อมูลแบนเนอร์และแสดงภายในwshล่าม

jonathan@blackbox:~$ wsh /usr/sbin/apache2
> a = ap_get_server_banner()
> print(a)
Apache/2.4.7
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.