bash: Solicitando entrada do usuário durante a leitura do arquivo

3

Estou trabalhando em um script bash que analisa um arquivo separado por tabulações. Se o arquivo contiver a palavra "prompt", o script deve pedir ao usuário para inserir um valor.

Parece que, ao ler o arquivo, o comando "read" não consegue ler da entrada padrão, pois a "leitura" é simplesmente ignorada.

Alguém tem um trabalho em torno de fazer tanto a leitura de um arquivo, bem como de stdin?

Observação: o script deve ser executado no Git Bash e no MacOS.

Abaixo está um pequeno exemplo de código que falha:

#!/bin/bash

#for debugging
set "-x"


while IFS=$'\r' read -r line || [[ -n "$line" ]]; do
  [[ -z $line ]] && continue

  IFS=$'\t' read -a fields <<<"$line"

  command=${fields[0]}

  echo "PROCESSING "$command
  if [[ "prompt" = $command ]]; then
    read -p 'Please enter a value: ' aValue
    echo
  else 
    echo "Doing something else for "$command
  fi
done < "$1"

Saída:

$ ./promptTest.sh promptTest.tsv
+ IFS=$'\r'
+ read -r line
+ [[ -z something       else ]]
+ IFS=' '
+ read -a fields
+ command=something
+ echo 'PROCESSING something'
PROCESSING something
+ [[ prompt = something ]]
+ echo 'Doing something else for something'
Doing something else for something
+ IFS=$'\r'
+ read -r line
+ [[ -z prompt ]]
+ IFS=' '
+ read -a fields
+ command=prompt
+ echo 'PROCESSING prompt'
PROCESSING prompt
+ [[ prompt = prompt ]]
+ read -p 'Please enter a value: ' aValue
+ echo

+ IFS=$'\r'
+ read -r line
+ [[ -n '' ]]

Exemplo de arquivo tsv:

$ cat promptTest.tsv
something       else
prompt
otherthing       nelse
    
por Bernie Lenz 05.07.2018 / 16:43

3 respostas

2

A maneira mais simples é usar /dev/tty como leitura para entrada de teclado.

Por exemplo:

#!/bin/bash

echo hello | while read line
do
  echo We read the line: $line
  echo is this correct?
  read answer < /dev/tty
  echo You responded $answer
done

Isso quebra se você não executar isso em um terminal e não permitir que a entrada seja redirecionada para o programa, mas funcionará muito bem.

Em geral, você pode pegar um novo identificador de arquivo baseado no stdin original e ler a partir dele. Observe a linha exec e o read

#!/bin/bash

exec 3<&0

echo hello | while read line
do
  echo We read the line: $line
  echo is this correct?
  read answer <&3
  echo You responded $answer
done

Em ambos os casos, o programa parece um pouco com:

% ./y
We read the line: hello
is this correct?
yes
You responded yes

A segunda variação permite que a entrada também seja redirecionada

% echo yes | ./y
We read the line: hello
is this correct?
You responded yes
    
por 05.07.2018 / 16:56
0

Este SO Q & A intitulado: Como ler de um arquivo ou stdin no Bash? mostra este método que destaca como fazer o que parece que você está procurando:

while read line
do
  echo "$line"
done < "${1:-/dev/stdin}"
    
por 05.07.2018 / 16:49
0

Você pode usar a entrada padrão antes de fazer o redirecionamento.

#!/bin/bash
# Assuming here fd 4 is unused.  Dup file descriptor 0 (stdin) to
# file descriptor 4
exec 4<&0 

while read x; do # Read from stdin (the file b/c of redirect below)
    echo "From $1: $x"
    read y <&4 # Read from file descriptor 4 (the original stdin)
    echo "From keyboard: $y"
done < "$1"
    
por 05.07.2018 / 17:00

Tags