Подтвердить что ты не робот

Bash: динамически переназначать стандартный ввод в script

Я пытался сделать это, чтобы решить, нужно ли перенаправить stdin в файл или нет:

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

Но это не работает, потому что, когда переменная $input является "& 0", bash интерпретирует ее как имя файла.

Однако я мог просто сделать:

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

Проблема заключается в том, что. /myScript на самом деле является длинной командной строкой, которую я не хочу дублировать, и я не хочу создавать для нее функцию, потому что она не так долго (она не стоит).

Тогда мне пришло в голову сделать это:

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

Но для этого требуется запустить еще одну команду и канал (т.е. подоболочку).
Есть ли другой способ, который проще и эффективнее?

4b9b3361

Ответ 1

Прежде всего, stdin - это дескриптор файла 0 (ноль), а не 1 (который является stdout).

Вы можете дублировать файловые дескрипторы или использовать имена файлов условно следующим образом:

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

some_long_command_line <&3

Ответ 2

(
    if [ ...some condition here... ]; then
        exec <$fileName
    fi
    exec ./myscript
)

В подоболочке условно переадресовываем stdin и выполняем script.

Ответ 3

Стандартный ввод также может быть представлен специальным файлом устройства /dev/stdin, поэтому использование этого в качестве имени файла будет работать.

file="/dev/stdin"
./myscript < "$file"

Ответ 4

Как насчет

function runfrom {
    local input="$1"
    shift
    case "$input" in
        -) "[email protected]" ;;
        *) "[email protected]" < "$input" ;;
    esac
}

Я использовал знак минус для обозначения стандартного ввода, потому что это традиционно для многих программ Unix.

Теперь вы пишете

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

Я нахожу эти функции/команды, которые принимают команды в качестве аргументов (например, xargs(1)), могут быть очень полезными, и они хорошо складываются.

Ответ 5

Если вы будете осторожны, вы можете использовать 'eval' и свою первую идею.

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

Однако вы говорите, что "myScript" на самом деле является сложным вызовом команды; если он включает в себя аргументы, которые могут содержать пробелы, тогда вы должны быть очень осторожны, прежде чем решите использовать "eval".

Честно говоря, беспокоиться о стоимости команды "cat", вероятно, не стоит проблем; это вряд ли будет узким местом.

Еще лучше создать myScript, чтобы он работал как обычный фильтр Unix - он читается со стандартного ввода, если ему не дано работать один или несколько файлов (например, cat или grep в качестве примеров). Этот дизайн основан на длительном и надежном опыте - и поэтому стоит подражать, чтобы избежать необходимости решать такие проблемы.

Ответ 6

Используйте eval:

#! /bin/bash

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

eval "./myScript <$input"

Эта простая версия для myScript

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

выводит следующий результат:

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

$ ./myDemux
foo
oof
bar
rab
baz
zab

Обратите внимание, что он также обрабатывает пробелы в входах:

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

Чтобы подключить вход до myScript, используйте замещение процесса:

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

Обратите внимание, что если вы попытаетесь напрямую передать вывод, как в

$ md5sum /etc/issue | ./myDemux

он будет ждать ожидания на входе от терминала, тогда как эфемерный ответ не имеет этого недостатка.

Небольшое изменение вызывает желаемое поведение:

#! /bin/bash

[ $# -gt 0 ] && input="'"$1"'" || input=/dev/stdin
eval "./myScript <$input"

Ответ 7

люди показывают вам очень длинные сценарии, но... вы получаете bash trap:) Вы должны процитировать все в bash. например, вам нужен файл с именем & 0.

filename = '& 0' #right ls $filename #wrong! это подставить $filename и интерпретировать & 0 ls "$ filename" #right

другой, файлы с пробелами.

filename = 'некоторый файл с пробелами' ls $filename #wrong, bash сократить первое и последнее пространство и уменьшить количество пробелов между словами и пробелами ls "$ filename" righ

то же самое в вашем script. измените:

./myScript < $input

к

./myScript < "$input"

все его. bash больше ловушек. Я предлагаю сделать цитату для "$ file" по той же причине. пробелы и другие символы, которые могут быть интерпретированы, всегда создают проблемы.

но как насчет /dev/stdin? это можно использовать только тогда, когда вы перенаправляете stdin и хотите что-то напечатать для реального stdin.

поэтому ваш script должен выглядеть следующим образом:

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