Bash: redirigir dinámicamente la entrada estándar en un script


Estaba tratando de hacer esto para decidir si redirigir stdin a un archivo o no:

[ ...some condition here... ] && input=$fileName || input="&0"
./myScript < $input

Pero eso no funciona porque cuando la variable input input es "&0", bash la interpreta como un nombre de archivo.

Sin embargo, podría hacer:

if [ ...condition... ];then
    ./myScript <$fileName
else
    ./myScript

El problema es que ./ MyScript es en realidad una línea de comandos larga que no quiero duplicar, ni tampoco quiero crear una función para ella porque tampoco es tan larga (no vale la pena).

Entonces se me ocurrió hacer esto:

[ ...condition... ] && input=$fileName || input=  #empty
cat $input | ./myScript

Pero eso requiere ejecutar un comando más y una tubería (es decir, una subcapa).
¿Hay otra manera que sea más simple y más eficiente?

Author: GetFree, 2010-01-01

7 answers

En primer lugar stdin es el descriptor de fichero 0 (cero) en lugar de 1 (que es stdout).

Puede duplicar descriptores de archivo o usar nombres de archivo condicionalmente como este:

[[ some_condition ]] && exec 3<"$filename" || exec 3<&0

some_long_command_line <&3

Tenga en cuenta que la orden mostrada ejecutará la segunda execsi la condición es falsa o la primera exec falla. Si usted no quiere un fracaso potencial para hacer que entonces usted debe utilizar un if / else:

if [[ some_condition ]]
then
    exec 3<"$filename"
else
    exec 3<&0
fi

Pero las redirecciones posteriores desde el descriptor de archivo 3 fallarán si la primera redirección falló (después de que la condición fuera verdadera).

 22
Author: Dennis Williamson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-17 13:53:05
(
    if [ ...some condition here... ]; then
        exec <$fileName
    fi
    exec ./myscript
)

En una subcapa, redirige condicionalmente stdin y ejecuta el script.

 7
Author: ephemient,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-31 23:23:49

La entrada estándar también puede ser representada por el archivo de dispositivo especial /dev/stdin, por lo que usar eso como nombre de archivo funcionará.

file="/dev/stdin"
./myscript < "$file"
 4
Author: Ignacio Vazquez-Abrams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-01-01 00:43:54

¿Qué tal

function runfrom {
    local input="$1"
    shift
    case "$input" in
        -) "$@" ;;
        *) "$@" < "$input" ;;
    esac
}

He usado el signo menos para indicar la entrada estándar porque eso es tradicional para muchos programas Unix.

Ahora escribes

[ ... condition ... ] && input="$fileName" || input="-"
runfrom "$input" my-complicated-command with many arguments

Encuentro que estas funciones/comandos que toman comandos como argumentos (como xargs(1)) pueden ser muy útiles, y componen bien.

 2
Author: Norman Ramsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-31 21:37:40

Si tienes cuidado, puedes usar 'eval' y tu primera idea.

[ ...some condition here... ] && input=$fileName || input="&1"
eval ./myScript < $input

Sin embargo, usted dice que 'MyScript' es en realidad una invocación de comandos compleja; si involucra argumentos que pueden contener espacios, entonces debe tener mucho cuidado antes de decidir usar 'eval'.

Francamente, preocuparse por el costo de un comando 'cat' probablemente no valga la pena; es poco probable que sea el cuello de botella.

Aún mejor es diseñar myScript para que funcione como un filtro Unix normal - lee desde la entrada estándar a menos que se le dé uno o más archivos para trabajar (como, por ejemplo, cat o grep como ejemplos). Ese diseño se basa en una larga y sólida experiencia , y por lo tanto vale la pena emularlo para evitar tener que lidiar con problemas como este.

 2
Author: Jonathan Leffler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-12-31 22:08:20

Uso eval:

#! /bin/bash

[ $# -gt 0 ] && input="'"$1"'" || input="&1"

eval "./myScript <$input"

Este simple sustituto para myScript

#! /usr/bin/perl -lp
$_ = reverse

Produce la siguiente salida:

$ ./myDemux myScript
pl- lrep/nib/rsu/ !#
esrever = _$

$ ./myDemux
foo
oof
bar
rab
baz
zab

Tenga en cuenta que maneja espacios en entradas también:

$ ./myDemux foo\ bar
eman eht ni ecaps a htiw elif

Para canalizar la entrada hasta myScript, use sustitución de procesos :

$ ./myDemux <(md5sum /etc/issue)
eussi/cte/  01672098e5a1807213d5ba16e00a7ad0

Tenga en cuenta que si intenta canalizar la salida directamente, como en

$ md5sum /etc/issue | ./myDemux

Se colgará esperando la entrada del terminal, mientras que la respuesta de ephemient no tiene esto defecto.

Un ligero cambio produce el comportamiento deseado:

#! /bin/bash

[ $# -gt 0 ] && input="'"$1"'" || input=/dev/stdin
eval "./myScript <$input"
 1
Author: Greg Bacon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:25:45

La gente te muestra guiones muy largos, pero.... usted consigue bash trampa :) Debes citar todo en bash. por ejemplo, desea un archivo de lista llamado & 0 .

Filename = '& 0 ' # derecha ls fil filename # wrong! esto sustituye fil nombre de archivo e interpreta & 0 ls "fil filename" # right

Otro, archivos con espacios.

Filename = ' algún archivo con espacios ' lsename filename #wrong, bash cut first and last space, and reduce multiple spaces between with and spaces words ls "$nombre_archivo" rig

Lo mismo está en su script. por favor, cambie:

./myScript < $input

A

./myScript < "$input"

Es todo. bash tiene más trampas. Sugiero hacer una cita para "file file" con la misma razón. los espacios y otros caracteres que pueden ser interpretados siempre crean problemas.

Pero ¿qué pasa con /dev/stdin ? esto solo se puede usar cuando redirige stdin y desea imprimir algo en stdin real.

Entonces, tu script debería mostrarse así:

[ ...some condition here... ] && input="$fileName" || input="&0"
./myScript < "$input"
 0
Author: Znik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-22 13:59:06