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?
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 exec
si 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).
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.
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"
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.
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.
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"
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"
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