Preservar líder espaço em branco durante a leitura >> escrevendo um arquivo linha por linha em bash

StackOverflow https://stackoverflow.com/questions/1648055

  •  22-07-2019
  •  | 
  •  

Pergunta

Eu estou tentando fazer um loop através de um diretório de arquivos de texto e combiná-los em um único documento. Isso funciona muito bem, mas os arquivos de texto contêm trechos de código, e toda minha formatação está sendo recolhido para a esquerda. Tudo levando espaços em branco em uma linha é despojado.

#!/bin/sh
OUTPUT="../best_practices.textile"
FILES="../best-practices/*.textile"
for f in "$FILES"
do
  echo "Processing $f file..."
  echo "">$OUTPUT

  cat $f | while read line; do 
      echo "$line">>$OUTPUT
  done
  echo >>$OUTPUT
  echo >>$OUTPUT
done

Eu estou reconhecidamente um noob bash, mas depois de procurar alto e baixo eu não poderia encontrar uma solução adequada. Aparentemente BASH odeia o espaço em branco levando em geral.

Foi útil?

Solução

Em vez de:

cat $f | while read line; do 
    echo "$line">>$OUTPUT
done

Faça o seguinte:

cat $f >>$OUTPUT

(Se há uma razão que você precisa fazer as coisas linha por linha que seria bom para incluir que na pergunta.)

Outras dicas

Como os outros têm para fora pontas, usando gato ou awk em vez de um loop de leitura-eco é uma maneira muito melhor de fazer isso - evita o problema-aparar espaço em branco (e um par de outros que você não ter tropeçado em cima), corre mais rápido, e pelo menos com o gato, é simplesmente um código mais limpo. No entanto, eu gostaria de tomar uma facada no sentido de conseguir o loop de leitura-echo para a direita trabalho.

Em primeiro lugar, o problema-aparar espaço em branco: o comando ler automaticamente cortado esquerda e à direita espaço em branco; isto pode ser corrigido alterando a sua definição de espaço em branco ajustando a variável de IFS para branco. Além disso, leitura assume que uma barra invertida no final da linha significa que a próxima linha é uma continuação, e devem ser unidas em conjunto com este um; para corrigir isso, use a sua bandeira -r (raw). O terceiro problema aqui é que muitas implementações de eco interpretar sequências de escape na cadeia (por exemplo, podem transformar \ n em uma nova linha real); para corrigir isso, use printf vez. Finalmente, assim como uma regra de higiene scripting geral, você não deve usar gato quando você realmente não precisa; usar redirecionamento de entrada em seu lugar. Com essas mudanças, o loop olhares internos como este:

while IFS='' read -r line; do 
  printf "%s\n" "$line">>$OUTPUT
done <$f

... também há um par de outros problemas com o roteiro envolvente: a linha que tenta definir os arquivos como a lista de arquivos .textile disponíveis tem aspas em torno dele, o que significa que nunca se expandiu em uma lista real de arquivos . A melhor maneira de fazer isso é usar um array:

FILES=(../best-practices/*.textile)
...
for f in "${FILES[@]}"

(e todas as ocorrências de $ f deve estar em aspas no caso de qualquer um dos nomes de arquivos têm espaços ou outros personagens engraçados em si - deve realmente fazer isso com $ OUTPUT bem, embora desde que é definido no script é realmente seguro para deixar de fora.)

Finalmente, há uma echo "">$OUTPUT perto do topo do laço-over-arquivos que vai apagar o arquivo de saída de cada vez através de (ou seja, no final, ele contém apenas o último arquivo .textile); isso precisa ser movido para antes do loop. Eu não tenho certeza se a intenção aqui foi a de colocar uma linha em branco no início do arquivo, ou três linhas em branco entre arquivos (e uma no início e dois no final), então eu não sei exatamente o que a substituição apropriado é. De qualquer forma, aqui está o que eu posso com após a fixação todos estes problemas:

#!/bin/sh
OUTPUT="../best_practices.textile"
FILES=(../best-practices/*.textile)

: >"$OUTPUT"
for f in "${FILES[@]}"
do
  echo "Processing $f file..."
  echo >>"$OUTPUT"

  while IFS='' read -r line; do 
    printf "%s\n" "$line">>"$OUTPUT"
  done <"$f"

  echo >>"$OUTPUT"
  echo >>"$OUTPUT"
done

que é uma maneira excessivamente caros de combinar arquivos.

cat ../best-practices/*.textile >  ../best_practices.textile

Se você quiser adicionar um em branco (nova linha) para cada arquivo como você concatenar, use awk

awk 'FNR==1{print "">"out.txt"}{print > "out.txt" }' *.textile

ou

awk 'FNR==1{print ""}{print}' file* > out.txt

Isto permite-lhe novas linhas Intercale entre cada arquivo de entrada como você tem feito no seu script original:

for f in $FILES; do echo -ne '\n\n' | cat "$f" -; done > $OUTPUT

Note que $FILES é não cotadas para este ao trabalho (caso contrário, as novas linhas extras aparecer apenas uma vez no final de toda a saída), mas $f devem ser arredondadas para espaços protegerá em nomes de arquivos, se eles existirem.

A resposta correta, imo, é este , reproduzido abaixo:

while IFS= read line; do
    check=${line:0:1}
done < file.txt

Note que ele vai cuidar de situações em que a entrada é canalizada de outro comando, e não apenas de um arquivo real.

Note que você também pode simplificar o redirecionamento como mostrado abaixo.

#!/bin/bash
OUTPUT="../best_practices.textile"
FILES="../best-practices/*.textile"
for f in "$FILES"
do
  echo "Processing $f file..."
  {
  echo

  while IFS= read line; do 
      echo "$line"
  done < $f
  echo
  echo;
  } > $OUTPUT
done
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top