Adicionar conteúdo faltante de um arquivo para outro

3
FILE1=$1   
FILE2=$2

if [ ! -e "$FILE2" ]
then
   touch $FILE2
fi

while read line; do
  if grep -q "$line" $FILE2
  then
    echo
  else
    echo $line >> $FILE2
  fi
done < $FILE1

Eu quero adicionar as linhas de FILE1 a FILE2 se elas não estiverem presentes em FILE2. Meu objetivo é listar as linhas do FILE1 e adicioná-las ao FILE2 se as linhas não estiverem presentes no FILE2.

O que há de errado?

    
por Questioner 29.10.2014 / 14:40

1 resposta

4

awk 'NR == FNR {first[$0];next}; ! ($0 in first)' "$file2" "$file1" >> "$file2"

Isso pressupõe que os nomes dos arquivos não contenham = caracteres. Se pudessem, em sistemas que suportam / dev / fd / n, você poderia fazer:

awk 'NR == FNR {first[$0];next}; ! ($0 in first)' \
  /dev/fd/3 3< "$file2" /dev/fd/4 4< "$file1" >> "$file2"

Observe que, se as linhas ocorrerem várias vezes em $file1 e não em $file2 , todas as ocorrências serão anexadas a $file2 . Se não é isso que você quer, então:

awk 'NR != FNR && ! ($0 in seen); {seen[$0]}' \
  /dev/fd/3 3< "$file2" /dev/fd/4 4< "$file1" >> "$file2"

Se as linhas forem exclusivas e você não se importar com a saída sendo classificada, você também poderá:

LC_ALL=C sort -uo "$file2" "$file1" "$file2"

Quanto ao que está errado no seu código:

FILE1=$1   
FILE2=$2

todas as variáveis em maiúsculas são por convenção reservada para variáveis ambiente (variáveis exportadas para o ambiente que é um espaço de nomes compartilhado), portanto é uma boa prática não usar todas as variáveis em maiúsculas para meras variáveis de shell. p>

if [ ! -e "$FILE2" ]
then
   touch $FILE2
fi

Você o citou nos argumentos para [ , mas não para touch , por quê?

Além disso, observe que é

touch "$file_or_option"

Se você quiser que a variável seja sempre tratada como um argumento de arquivo, será necessário:

touch -- "$file2"

Além disso, o if ! condition; then do-something-to-make-it-true costuma ser uma prática ruim e está sujeito à condição de corrida. É melhor fazer enforce-the-condition || die-if-couldn't . Então aqui:

touch -- "$file2" || exit

Mas, de qualquer forma, o redirecionamento >> criará o arquivo (e retornará um erro se não puder).

while read line; do

Quando você começa a usar loops em shells especialmente while read loops , isso geralmente significa que você está indo para a abordagem errada. Você vai executar vários comandos seqüencialmente para cada linha de um arquivo, o que não é como você faz o script de shell. Em vez disso, você deseja executar alguns comandos especializados uma vez cooperando com a tarefa.

Primeiro, para ler um line em shells POSIX, a sintaxe é

IFS= read -r line

não read line

  if grep -q "$line" $FILE2

Novamente, é:

grep -q "$option_or_regexp"

Ou:

grep -q -- "$regexp"

Ou:

grep -qe "$regexp"

Se você quiser pesquisar por strings em vez de corresponder a regexps, precisará da opção -F e, se quiser corresponder as linhas como um todo, precisará de -x . Então:

grep -qxFe "$line" < "$file2"
  then
    echo
  else
    echo $line >> $FILE2

Novamente, você está aplicando o operador split + glob ao conteúdo de $line (e a $FILE2 se estiver usando bash não no modo sh).

Além disso, echo não pode ser usado para dados arbitrários. Você precisa de:

printf '%s\n' "$line" >> "$file2"

Aqui. Mas é um desperdício reabrir $file2 para cada passagem no loop onde você poderia ter feito uma vez para todo o loop.

  fi
done < $FILE1

Novamente:

done < "$file1"

se estiver usando bash .

    
por 29.10.2014 / 15:54