¿Por qué algunos comandos no leen de su entrada estándar?

19

Me pregunto qué deberíamos usar para canalizar y cuándo no.

Digamos, por ejemplo, que para matar ciertos procesos que manejan archivos pdf, lo siguiente no funcionará usando pipeline:

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

En cambio, solo podemos hacerlo de las siguientes maneras:

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

o

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

Según man bash(versión 4.1.2):

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

Para el escenario anterior:

  • el stdin de grepes el stdout de ps. Eso funciona.
  • el stdin de awkes el stdout de grep. Eso funciona.
  • el stdin de killes el stdout de awk. Eso no funciona

El stdin del siguiente comando siempre recibe información del stdout del comando anterior.

  • ¿Por qué no funciona con killo rm?
  • ¿Cuál es la diferencia entre kill, rmde entrada, con grep, awkde entrada?
  • ¿Hay alguna regla?
sylye
fuente
1
Esto no es una respuesta, pero es posible que desee echar un vistazo a las pgrep, pkilly los killallcomandos.
terdon
2
@terdon: solo uso el escenario anterior para mostrar el problema de la tubería, entiendo que pgrepy el resto puede lograr esto perfectamente :)
sylye

Respuestas:

17

Hay dos formas comunes de proporcionar aportes a los programas:

  • proporcionar datos a STDIN de los procesos
  • especificar argumentos de línea de comando

killusa solo argumentos de línea de comando. No lee de STDIN. Los programas tienen gusto grepy awkleen de STDIN (si no se dan nombres de archivo como argumentos de línea de comando) y procesan los datos de acuerdo con sus argumentos de línea de comando (patrón, declaraciones, banderas, ...).

Solo puede canalizar a STDIN de otros procesos, no a argumentos de línea de comando.

La regla común es que los programas usan STDIN para procesar una cantidad arbitraria de datos. Todos los parámetros de entrada adicionales o, si generalmente hay solo unos pocos, se pasan por argumentos de línea de comando. Si la línea de comando puede ser muy larga, por ejemplo, para awktextos de programa largos , a menudo existe la posibilidad de leerlos desde archivos de programa adicionales ( -fopción de awk).

Para usar el STDOUT de programas como argumentos de línea de comando, use $(...)o en caso de muchos datos xargs. findTambién puede esto directamente con -exec ... {} +.

Para completar: Para escribir argumentos de línea de comando en STDOUT, use echo.

jofel
fuente
1
¿Cómo sabemos que un comando solo tomará argumentos pero no STDIN? ¿Existe una forma sistemática o programática en lugar de adivinar o leer desde la página de manual? Al solo leer la página de manual, no pude obtener ninguna pista específica para afirmar si el comando puede o no tomar STDIN, ya que STDIN también es parte de los argumentos de la forma en que se presenta una página de manual. Por ejemplo, gzipen la SINOPSIS, no dijo que debe tomar un FILENAME como entrada. Estoy buscando hay una manera más sistemática para determinar eso.
sylye
También existe el argumento "-" que significa "stdin" (o "stdout") para algunos comandos.
Emmanuel
¿No le xargspermitirá precisamente "canalizar los argumentos de la línea de comando"?
T. Verron
@ T.Verron sí, esta es la tarea de xargs. Llama al comando si es necesario más de una vez (el tamaño de la línea de comando es limitado) y tiene muchas otras opciones.
jofel
2
El texto de la descripción describirá cómo puede usar el programa. Por ejemplo, gzip dice: "El programa gzip comprime y descomprime archivos usando la codificación Lempel-Ziv (LZ77). Si no se especifican archivos, gzip se comprimirá desde la entrada estándar o se descomprimirá a la salida estándar". Si una página de manual no menciona la entrada estándar, no la usará.
Alan Shutko
16

Esta es una pregunta interesante, y trata con una parte de la filosofía de Unix / Linux.

Así que, ¿cuál es la diferencia entre programas como grep, sed, sortpor un lado, y kill, rm, lsen el otro lado? Veo dos aspectos.

El aspecto del filtro

  • El primer tipo de programas también se llama filtros . Toman una entrada, ya sea de un archivo o de STDIN, la modifican y generan alguna salida, principalmente a STDOUT. Están destinados a ser utilizados en una tubería con otros programas como fuentes y destinos.

  • El segundo tipo de programas actúa sobre una entrada, pero la salida que dan a menudo no está relacionada con la entrada. killno tiene salida cuando funciona regularmente, tampoco lo hace ls. Solo tienen un valor de retorno para mostrar el éxito. Normalmente no reciben información de STDIN, pero en su mayoría dan salida a STDOUT.

Para programas como ls, el aspecto del filtro no funciona tan bien. Ciertamente puede tener una entrada (pero no la necesita), y la salida está estrechamente relacionada con esa entrada, pero no funciona como filtro. Sin embargo, para ese tipo de programas, el otro aspecto aún funciona:

El aspecto semántico.

  • Para los filtros, su entrada no tiene significado semántico . Simplemente leen datos, modifican datos, generan datos. No importa si se trata de una lista de valores numéricos, algunos nombres de archivo o código fuente HTML. El significado de estos datos solo viene dado por el código que usted proporciona al filtro: la expresión regular para grep, las reglas para awko el programa Perl.

  • Para otros programas, como killo ls, su entrada tiene un significado , una denotación . killespera números de proceso, lsespera nombres de archivo o ruta. No pueden manejar datos arbitrarios y no están destinados a hacerlo. Muchos de ellos ni siquiera necesitan ninguna entrada o parámetro, como ps. Normalmente no leen de STDIN.

Probablemente se podrían combinar estos dos aspectos: un filtro es un programa cuya entrada no tiene un significado semántico para el programa.

Estoy seguro de que he leído sobre esta filosofía en alguna parte, pero no recuerdo ninguna fuente en este momento, lo siento. Si alguien tiene algunas fuentes presentes, no dude en editar.

Dubu
fuente
5

No hay "reglas" como tales. Algunos programas reciben información de STDIN y otros no. Si un programa puede recibir información de STDIN, se puede canalizar a, si no, no puede.

Normalmente puede saber si un programa tomará información o no al pensar en lo que hace. Si el trabajo del programa es manipular de alguna forma los contenidos de un archivo (por ejemplo grep, sed, awketc.), que normalmente toma de entrada de STDIN. Si su trabajo consiste en manipular el archivo en sí (por ejemplo mv, rm, cp) o un proceso (por ejemplo kill, lsof) o a la información de retorno sobre algo (por ejemplo top, find, ps), entonces no lo hace.

Otra forma de pensarlo es la diferencia entre argumentos y aportes. Por ejemplo:

mv foo bar

En el comando anterior, mvno tiene entrada como tal. Lo que se le ha dado son dos argumentos. No sabe ni le importa lo que hay en ninguno de los archivos, solo sabe que esos son sus argumentos y debe manipularlos.

Por otra parte

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

Aquí, sedse ha dado información y un argumento. Como toma entrada, puede leerlo desde STDIN y puede canalizarlo.

Se vuelve más complicado cuando un argumento puede ser la entrada. Por ejemplo

cat file

Aquí fileestá el argumento que se le dio cat. Para ser precisos, el nombre del archivo filees el argumento. Sin embargo, dado que cates un programa que manipula el contenido de los archivos, su entrada es lo que esté dentro file.

Esto puede ilustrarse con straceun programa que rastrea las llamadas al sistema realizadas por los procesos. Si corremos a cat footravés de strace, podemos ver que el archivo fooestá abierto:

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

La primera línea de arriba muestra que el programa /bin/catfue llamado y sus argumentos fueron caty foo(el primer argumento es siempre el programa en sí). Más tarde, el argumento foose abrió en modo de solo lectura. Ahora, compara esto con

$ 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

Aquí también, se lstomó y foocomo argumentos. Sin embargo, no hay openllamada, el argumento no se trata como entrada. En cambio, lsllama a la statbiblioteca del sistema (que no es lo mismo que el statcomando) para obtener información sobre el archivo foo.

En resumen, si el comando que está ejecutando leerá su entrada, puede canalizarlo, si no lo hace, no puede.

terdon
fuente
0
  • ¿Por qué no funciona con kill o rm?

killy rmno necesita STDIN.

  • ¿Cuál es la diferencia entre kill, rm input con grep, awk input?

Para killy rm, los usuarios proporcionan su información personalizada como argumento, y $(cmd)ayuda a tomar el STDOUT de cmdy convertir su argumento de información.

Para grepy awk, los usuarios proporcionan argumentos y, además, también STDINo un archivo normal que será procesado por el comando. STDINse puede pasar con tubería |o ingresando manualmente.

  • ¿Hay alguna regla?

Lea el manual o los códigos fuente. Y si no encuentra nada de lo que necesita, puede hacer una prueba simple pero quizás peligrosa:

Simplemente ingrese el comando que le interesa, con argumentos que ya ha entendido, y vea si el comando se detiene (no pasa nada). Si pausa, en realidad está a la espera de STDIN (puedes probar caty echover el diferente). Escribe manualmente Ctrl-Dy el comando continúa (muestra resultados o errores) y regresa. Tal comando necesita STDIN en esa situación (con argumentos que proporcione).

El mismo comando puede no necesitar STDIN en diferentes situaciones (por ejemplo, catespera STDIN pero cat file.txtno).

Alex Huang
fuente