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
.