if tty>/dev/null ; then
read x
else
read x<fic
fi
означает, что fic
будет прочитан только тогда, когда tty
вернет ошибку. Что произойдет, когда он обнаружит, что его stdin
не является терминалом. Обычно это происходит либо когда сценарий находится в конвейере, либо когда вы запускаете его через nohup
, либо как задание cron, либо удаленно через ssh -T
...
Использование [-t 0]
как условие почти такое же (и быстрее, поскольку [
он же тест
обычно является встроенным в наши дни).
Есть много способов решить эту проблему, но я пойду с того, что вы начали.
Во-первых, не забудьте начать скрипт со строкой интерпретатора ("shebang"):
#!/bin/bash
echo "Enter file 1:"
read file1
echo "Enter file 2:"
read file2
cmp $file1 $file2 > newfile
На этом этапе вы можете проверить пару вещей:
если новый файл не пустой, файлы различаются
if [ ! -s newfile ]; then
rm -i $file2
fi
Проверить код выхода для операции cmp. Если он равен 0, файлы совпадают.
if [ `echo $?` == 0 ]; then
rm -i $file2
fi
Кроме того, ваша команда wc не совсем работает. Попробуйте запустить его вне скрипта. Вы получаете ожидаемый результат?
Сначала добавьте шебанг #!
вверху, например#!/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"
в противном случае он может удалить несколько файлов.
Непосредственной проблемой в вашем коде является синтаксическая ошибка при чтении строки
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 для этой утилиты .
Следующий код проверяет, является ли вывод null
или пустым. Если он пустой, то файлы одинаковые, иначе разные.
output=`echo | cmp -b $file1 $file2`
if [[ -z $output ]]
then
echo "Same"
rm $file2
else
echo "Diff"
fi
Вот моя реализация.
#! /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'?