Как сделать bash-скрипт для ввода данных из командной строки (позиционные), stdout (pipe) и подстановки процессов? - PullRequest
1 голос
/ 10 июля 2011

Я написал скрипт, который принимает несколько опций (-d, -v, -l, а также --version, --leader и т. Д.), А затем остальную часть текста ($ *)может быть что угодно.Скрипт обрабатывает текст и выплевывает его переформатированный.Это довольно долго, поэтому вот сокращенная версия:


## ––––––––––––––––––– [myScript.sh] –––––––––––––––––––––––––– ##

(1) Настройка по умолчаниюзначения:

declare -- v='1.0' FS=$':\n\r\v\f\t'  Application='Finder' Files s='s'
declare -i errors=0 element=0 counter=0 n L=2

(2) Анализ пользовательского ввода:

until [[ -z "$1" || "$1" == '--' || "${1:0:1}" != '-' ]]; do
    [ "$Input" ] && unset Input
    if [[ "$1" =~ ^(-[Ww]|--[Ww]idth=)([0-9]+)? ]]; then
        Input=$(echo "$1" | gsed -re 's|--?W(idth=)?||I' | grep -Eoe '^(0|[1-9][0-9]*)$')
        [ -z "$Input" ] && echo "$2" | grep -Eoe '^(0|[1-9][0-9]*)$' && Input="$2" && shift 1
        (( Input >= 0 )) && Width="$Input" || unset Width
    elif [[ "$1" =~ ^((-[LlIi]|--(([Ll]ead(er|ing)?)?([Ii]n(dent)?)|[Ll]ead(er|ing)?)=)([0-9]+)?)$ ]]; then
        Input=$(echo "$1" | gsed -re 's|--?[a-z]+=?||I' | grep -Eoe '^(0|[1-9][0-9]*)$')
        [ -z "$Input" ] && echo "$2" | grep -Eoe '^(0|[1-9][0-9]*)$' && Input="$2" && shift 1
        (( Input >= 0 )) && L="$Input" || unset L
    ...
    else printf "$(Bold 'Error:') Unrecognized option: '$(Tbrown "$1")'\n\n" >&2
        exit 2
    fi
    shift 1
done

(3) Теперь получите текст:

IFS=''
[ -n "${*}" ] && declare Text="${*}" || Text="$(cat)" ## could also use read instead of cat ##
[ -z "$Text" ] && printf "$(Bold 'Error:') No text entered...\n\n" >&2 && exit 2

(4) Обработка текста:

Text="$(echo "$Text" | gsed -rne '1h;1!H;$g;s|[\x0A-\x0D]+| |g;$p' | expand -t4 )"
echo "$Text"    ##  (temporary)   ##
exit 0          ##  (temporary)   ##
...             ## (process text) ##
...             ## (process more) ##

Часть 3 работает для принятия текста, введенного после параметров и при передаче по трубопроводу, но зависает в ожидании ввода, если текст не введен, и не видит текст, переданный в качестве замены процесса ... например:

Примеры:

./myScript.sh -L10 --width=20 'This is a test'
> This is a test

echo 'This is a test' | ./myScript.sh -L10 --width=20
> This is a test

./myScript.sh -L10 --width=20 < <( echo 'This is a test' )</CODE>
> This is a test

./myScript.sh -L10 --width=20      ##* Want to stop this *##
> (No output)... hangs waiting on cat (or read) for a ^D

echo 'This is a test' >( ./myScript.sh )
> This is a test /dev/fd/63
> Error: No text entered...

./myScript.sh -L10 --width=20 <<<'This is a test'</CODE>
> This is a test

echo "This is a test" | tee >( ./myScript.sh -L10 --width=20 ) >( ./myScript.sh  )
> This is a test
> This is a test
> This is a test

Как заставить скрипт не висеть на коте или читать, ожидая ввода?(без использования тайм-аута или чтения -t, поскольку это только замедляет процесс)?

Ответы [ 2 ]

1 голос
/ 10 июля 2011

Я думаю, что вы хотите здесь, чтобы избежать чтения с терминала:

if test -t 0; then
    echo "Not reading from terminal.  Pipe through cat if you really want to do this" >&2
    exit 1
fi

Невозможно обнаружить общий случай, когда источник ввода не был предоставлен специально для программы;все перенаправления выполняются оболочкой, и ваша программа не может определить, был ли ее стандартный ввод задан именно ей или унаследован от оболочки.

0 голосов
/ 10 июля 2011

Работай! Спасибо geekosaur! С вашей помощью вот мое решение:

IFS=''
if [ -n "${*}" ]; then
    declare Text="${*}"
elif [ -t 0 ]; then
    TempT="$(mktemp -t 'Entered Text')"
    printf "\n$(Bold Enter text here:)\n\t** Note - To $(Tred stop) reading from stdin, enter $(Tred '/EOT')\n$(Twhite '∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞∞')\n"
    counter=0
    until [[ "$line" == /[Ee][0Oo][Tt] ]]; do
        (( counter++ ))
        read -rp "$(printf "$(Twhite '⋇')$(Taqua "%%7s")$(Twhite '⋇ ')" "$(frame 0 7 "[$counter]" 0 - --no)")" -e line
        echo "$line" >>"$TempT"
    done
    readarray -t EnteredtText <"$TempT"
    ( set -u; [[ -f "$TempT" && "$TempT" =~ ^(/private)?/tmp/ ]] && { mv -f "$TempT" "$HOME/.Trash/" || rm -f "$TempT"; } ) &>'/dev/null'
    IFS=$'\n'
    declare Text="$(echo "${EnteredtText[*]}" | gsed -e '$d')"
else Text="$(cat)"
fi
На этой заметке у меня есть еще один вопрос:

Можно ли (я уверен, что это так) читать блок текста из стандартного ввода без использования файла?

Этот надоедливый цикл while запускает вложенную оболочку. Я попробовал несколько вещей:
exec 7<&0
exec 0<Temp ## Perhaps some process sub? ##
until [[ "$line" == '/EOT' ]]; do
    read line
done >Temp  ## or sub here? ##
exec 0<&7
exec 7<&-
mapfile Text <Temp</code>

while read line; do
    [[ "$line" != '/EOT' ]] && readarray Text <( echo "$line" )
done</code>

exec 7<&0
exec 0<( readarray -u7 Text )
until [[ "$line" == '/EOT' ]]; do
    read -u7 line
done >&7
exec 0<&7
exec 7<&-</code>
Может быть, что-то с командой здесь-<< -type? Я пишу сценарии чуть менее 2 лет, и всю жизнь я не могу понять перенаправление ввода / вывода (помимо основ). Перечитывание главы во многих различных книгах / формах всегда приводит к куче игр вокруг тестирования, я не получаю его [я думаю, вы видите;)], я расстраиваюсь, и, наконец, я нахожу другой способ сделать то, что я хочу. Таинственная команда exec и все файловые дескрипторы, кроме 0/1/2, просто не используются ни в одном из моих сценариев ... но я знаю, что есть способ ответить на этот вопрос без создания файла и последующего удаления.

Спасибо!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...