Como fazer o bash embutido “read” ignora linhas comentadas ou vazias?

2

(Por simplicidade, assumirei que o arquivo a ler é o primeiro argumento - $1 .)

Eu posso fazer o que eu quero externamente com:

tempfile=$(mktemp)
awk '/^#/ {next}; NF == 0 {next}; {print}' "$1" > $tempfile
while read var1 var2 var3 var4 < $tempfile; do
  # stuff with var1, etc.
done

No entanto, parece absurdo precisar chamar awk toda vez que eu analisar o arquivo de configuração. Existe uma maneira de fazer com que read ignore linhas comentadas ou apenas espaços em branco em um arquivo, sem binários externos / possíveis problemas de desempenho?

As respostas até agora são bastante úteis! Para esclarecer, eu não quero usar um arquivo temporário, mas eu faço quero ler a configuração de um arquivo , não do padrão em. que eu posso usar um redirecionamento de entrada quando eu chamo o script, mas por várias razões que não funcionam na minha circunstância.

Eu quero codificar a entrada para ler, por exemplo:

configfile="/opt/myconfigfile.txt"
[ $# -gt 0 ] && [ -r "$1" ] && configfile="$1"

while read var1 var2 var3 var4 < "$configfile" ; do
  ...

Mas quando eu tento isso, apenas leio a primeira linha de configfile até que eu mate o processo.

Talvez seja essa a sua própria pergunta ... mas provavelmente é uma mudança de linha única do que estou fazendo. Onde está o meu erro?

    
por Wildcard 20.11.2015 / 20:48

2 respostas

2

Você não precisa de um arquivo temporário para fazer isso, e sed (ou awk) são muito mais flexíveis no processamento de comentários do que uma declaração de caso shell.

Por exemplo:

configfile='/opt/myconfigfile.txt'
[ $# -gt 0 ] && [ -r "$1" ] && configfile="$1"

sed -e 's/[[:space:]]*#.*// ; /^[[:space:]]*$/d' "$configfile" |
    while read var1 var2 var3 var4; do
      # stuff with var1, etc.
    done

Isso remove comentários (com ou sem espaços em branco iniciais) e exclui linhas vazias da entrada antes de inseri-la no loop while. Ele lida com comentários nas linhas por si e comentários anexados ao final da linha:

# full-line comment
# var1 var2 var3 var4
abc 123 xyz def # comment here

Chamar sed ou awk para tarefas como esta não é "absurdo", é perfeitamente normal. É para isso que servem essas ferramentas. Quanto ao desempenho, eu apostaria que em nada além de arquivos de entrada muito pequenos, a versão sed seria muito mais rápida. O piping para sed tem alguma sobrecarga de inicialização, mas é executado muito rápido, enquanto o shell é lento.

    
por 20.11.2015 / 21:41
1

Isso funciona porque read quebra tudo no espaço em branco (IFS), portanto, se var1 estiver vazio ou começar com '#', ignore-o.

while read var1 var2 var3 var4; do
   case $var1 in
       ''|\#*) continue ;;         # skip blank lines and lines starting with #
   esac
   echo "var1: '$var1'"
   # stuff with var1, etc.
done < "${1:-default_config_file}"

Em seguida, a entrada deve ser redirecionada para o loop em vez de para a lista de comandos while . O "${1:-default_config_file}" se expande para o primeiro parâmetro da linha de comando, se não estiver vazio. Caso contrário, ele será expandido para default_config_file . Você também pode usar a expansão de variável, etc. na sequência de valor padrão.

Como você está interessado em minimizar o pré-processamento, acho que isso é equivalente, mas também remove todos os comentários:

while read line; do
    echo "${line%%#*}" | {
        read var1 var2 var3 var4
        [ -z "$var1" ] && continue
        # stuff with var1, etc.
        for i in 1 2 3 4; do eval echo "\"var$i: \$var$i\""; done  #debug only!
    }
done < "${1:-default_config_file}"

Isso usa o recurso de processamento de substring de expansão de parâmetros do shell. ${line%%#*} expande para o valor original de line , exceto o primeiro # e tudo depois que ele é removido. Carregue isso em var1-4 e continue como de costume. O teste para continue encurta porque agora só precisamos verificar se há uma string vazia, não # .

    
por 20.11.2015 / 20:55