ทำไม `watch` สร้าง` ls / tmp` ทำรายการเนื้อหาของ $ HOME?


13

ฉันพยายามดูจำนวนไฟล์ใน/tmp/ไดเรกทอรีของฉัน สำหรับสิ่งนี้ฉันคิดว่าคำสั่งนี้จะทำงาน:

watch sh -c 'ls /tmp/|wc -l'

แต่ดูเหมือนว่าจะทำงานเหมือนlsไม่มีข้อโต้แย้ง คือผมอยู่ในและฉันได้รับจำนวนไฟล์ที่มีแทน~ /tmp/ฉันพบวิธีแก้ปัญหาซึ่งดูเหมือนจะทำงานได้:

watch sh -c 'ls\ /tmp/|wc -l'

แต่ทำไมฉันต้องหลบหนีช่องว่างระหว่างlsและ/tmp/? คำสั่งถูกแปลงโดยwatchเพื่อให้lsเอาต์พุตถูกป้อนเข้าwcแต่/tmp/ไม่ถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังls?


1
watch "sh -c 'ls /tmp | wc -l'"การทำคำสั่งนี้ควรจะได้รับผลกระทบที่ต้องการ นี่ไม่ใช่ความผิดลองsh -c ls /tmpและคุณจะได้รับไดเรกทอรีบ้านของคุณ (แต่ฉันไม่รู้ว่าทำไม ... )
Jacob Minshall

8
ไม่ใช่คำตอบ แต่คุณใช้watchอย่างไม่ถูกต้องคำสั่งที่คุณส่งไปwatchนั้นจะถูกป้อนwatchไปยังsh -cดังนั้นคุณจึงมีผลในการทำsh -cสองครั้ง
iruvar

หากคุณสงสัยคุณสามารถดูที่แหล่งข้อมูลได้
michas

1
@JacobMinshall ที่ตรงไปตรงมาคือเหตุผลที่การ/tmpเป็นอาร์กิวเมนต์ในกรณีที่ไม่ได้เป็นอาร์กิวเมนต์sh ls
ชาร์ลส์ดัฟฟี่

คำตอบ:


17

ความแตกต่างอาจเห็นได้ผ่านstrace:

$ strace -ff -o bq watch sh -c 'ls\ /tmp/|wc -l'
^C
$ strace -ff -o nobq watch sh -c 'ls /tmp/|wc -l'
^C
$ grep exec bq* | grep sh
bq.29218:execve("/usr/bin/watch", ["watch", "sh", "-c", "ls\\ /tmp/|wc -l"], [/* 54 vars */]) = 0
bq.29219:execve("/bin/sh", ["sh", "-c", "sh -c ls\\ /tmp/|wc -l"], [/* 56 vars */]) = 0
bq.29220:execve("/bin/sh", ["sh", "-c", "ls /tmp/"], [/* 56 vars */]) = 0
$ grep exec nobq* | grep sh
nobq.29227:execve("/usr/bin/watch", ["watch", "sh", "-c", "ls /tmp/|wc -l"], [/* 54 vars */]) = 0
nobq.29228:execve("/bin/sh", ["sh", "-c", "sh -c ls /tmp/|wc -l"], [/* 56 vars */]) = 0
nobq.29229:execve("/bin/sh", ["sh", "-c", "ls", "/tmp/"], [/* 56 vars */]) = 0

ในกรณี backquote ls /tmpจะถูกส่งเป็นอาร์กิวเมนต์เดียวไปยัง-cถึงshซึ่งทำงานตามที่คาดไว้ หากไม่มี backquote นี้คำสั่งจะแทน word split เมื่อwatchรันshซึ่งจะรันคำสั่งที่จัดshไว้ดังนั้นlsจะถูกส่งผ่านเป็นอาร์กิวเมนต์-cเท่านั้นซึ่งหมายความว่า sub-sub shจะรันlsคำสั่งเปล่าเท่านั้นและแสดงรายการเนื้อหาของงานปัจจุบัน ไดเรกทอรี

ดังนั้นทำไมความซับซ้อนของsh -c ...? ทำไมไม่ทำงานเพียงwatch 'ls /tmp|wc -l'?


โอ้ไม่คิดลองstraceดู
Ruslan

1
ที่จริงแล้ว `คือ backquote (หรือ back-tick) คำถามนี้เกี่ยวกับ \ ซึ่งเป็นแบ็กสแลช
G-Man กล่าวว่า 'Reinstate Monica'

@Ruslan: ฉันโพสต์ความคิดเห็นนี้ในคำตอบนี้เพราะมันเป็นความเห็นต่อคำตอบนี้ thrig กล่าวว่า "ในกรณีbackquotels /tmpคือ ... " และ "หากไม่มีbackquoteนี้คำสั่งคือ ... " และใช้bqและnobqเป็นชื่อไฟล์เมื่อทุกอย่างในขณะที่อ้างถึงแบ็กสแลชในls\ /tmpคำสั่งของคุณ
G-Man กล่าวว่า 'Reinstate Monica'

8

มีwatchคำสั่งหลักสองประเภท(ของคำสั่งที่เรียกใช้คำสั่งเป็นระยะwatchไม่ใช่คำสั่งมาตรฐานมีแม้กระทั่งระบบที่watchทำสิ่งที่แตกต่างอย่างสิ้นเชิงเช่นสอดแนมบนบรรทัด tty อื่นบน FreeBSD)

หนึ่งที่ผ่านการต่อกันของการขัดแย้งกับช่องว่างไปยังเปลือก (มันจะมีผลในการโทรsh -c <concatenation-of-arguments>) และหนึ่งที่เพียงแค่เรียกใช้คำสั่งที่ระบุด้วยข้อโต้แย้งที่ระบุโดยไม่ต้องเรียกเปลือก

คุณอยู่ในสถานการณ์แรกดังนั้นคุณเพียงแค่ต้องการ:

watch 'ls /tmp/|wc -l'

เมื่อคุณทำ:

watch sh -c 'ls /tmp/|wc -l'

ของคุณwatchทำงานจริง:

sh -c 'sh -c ls /tmp/|wc -l'

และsh -c ls /tmp/กำลังเรียกใช้lsสคริปต์แบบอินไลน์ที่$0เป็น/tmp/(เพื่อให้lsทำงานโดยไม่มีข้อโต้แย้งและแสดงรายการไดเรกทอรีปัจจุบัน)

การwatchใช้งานบางอย่างในหมวดหมู่แรก (เช่นที่มาจาก procps-ng บน Linux) ยอมรับ-xตัวเลือกเพื่อให้พวกมันมีพฤติกรรมเช่นเดียวกับwatchประเภทที่สอง คุณสามารถทำได้:

watch -x sh -c 'ls /tmp/|wc -l'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.