あるファイルの欠落コンテンツを別のファイルに追加する

あるファイルの欠落コンテンツを別のファイルに追加する
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

FILE1の行がFILE2にない場合は、FILE2に追加したいと思います。私の目的は、FILE1の行をリストし、その行がFILE2にない場合はFILE2に追加することです。

何が問題なの?

ベストアンサー1

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

ファイル名に=文字がないとします。可能であれば、/dev/fd/n をサポートするシステムで次のことができます。

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

$file1行がで複数回発生し、発生しない場合は、$file2すべての発生がに追加されます$file2。これが望ましくない場合は、次のようにします。

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

行が一意で出力ソートに問題がない場合は、次のこともできます。

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

コードにどのような問題があるか:

FILE1=$1   
FILE2=$2

通常、すべての大文字変数は予約されています。環境変数(共有ネームスペースとして環境にエクスポートされた変数)なので、シェル変数にのみ大文字変数を使用しないことをお勧めします。

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

のパラメータでは参照しましたが、パラメータ[では参照しませんでしたtouch。なぜですか?

またそうですので参考にしてください

touch "$file_or_option"

変数が常にファイルパラメータとして処理されるようにするには、次の手順を実行する必要があります。

touch -- "$file2"

そしてこれはif ! condition; then do-something-to-make-it-true一般的に悪い習慣であり、競争条件の影響を受けます。これを行う方が良いですenforce-the-condition || die-if-couldn't。だからここにあります:

touch -- "$file2" || exit

ただし、それに関係なく、>>リダイレクトはファイルを生成します(生成できない場合はエラーを返します)。

while read line; do

シェルでループを使い始めるとき特にwhile readループ、これは通常、間違ったアプローチを取っていることを意味します。ファイルの各行に対して複数のコマンドを順次実行しますが、これはシェルスクリプトの作成方法ではありません。代わりに、特定の特殊コマンドを一度実行したいと思います。協力仕事に。

まず、POSIXシェルからaを読むには、line構文は次のとおりです。

IFS= read -r line

いいえ read line

  if grep -q "$line" $FILE2

繰り返しますが、それは次のとおりです。

grep -q "$option_or_regexp"

または:

grep -q -- "$regexp"

または:

grep -qe "$regexp"

正規表現を一致させるのではなく文字列を検索したい場合、-Fまたは行全体を一致させたい場合は、このオプションが必要です-x

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

同様に、Split + glob演算子を適用するターゲット($lineshモードで$FILE2使用されていない場合bash)。

また、echo任意のデータと併用することはできません。以下を行う必要があります。

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

ここで。しかし、$file2ループ全体に対して一度しか実行できませんでしたが、ループの各パスに対して再度開くと無駄です。

  fi
done < $FILE1

再:

done < "$file1"

使用されている場合bash

おすすめ記事