2つのファイルを比較して同じであると判断した場合は、シェルスクリプトを使用して重複ファイルを削除するようにユーザーに要求するにはどうすればよいですか?

2つのファイルを比較して同じであると判断した場合は、シェルスクリプトを使用して重複ファイルを削除するようにユーザーに要求するにはどうすればよいですか?

私はLinuxを学んでいますが、この質問を宿題にしていますが、シェルモードで2つのファイルの内容を比較する方法に関する問題は解決できません。 (ここでは、両方のファイルがこのようなテキストコンテンツを持っていると仮定できます。たとえば、ファイル1の$ cat> f1)

$ cat duplicate_file.sh
echo "Enter file 1:"
read file1
echo "Enter file 2:"
read file2
cmp $file1 $file2 > newfile
x=` wc newfile | cut -d" " -f2 `
if [` $x -eq 0 `]
then
rm -i $file2
fi

このプログラムを書きましたが、うまくいきません!それでは提案がありますか?

ベストアンサー1

コードの即時の問題は、読み出し行の構文エラーです。

if [` $x -eq 0 `]

[との引数はスペース文字で]区切る必要があります。また、その行のコマンド置換は値をコマンドとして実行しよ`$x -eq 0`うとするため、意味がありません。$x

また、スクリプトが空白文字とファイル名のグロービングパターンを含むファイル名を処理できないという引用符なしの変数拡張の問題もあります。

また、スクリプトは無条件にファイルを不必要に破損させ(newfile既存のディレクトリ名の場合は失敗します) - 行がありません。newfile#!


ユーザーに対話的にファイルパスを要求することは意味がありません。ユーザーは、コマンドラインでシェルのファイル名補完を使用して、ファイルのパス名を両方のオペランドとして指定することをお勧めします。

$ ./script.sh some/path/file1 some/other/path/file2

この方法でスクリプトを実行すると、スクリプト内とという"$1"2つのパス名を使用できます"$2"

cmp実用的な事項一時ファイルを作成しなくても、このスクリプト内で使用できます。出力をリダイレクトするのではなく、対応するオプション(「silent」)を使用して自動的に作成し、終了ステータスを-s使用して2つのファイルが同じであることを確認します。

スクリプトは次のとおりです。

#!/bin/sh

if cmp -s -- "$1" "$2"; then
    rm -i -- "$2"
fi

またはより短く、

#!/bin/sh

cmp -s -- "$1" "$2" && rm -i -- "$2"

rm -i参照するファイルの内容が最初のパス名と同じ場合、指定された2つのパス名のうち2番目のパスを呼び出します。--ダッシュで始まるファイル名をオプションセットとして解釈したくない場合は、コマンドが必要です。cmprm

これ質問このスクリプトを提供する場合は、独自のスクリプトのように使用してください。同じパス名が2回つまり、ファイルを自分と比較すると削除を提案します。

したがって、両方のパス名が両方のパスを参照していることを確認する必要があります。その他文書。

2つのパス名文字列を比較することでこれを行うことができます。

#!/bin/sh

if [ "$1" != "$2" ] && cmp -s -- "$1" "$2"; then
    rm -i -- "$2"
fi

./fileこれは一部のアプリケーションでは十分ですが、他のパス(vs filevs/path/to/fileなど)で指定されたシンボリックリンクやファイルは考慮されません。ほとんどのシェルでは非標準(しかし、-efテスト(「同じファイル」)は、2つのパス名が同じファイルを参照するかどうかをテストします(同じinode番号とデバイスなので、同じファイルへの2つのハードリンクに対してもtrueを返します)。

#!/bin/bash

if ! [ "$1" -ef "$2" ] && cmp -s -- "$1" "$2"; then
    rm -i -- "$2"
fi

または、

#!/bin/bash

! [ "$1" -ef "$2" ] && cmp -s -- "$1" "$2" && rm -i -- "$2"

そして、いくつかの完全性チェックを実行します(また、-efテストを完全性チェックセクションに移動します)。

#!/bin/bash

if [ "$#" -ne 2 ]; then
    # did not get exactly two arguments
    printf 'Usage:\n\t%s file1 file2\n' "$0" >&2
    exit 1
elif [ ! -f "$1" ] || [ ! -f "$2" ]; then
    echo 'One of the files does not exist (or is not a regular file)' >&2
    exit 1
elif [ "$1" -ef "$2" ]; then
    printf '%s and %s refer to the same file\n' "$1" "$2" >&2
    exit 1
fi

cmp -s -- "$1" "$2" && rm -i -- "$2"

パス名にスペースが含まれることは珍しくないため、引用変数の拡張が重要です(macOSではこれは一般的です)。また、二重引用符変数拡張は、その変数がシェルグローブパターンとして解釈されるのを防ぎます(たとえば、コードは名前付きファイルでは実行されません*)。また、#!スクリプトで-lineを適切に使用したことを確認してください。

宿題なら必要対話的に2つのファイルのパス名を読み取り、合計を空の文字列にread -r設定します。IFSこれにより、スペースまたはタブで始まるか終わり、文字を含むパス名を読み取ることができます\(改行文字を含むパス名は指定できません)。

#!/bin/bash

IFS= read -p '1st pathname: ' -r p1
IFS= read -p '2nd pathname: ' -r p2

if [ ! -f "$p1" ] || [ ! -f "$p2" ]; then
    echo 'One of the files does not exist (or is not a regular file)' >&2
    exit 1
elif [ "$p1" -ef "$p2" ]; then
    printf '%s and %s refer to the same file\n' "$p1" "$p2" >&2
    exit 1
fi

cmp -s -- "$p1" "$p2" && rm -i -- "$p2"

関連:


自分のコードのように、ある時点でファイルが空であることを確認する必要がある場合は、wcそのファイルを呼び出さないでください(ファイル全体を読む必要があるため、非効率的です)。代わりに-sテストを使用してください。

if [ -s "$pathname" ]; then
    printf '%s has non-zero size\n' "$pathname"
else
    printf '%s is empty (or does not exist)\n' "$pathname"
fi

man testシステムを参照するか、以下を参照してください。このユーティリティのPOSIX規格

おすすめ記事