¿Por qué ciertos programas (como readlink) no pueden recibir información de una tubería?

¿Por qué ciertos programas (como readlink) no pueden recibir información de una tubería?

Tengo un enlace simbólico a un script en mi $PATHcuyo archivo quería editar. Olvidé la ruta del archivo, así que intenté hacer lo siguiente:

$ which my_script_link | readlink

Esperaba que eso generara la ruta del archivo, pero en su lugar generó

> readlink: missing operand
> Try 'readlink --help' for more information

He visto un comportamiento similar antes en otras situaciones (como intentar canalizar una lista de archivos a vim para editarlos). Sé que existen soluciones, como una subcapa readlink $(which my_script_link), pero quiero entenderpor quéLa tubería no funciona como creo que debería en esta situación.

¡Gracias!

Respuesta1

En pocas palabras, porque el programa no está escrito para hacerlo. Depende de los programadores decidir si su software puede leer desde STDIN o si requiere un archivo de entrada. En el caso de readlink, su manpágina dice (el énfasis es mío):

enlace de lectura - impresión resueltaenlaces simbólicoso canóniconombres de archivos

SINOPSIS
readlink [OPCIÓN]...ARCHIVO...

Como regla general, los programas que reciben información de STDIN están diseñados para analizar esa entrada de alguna manera. Cuando canaliza datos, readlinkrecibe un flujo de texto y no tiene idea de qué hacer con él, ya que solo trata con archivos y no con su contenido. Lo mismo es cierto para programas como lso cdo cpetc. Sólo los programas que tratan con flujos de texto/datos pueden aceptar entradas de una tubería.

Respuesta2

Si un programa toma sus entradas de stdino como argumentos de la línea de comandos depende del diseñador. Ambos enfoques tienen sus ventajas. Sin embargo, para programas que operan estrictamente con archivos, generalmente es más conveniente pasar los nombres de los archivos como argumentos de línea de comando en lugar de hacerlo a través de stdin.

La razón más obvia es que el caso común, el de simplemente ejecutar un programa en un archivo, es más fácil de escribir: readlink fileen lugar de echo file | readlink.

Una cuestión más sutil es la corrección. El problema es que cuando se pasan nombres de archivos stdin, el programa necesita poder distinguir un nombre de archivo de otro. A menudo, esto se hace asumiendo que los nombres de los archivos están separados por espacios en blanco o nuevas líneas, pero esto es incorrecto ya que los nombres de archivos pueden incluir espacios en blanco. Una mejor manera es separar los nombres de archivos con bytes nulos, pero puede resultar inconveniente generar una lista de archivos separados por valores nulos.

Pasar nombres de archivos en la línea de comando evita este problema porque el shell maneja todo el análisis y las citas del programa. Puede escribir touch $'foo\nbar'y touchver esto correctamente como un nombre de archivo que contiene una nueva línea, sin tener que manejar ningún análisis especial o citar.

Dicho todo esto, si deseas pasar archivos stdinpara un programa en particular, puedes hacerlo. Para esto es xargs. xargsle permite tomar un programa que sólo acepta argumentos en la línea de comando y, en su lugar, hacer que pase sus argumentos a través de stdin.

En otras palabras, te permite hacer esto: which my_script | xargs readlink.

Si desea que siempre readlinkfuncione de esta manera, puede crear un alias: alias readlink="xargs readlink". Eso le permitiría escribir which my_script | readlinkcomo quería originalmente.

Respuesta3

Para los comandos que no toman argumentos a través de STDIN, puedes usar este pragma de código:

$ readlink $(which my_script_link)

Ejemplo

$ ln -s /bin/ls ~/bin/somecmd

Ahora comprueba que esté en el $PATH.

$ which somecmd
~/bin/somecmd

O la forma preferida, usando typeinstad of which:

$ type somecmd
somecmd is /home/saml/bin/somecmd

O solo el valor:

$ type -P somecmd 
/home/saml/bin/somecmd

Ahora ejecutamos readlink:

$ readlink $(type -P somecmd)
/bin/ls

información relacionada