2010-11-23 28 views
8

Estoy escribiendo una secuencia de comandos bash para redirigir la salida de otro comando a la ubicación correcta. Básicamente, cuando se invoca el script desde un shell/línea de comando, quiero enviar el resultado a STDOUT. Pero, cuando el script bash se ejecuta desde alguna otra aplicación (por ejemplo, otro script bash, alguna aplicación o, en mi caso, desde el plugin awesome-prompt en mi Awesome Window Manager) quiero redirigir el resultado a otro lugar.Comprueba si se invocó la secuencia de comandos bash desde una shell u otra secuencia de comandos/aplicación

¿Hay alguna manera en bash de ver cómo se invoca un script?

+1

La otra forma de verlo es, ¿por qué no utiliza un script de envoltura que canaliza la salida? Entonces, Awesome Window Manager llama a script-wrapper.sh, y contiene la línea "./script.sh >> awesome.log" – laher

+1

Como usuario de scripts de shell, prefiero que los scripts no intenten ser demasiado ingeniosos . Si quiero el resultado en un archivo, soy perfectamente capaz de ponerlo en un archivo yo mismo. Proporcionar un envoltorio o una bandera a la secuencia de comandos para enviar la salida a un archivo (por ejemplo, '-o nombre de archivo') sería ideal, fácil, pero explícito. –

+0

Buenos puntos, pero quiero que esto sea simple. El comando en cuestión es Taskwarrior. Solo quiero poder escribir 'task add blah blah' y no preocuparme donde lo escribí. Si necesito escribir una serie de redirecciones u opciones cada vez, no lo usaré porque no es lo suficientemente conveniente. –

Respuesta

16

Prueba esto:

ps -o stat= -p $PPID 

Si el resultado contiene "s" (en minúsculas) se ejecutó desde la línea de comandos o desde un script. Para distinguirlos a los dos:

ps -o stat= -p $$ 

contendrá un "+" si no está conectado a tierra.

Aquí hay una tabla:

Run   $$ $PPID 
CL   S+ Ss 
CL&   S  Ss+ 
Script  S+ S+ 
Script&  S  S 
Script(&) S  Ss 
Script&(&) S  NULL 

Dónde (&) significa el guión niño estaba en segundo plano y & significa el guión padre (que es lo que "escritura" se refiere a) que corrió fue en segundo plano. CL significa línea de comando. NULL significa que la salida ps es nula y que $PPID es "1".

De man ps:

s is a session leader 
    + is in the foreground process group 

Hay que señalar que esta respuesta se basa en GNU ps, pero las páginas del manual de BSD (incluyendo OS X) indican una funcionalidad similar. Y GNU ps es un híbrido que incluye funcionalidad BSD, entre otros.

+2

"Secuencia de comandos" en la tabla significa el script principal, en lugar del script que es el tema de la pregunta. –

+0

Muy buena solución. –

-1

Usted podría utilizar $ PPID

Algo así como

PARENT=`ps --no-heading -o %c -p $PPID` 
    echo $PARENT 

Esto sería establecer la variable PADRES a nombre de proceso de los padres y, a continuación, imprimirlo en pantalla.

A continuación, podría poner un poco de si las declaraciones basadas en el valor de $ PADRES

HTH

+0

Probé esto y no importa si lo ejecuto directamente o desde otro script * bash * imprime "bash" – ndemou

+0

@ndemou Confirme en Ubuntu 16.04, kernel 4.4.0-53, y cinco años después todavía imprime "bash". – WinEunuuchs2Unix

7

Creo que lo que realmente quiere saber es si la salida estándar es un terminal o no. Si es así, puedes (casi) asumir con seguridad que se trata de una sesión interactiva. Pruebe el siguiente fragmento:

if [[ -t 1 ]]; then 
     echo "Terminal" 
else 
     echo "Not-a-terminal" 
fi 

El comando [[ -t 1 ]] arriba es lo que comprueba si el descriptor de archivo 1 (es decir, la salida estándar) es un terminal o no.

EDIT:

Tenga en cuenta que esto indicará una salida estándar no terminal si canalizar la salida a algún otro programa. En ese caso, es posible que desee una condición más versátil que también controlará la entrada estándar (descriptor de fichero 0):

[[ -t 0 || -t 1 ]] 
+0

Pero es posible que desee saber si se ejecuta desde una línea de comando incluso cuando stdout no está en una terminal (como cuando está en una tubería). –

+0

@Dennis: No creo que haya una solución universal o mágica para esto. El método PPID es efectivo, pero también fallaría si el script se llamara a través de otro script. – thkala

+0

¿Estás hablando de un tercer nivel? Secuencia de comandos de script que llama al script? Todavía podría usar la técnica en mi respuesta para determinar si la secuencia de comandos de interés se ejecutó desde la línea de comandos. –

1

Esta es una función que adapté de otra publicación sobre este tema. Coincide con todos los procesos principales hasta la parte superior con los elementos enumerados en la variable $shells. Cuando está hecho $iscli se establece en 0 o 1. Si se establece en 0, entonces usted sabe que se ejecutó desde un caparazón o lo que considera suficiente para este fin. Si está configurado en 1, entonces sabrá que un programa estuvo involucrado y que no está aprobado. Lo uso para scripts que quiero ejecutar en un shell y vía PHP cuando quiero que se proporcione un resultado diferente para cada uno.

Por supuesto, necesitará llamar a la función inicialmente sin ningún parámetro antes de que necesite $iscli para tener un valor.

function top_level_parent_pid { 

    scriptname="${0##*/}" 
    shells="^bash|^init|^screen|^sh|^ssh|^su|${scriptname}" 

    pid=${1:-$$} 
    pidname="`ps --no-heading -o %c -p ${pid}`" 
    stat=($(</proc/${pid}/stat)) 
    ppid=${stat[3]} 
    ppidname="`ps --no-heading -o %c -p ${ppid}`" 
    isclitest="`echo "${ppidname}" | grep -iv -E "${shells}"`" 

    until [ "${ppid}" -eq "1" ] || [ "${iscli}" = "1" ]; do 

      if [[ -n "${isclitest}" ]]; then 

        iscli="1" 

       else 

        iscli="0" 


        top_level_parent_pid ${ppid} 
      fi 
    done 
} 
Cuestiones relacionadas