Как сравнить два файла и если найдено равным, попросите пользователя удалить дубликат файл с помощью скрипта Shell?

if tty>/dev/null ; then
    read x
else
    read x<fic
fi

означает, что fic будет прочитан только тогда, когда tty вернет ошибку. Что произойдет, когда он обнаружит, что его stdin не является терминалом. Обычно это происходит либо когда сценарий находится в конвейере, либо когда вы запускаете его через nohup , либо как задание cron, либо удаленно через ssh -T ...

Использование [-t 0] как условие почти такое же (и быстрее, поскольку [ он же тест обычно является встроенным в наши дни).

2
10.11.2018, 08:13
5 ответов

Есть много способов решить эту проблему, но я пойду с того, что вы начали.

Во-первых, не забудьте начать скрипт со строкой интерпретатора ("shebang"):

#!/bin/bash

echo "Enter file 1:"
read file1
echo "Enter file 2:"
read file2
cmp $file1 $file2 > newfile

На этом этапе вы можете проверить пару вещей:

  1. если новый файл не пустой, файлы различаются

    if [ ! -s newfile ]; then
      rm -i $file2
    fi
    
  2. Проверить код выхода для операции cmp. Если он равен 0, файлы совпадают.

    if [ `echo $?` == 0 ]; then
      rm -i $file2
    fi
    

Кроме того, ваша команда wc не совсем работает. Попробуйте запустить его вне скрипта. Вы получаете ожидаемый результат?

0
27.01.2020, 21:52

Сначала добавьте шебанг #!вверху, например#!/bin/bash

У вас две ошибки:

Вместо

cmp $file1 $file2 > newfile,

так и должно быть

cmp -- "$file1" "$file2" > newfile

, так как эти значения этих переменных могут содержать пробелы, табуляции, символы новой строки (символы $IFS), *, [,?(подстановочные знаки )в них или могут начинаться с -.

Вторая ошибка:

Вместо

if [` $x -eq 0 `]

так и должно быть

if [ "$x" -eq 0 ].

В противном случае вы получите ошибку

bash: 0: command not found.

Кроме того, если в именах файлов есть пробелы или подстановочные знаки, это должно быть:

rm -i -- "$file2"в противном случае он может удалить несколько файлов.

3
27.01.2020, 21:52

Непосредственной проблемой в вашем коде является синтаксическая ошибка при чтении строки

if [` $x -eq 0 `]

[и ]должны быть отделены от аргументов внутри символом пробела. Кроме того, подстановка команды в этой строке, `$x -eq 0`, не имеет смысла, так как она будет пытаться запустить значение $xкак команду.

У вас также есть проблемы с не -заключением в кавычки ваших расширений переменных, что лишает ваш сценарий возможности работать с именами файлов, содержащими символы пробела и шаблоны подстановки имен файлов.

Сценарий также безоговорочно затирает файл newfileбез необходимости (и завершится ошибкой, если newfileявляется именем существующего каталога )и в нем отсутствует строка #!-.


Нет смысла интерактивно запрашивать у пользователя пути к файлам. Было бы лучше, если бы пользователь мог использовать завершение имени файла оболочки в командной строке и указывать пути к файлам в виде двух операндов :

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

Если запустить скрипт таким образом, два пути будут доступны внутри скрипта как "$1"и "$2".

Утилиту cmpможно использовать в этом скрипте без создания временного файла. Вместо перенаправления его вывода сделайте его тихим, используя параметр -s(для «тихого» )и используйте его статус выхода, чтобы определить, идентичны ли два файла или нет.

Скрипт будет выглядеть так

#!/bin/sh

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

Или, короче,

#!/bin/sh

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

Это вызовет rm -iдля второго из двух заданных путей, если он ссылается на файл с идентичным содержимым, что и первый путь. Параметр --в командах cmpи rmнеобходим, чтобы избежать интерпретации имени файла, начинающегося с дефиса, как набора параметров.

Проблема с этим скриптом, как и с вашим собственным скриптом,заключается в том, что если вы дадите ему один и тот же путь дважды , т.е. вы сравните файл с самим собой, то он предложит удалить его.

Следовательно, мы также должны убедиться, что два пути относятся к двум разным файлам.

Это можно сделать, сравнив две строки имени пути друг с другом:

#!/bin/sh

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

Этого может быть достаточно для большинства приложений, но не учитываются символические ссылки. В некоторых оболочках вы также можете использовать нестандартный --efтест («равный файл» ), который проверяет, относятся ли два пути к одному и тому же файлу (один и тот же i -номер узла и устройство):

#!/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, это очень распространено ). Расширения переменных в двойных кавычках также предотвращают их интерпретацию как шаблоны подстановки оболочки (ваш код, например, не будет работать с файлом с именем*). Также обратите внимание на использование строки #!-, соответствующей сценарию.

Если ваше домашнее задание требует чтения путей к двум файлам в интерактивном режиме, сделайте это с 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 для этой утилиты .

5
27.01.2020, 21:52

Следующий код проверяет, является ли вывод nullили пустым. Если он пустой, то файлы одинаковые, иначе разные.

output=`echo | cmp -b $file1 $file2`

if [[ -z $output ]]
then
    echo "Same"
    rm $file2
else
    echo "Diff"    
fi
0
02.05.2020, 05:50

Вот моя реализация.

#! /bin/bash

echo -n "Enter file1: " 
read file1
echo -n "Enter file2: " 
read file2

if cmp -s -- "$file1" "$file2"
then
      echo same 
      rm -i -- "$file2"
else 
      echo different
fi

У меня есть 3 файла :var1.txt, var2.txt, var3.txt.

  • var1отличается отvar2
  • var2совпадает сvar3

Запуск приведенного выше сценария(com.sh)для этих файлов приводит к:

$ bash com.sh
Enter file1: var1.txt 
Enter file2: var2.txt 
different

$ bash com.sh
Enter file1: var2.txt 
Enter file2: var3.txt 
same
rm: remove regular file 'var3.txt'?
0
11.07.2020, 08:00

Теги

Похожие вопросы