Чому деякі команди не читаються зі свого стандартного вводу?


19

Цікаво, що коли ми повинні використовувати трубопровід, а коли не повинні.

Скажімо, наприклад, щоб знищити певний процес, який обробляє файли PDF, наступне не буде працювати, використовуючи конвеєр:

ps aux | grep pdf | awk '{print $2}'|kill

Натомість ми можемо це зробити лише наступними способами:

kill $(ps aux| grep pdf| awk '{print $2}')

або

ps aux | grep pdf | awk '{print $2}'| xargs kill

Відповідно до man bash(версії 4.1.2):

The standard output of command is connected via a pipe to the standard input of command2.

Для вищезгаданого сценарію:

  • stdin of grep- це stdout of ps. Це працює.
  • stdin of awk- це stdout of grep. Це працює.
  • stdin of kill- це stdout of awk. Це не працює.

Наступна команда stdin завжди отримує введення з верстки попередньої команди.

  • Чому це не працює з killабо rm?
  • Що розрізнялися kill, rmвхід з grep, awkвходом?
  • Чи є якісь правила?

1
Це не відповідь , але ви можете мати погляд на pgrep, pkillі killallкоманді.
terdon

2
@terdon: Я просто використовую вищевикладений сценарій, щоб показати проблему з трубопроводом, я розумію, що pgrepі решта можуть домогтися цього ідеально :)
sylye

Відповіді:


17

Є два загальних способи надання вхідних програм для програм:

  • надати дані STDIN процесів
  • вкажіть аргументи командного рядка

killвикористовує лише аргументи командного рядка. Це не читається зі STDIN. Такі програми, як grepі awkчитаються з STDIN (якщо в якості аргументів командного рядка не вказано назви файлів), обробляють дані відповідно до аргументів їх командного рядка (шаблону, висловлювань, прапорів, ...).

Ви можете передавати на STDIN лише інші процеси, а не аргументи командного рядка.

Поширене правило полягає в тому, що програми використовують STDIN для обробки довільної кількості даних. Всі додаткові вхідні параметри або, якщо їх взагалі всього лише декілька, передаються аргументами командного рядка. Якщо командний рядок може отримати дуже довгий, наприклад, для довгих awkтекстів програм, часто існує можливість їх зчитування з додаткових програмних файлів ( -fопція awk).

Щоб використовувати STDOUT програм як аргументи командного рядка, використовуйте $(...)або у випадку великої кількості даних xargs. findможе також це безпосередньо -exec ... {} +.

Для повноти: Щоб написати аргументи командного рядка в STDOUT, використовуйте echo.


1
Як ми знаємо, що команда буде приймати лише аргументи, але не STDIN? Чи є систематичний чи програмний спосіб, а не здогадування чи читання з сторінки людини? Читаючи лише довідкову сторінку, я не міг отримати жодних конкретних підказок, щоб бути твердими щодо того, може команда може чи не може приймати STDIN, оскільки STDIN також є частиною аргументів того, як відображається сторінка man. Наприклад, gzipу SYNOPSIS не сказано, що він повинен приймати FILENAME як вхідний. Я шукаю, чи існує більш систематичний спосіб цього визначити.
sylye

Існує також аргумент "-", який означає "stdin" (або "stdout") для деяких команд.
Еммануїл

Чи xargsточно не дозволить вам "передавати аргументи командного рядка"?
Т. Веррон

@ T.Verron так, це завдання xargs. При необхідності вона викликає команду не раз (розмір командного рядка обмежений) і має безліч інших опцій.
jofel

2
У тексті опису буде описано, як можна користуватися програмою. Наприклад, gzip каже: "Програма gzip стискає та розпаковує файли, використовуючи кодування Lempel-Ziv (LZ77). Якщо не вказано жодних файлів, gzip буде стискатися зі стандартного вводу або декомпресувати до стандартного виводу." Якщо архівна сторінка не згадує стандартне введення, воно не використовуватиме її.
Алан Шутко

16

Це цікаве питання, і воно стосується частини філософії Unix / Linux.

Отже, в чому різниця між програмами , такими як grep, sed, sortз одного боку , і kill, rm, lsз іншого боку? Я бачу два аспекти.

Фільтр аспект

  • Перший вид програм також називають фільтрами . Вони беруть вхід або з файлу, або з STDIN, модифікують його і генерують деякий вихід, переважно в STDOUT. Вони призначені для використання у сукупності з іншими програмами як джерела та пункти призначення.

  • Другий вид програм діє на вхід, але вихід, який вони дають, часто не пов'язаний з входом. killне має виходу, коли працює регулярно, також не робить ls. У справедливих є повернене значення, щоб показати успіх. Вони зазвичай не беруть дані від STDIN, але в основному дають вихід STDOUT.

Для таких програм, як lsаспект фільтра, працює не так добре. Він, безумовно, може мати вхід (але не потрібен), і вихід тісно пов'язаний з цим входом, але він не працює як фільтр. Однак для таких програм все ще працює інший аспект:

семантичний аспект

  • Для фільтрів їх введення не має семантичного значення . Вони просто читають дані, змінюють дані, виводять дані. Не має значення, чи це список числових значень, деякі назви файлів або вихідний код HTML. Значення цих даних надається лише кодом, який ви надаєте фільтру: регулярний вираз grep, правила awkабо програма Perl.

  • Для інших програм, як-от killабо ls, їх введення має значення , позначення . killочікує числа процесів, lsочікує імен файлів або шляхів. Вони не можуть обробляти довільні дані, і вони не призначені для цього. Багатьом з них навіть не потрібні ні введення, ні параметри, наприклад ps. Зазвичай вони не читаються з STDIN.

Можливо, можна поєднати ці два аспекти: Фільтр - це програма, вхід якої не має смислового значення для програми.

Я впевнений, що десь читав про цю філософію, але наразі не пам’ятаю жодних джерел, вибачте. Якщо у когось є джерела, будь ласка, не соромтесь редагувати.


5

Не існує "правил" як таких. Деякі програми приймають дані від STDIN, а деякі ні. Якщо програма може приймати дані від STDIN, вона може бути передана, якщо ні, то не може.

Ви можете звичайно сказати, чи буде програма брати участь чи ні, думаючи про те, що вона робить. Якщо робота програми полягає в тому, щоб якимось - то чином маніпулювати вміст файлу (наприклад grep, sed, і awkт.д.), він зазвичай приймає вхідні дані з STDIN. Якщо його робота полягає в тому, щоб маніпулювати сам файл (наприклад mv, rm, cp) або процес (наприклад kill, lsof) або для повернення інформації про що - небудь (наприклад top, find, ps) , то це не робить.

Інший спосіб мислення про це - різниця між аргументами та вхідними даними. Наприклад:

mv foo bar

У наведеній вище команді mvнемає введення як такого. Надано два аргументи. Він не знає і не цікавить, що є в будь-якому з файлів, він просто знає, що це його аргументи, і він повинен ними маніпулювати.

З іншої сторони

sed -e 's/foo/bar/' < file
--- -- ------------   ----
 |   |       |          |-> input
 |   |       |------------> argument        
 |   |--------------------> option/flag/switch
 |------------------------> command

Тут sedбуло подано введення, а також аргумент. Оскільки він займає вхід, він може прочитати його з STDIN і до нього можна передати.

Це ускладнюється, коли аргументом може бути вхід. Наприклад

cat file

Ось fileаргумент, який було надано cat. Якщо бути точним, ім'ям файлу fileє аргумент. Однак, оскільки catце програма, яка маніпулює вмістом файлів, її введення - все, що є всередині file.

Це можна проілюструвати за straceдопомогою програми, яка відстежує системні виклики, здійснені процесами. Якщо ми запустимо cat fooчерез strace, ми можемо побачити, що файл fooвідкрито:

$ strace cat foo 2| grep foo
execve("/bin/cat", ["cat", "foo"], [/* 44 vars */]) = 0
open("foo", O_RDONLY)     

Перший рядок вище показує, що програма /bin/catвикликалася і її аргументи були catі foo(перший аргумент - це завжди сама програма). Пізніше аргумент fooбуло відкрито в режимі лише для читання. А тепер порівняйте це

$ strace ls foo 2| grep foo 
execve("/bin/ls", ["ls", "foo"], [/* 44 vars */]) = 0
stat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
lstat("foo", {st_mode=S_IFREG|0644, st_size=0, ...}) = 0
write(1, "foo\n", 4foo

Ось також, lsвзяв себе і fooяк аргументи. Однак openвиклику немає , аргумент не трактується як вхідний. Натомість lsвикликає statбібліотеку системи (що не те саме, що statкоманда), щоб отримати інформацію про файл foo.

Підсумовуючи це, якщо команда, яку ви виконуєте, прочитає її вхід, ви можете передати її, якщо вона не - не зможете.


0
  • Чому це не працює з kill або rm?

killі rmне потрібен STDIN.

  • У чому різниця між введенням, введенням rm з grep, awk введенням?

Для killта rmкористувачі надають персоналізовану інформацію як аргумент та $(cmd)допомагає приймати STDOUT cmdта перетворювати його інформаційний аргумент.

Для grepі awk, користувачі надають аргументи, а крім того, також STDINабо звичайний файл, який буде оброблений командою. STDINможе бути переданий трубопроводом |або ручним введенням.

  • Чи є якісь правила?

Прочитайте посібник чи вихідні коди. І якщо ви не знайдете нічого необхідного, можете зробити простий, але, можливо, небезпечний тест:

Просто введіть цікаву вам команду з аргументами, які ви вже зрозуміли, і подивіться, чи команда призупиняється (нічого не відбувається). Якщо вона призупиниться, вона насправді чекає STDIN (ви можете спробувати catі echoпобачити різні). Ви вводите вручнуCtrl-D і команда йде вперед (показує результати або помилки) і повертається. У цій ситуації така команда потребує STDIN (з наданими вами аргументами).

Ця ж команда може не потребувати STDIN в різних ситуаціях (наприклад, catчекає STDIN, але cat file.txtні).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.