Tengo un enlace simbólico a un script en mi $PATH
cuyo 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 man
pá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, readlink
recibe 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 ls
o cd
o cp
etc. 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 stdin
o 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 file
en 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 touch
ver 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 stdin
para un programa en particular, puedes hacerlo. Para esto es xargs
. xargs
le 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 readlink
funcione de esta manera, puede crear un alias: alias readlink="xargs readlink"
. Eso le permitiría escribir which my_script | readlink
como 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 type
instad 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