2

I tried to compare two files and output customized string. Following is my script.

#!/bin/bash

./${1} > tmp

if ! diff -q tmp ans.txt &>/dev/null; then
    >&2 echo "different"
else
    >&2 echo "same"
fi

When I execute script, I get:

sh cmp.sh ans.txt
different
Files tmp and ans.txt differ

The weird part is when I type diff -q tmp ans.txt &>/dev/null. No output will show up.

How to fix it(I don't want line:"Files tmp and ans.txt differ")? Thanks!

Timur Shtatland
  • 12,024
  • 2
  • 30
  • 47
Steven
  • 811
  • 4
  • 23
  • use `cmp` to compare files. `./${1} > tmp` should result in running `ans.txt` as a command - don't you get "permission denied" or other error on that line? – KamilCuk Sep 11 '20 at 14:23
  • yes, I get permission denied. I just want figure out what wrong with this output first. – Steven Sep 11 '20 at 14:26
  • 1
    You are executing your command with `sh` - what is your implementation of `sh`? Does the problem persist when running under `bash`? – KamilCuk Sep 11 '20 at 14:28
  • I type this `sh cmp.sh ans.txt` – Steven Sep 11 '20 at 14:29
  • Then type `bash cmp.sh ans.txt`. Does the problem persist? What is `sh` on your system? – KamilCuk Sep 11 '20 at 14:30
  • u r right, `bash` works good. BTW, how to check what is my `sh` on my system. – Steven Sep 11 '20 at 14:31
  • Most probably `sh --version`, if you're lucky. – KamilCuk Sep 11 '20 at 14:31
  • `diff` is really the wrong tool for this job -- it's expensive, because it's trying to calculate the shortest possible transform to change file-A into file-B; depending on the files' size and content, that can potentially be a very slow operation. If you don't _want_ that transform, use a tool that doesn't do all the work to generate it! The earlier advice to use `cmp` instead is spot-on. – Charles Duffy Sep 11 '20 at 14:32
  • @CharlesDuffy So `cmp` is better approach for this job? – Steven Sep 11 '20 at 14:33
  • 2
    Yes, `cmp` is better than `diff` when you only want to know if files are different, but don't care about generating a patch to transform one file to the other. – Charles Duffy Sep 11 '20 at 14:34
  • 1
    ...also, `cmp -s` exists so you don't need to redirect stdout and stderr at all; `if cmp -s file1 file2; then echo "Files are identical"; else echo "Files are different"; fi` – Charles Duffy Sep 11 '20 at 14:35
  • @KamilCuk my shell version is **GNU bash, version 4.4.19(1)-release (x86_64-pc-linux-gnu)**. Is that any different with `bash`? – Steven Sep 11 '20 at 14:35
  • 1
    @Steven, `/bin/sh` and `/bin/bash` behave differently _even if they're both provided by bash_ (but it's on systems where `/bin/sh` is provided by `ash`, `dash`, etc. that the differences get really big). If you're using bash-only syntax, _always_ use `#!/bin/bash` or `#!/usr/bin/env bash`, not `#!/bin/sh`. – Charles Duffy Sep 11 '20 at 14:36
  • Closely related to this question: [Fastest way to tell if two files have the same contents in unix/linux](https://stackoverflow.com/questions/12900538/fastest-way-to-tell-if-two-files-have-the-same-contents-in-unix-linux) – Charles Duffy Sep 11 '20 at 14:37

1 Answers1

3

Most probably the version of sh you are using doesn't understand the bash (deprecated/obsolete) extension &> that redirect both stdout and stderr at the same time. In posix shell the command &>/dev/null I think is parsed as { command & }; > /dev/null - it results in running the command in the background & and the > /dev/null part I think is ignored, as it just redirect output of a nonexistent command - it's valid syntax, but executes nothing. Because running the command in the background succeeds, the if always succeeds.

Prefer not to use &> - use >/dev/null 2>&1 instead. Use diff to pretty print the files comparison. Use cmp in batch scripts to compare files.

if cmp -s tmp ans.txt; then
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 1
    Thank you for for mentioning this page: Obsolete and deprecated syntax [Bash Hackers Wiki (DEV 20200708T2203)]: https://wiki-dev.bash-hackers.org/scripting/obsolete . It is worth reading in detail. Note that these guidelines deprecate not only `&>`, but also `set -euo pipefail`, previously recommended by some, e.g., Bash Strict Mode: http://redsymbol.net/articles/unofficial-bash-strict-mode/ . Not everyone may be aware of these changing recommendations. – Timur Shtatland Sep 11 '20 at 15:31