103

I am using make and tar to backup. When executing makefile, tar command shows file changed as we read it. In this case,

  • the tar package is ok when the warning comes up
  • but it stops the tar command for the following backup
  • the file showing the warning in fact doesn't change -- it is really strange that the warning comes up
  • the files showing the warning come up randomly, I mean, everytime I run my makefile, the files showing the warning are different
  • --ignore-failed-read doesn't help. I am using tar 1.23 in MinGW
  • I just changed my computer to WIN7 64 bit. The script works well in old WIN7 32 bit. But the tar version is not as new as the 1.23.

How can I stop the tar's warning to stop my backup following the warning?


Edit-2: it might be the reason

As I said above, the bash shell script worked well in my old computer. Comparing with the old computer, the msys version is different. So is the version of tar command. In the old computer, tar is 1.13.19 and it is 1.23 in the new computer. I copied the old tar command without copying its dependency msys-1.0.dll to the new computer and renamed it tar_old. And I also updated the tar command in the shell script and run the script. Then everything is ok. So, it seemed that the problem is the tar command. I am sure that there is no any file changed when taring. Is it a bug for tar command in new version? I don't know.


Edit-1: add more details

The backup is invoked by a bash shell script. It scans the target directory and builds makefile then invokes make to use tar command for backup. Followed is a typical makefile built by the bash shell script.

#--------------------------------------------
# backup VC
#--------------------------------------------
# the program for packing
PACK_TOOL=tar

# the option for packing tool
PACK_OPTION=cjvf

# M$: C driver
WIN_C_DIR=c:

# M$: D driver
WIN_D_DIR=d:

# M$: where the software is
WIN_PRG_DIR=wuyu/tools
# WIN_PRG_DIR=

# where to save the backup files
BAKDIR=/home/Wu.Y/MS_bak_MSYS

VC_FRAMEWORK=/home/Wu.Y/MS_bak_MSYS/tools/VC/VC_framework.tar.bz2
VC_2010=/home/Wu.Y/MS_bak_MSYS/tools/VC/VC_2010.tar.bz2

.PHONY: all

all: $(VC_FRAMEWORK) $(VC_2010)

$(VC_FRAMEWORK): $(WIN_C_DIR)/$(WIN_PRG_DIR)/VC/Framework/*
    @$(PACK_TOOL) $(PACK_OPTION) "$@" --ignore-failed-read /c/$(WIN_PRG_DIR)/VC/Framework
$(VC_2010): $(WIN_C_DIR)/$(WIN_PRG_DIR)/VC/VS2010/*
    @$(PACK_TOOL) $(PACK_OPTION) "$@" --ignore-failed-read /c/$(WIN_PRG_DIR)/VC/VS2010

As you can see, the tar package is stored in ~/MS_bak_MSYS/tools/VC/VC_2010.tar.bz2. I run the script in ~/qqaa. ~/MS_bak_MSYS is excluded from tar command. So, the tar file I am creating is not inside a directory I am trying to put into tar file. This is why I felt it strange that the warning came up.

warem
  • 1,471
  • 2
  • 14
  • 21
  • It looks like you are using windows setup, so not relevant for you. Yet, we have similar problem when underlying filesystem is glusterfs. It looks like there is a bug when lstat and fstat return different values: https://bugzilla.redhat.com/show_bug.cgi?id=1058526 – Arie Skliarouk Aug 14 '18 at 12:50
  • Got this problem using tar on a volume mounted by windows docker. Exchanging the `tar` utility for `pax` worked for me. – Andreas Nov 24 '19 at 11:01

10 Answers10

102

I also encounter the tar messages "changed as we read it". For me these message occurred when I was making tar file of Linux file system in bitbake build environment. This error was sporadic.

For me this was not due to creating tar file from the same directory. I am assuming there is actually some file overwritten or changed during tar file creation.

The message is a warning and it still creates the tar file. We can still suppress these warning message by setting option

--warning=no-file-changed

(http://www.gnu.org/software/tar/manual/html_section/warnings.html )

Still the exit code return by the tar is "1" in warning message case: http://www.gnu.org/software/tar/manual/html_section/Synopsis.html

So if we are calling the tar file from some function in scripts, we can handle the exit code something like this:

set +e 
tar -czf sample.tar.gz dir1 dir2
exitcode=$?

if [ "$exitcode" != "1" ] && [ "$exitcode" != "0" ]; then
    exit $exitcode
fi
set -e
sandeep
  • 1,179
  • 1
  • 7
  • 4
  • I have the same issue and this answer "solved" my problem by giving me the ability to work around it. Thanks @sandeep. – jaskho Jul 17 '14 at 15:45
  • 21
    Tar exts with `1`: "If tar was given \`--create', \`--append' or \`--update' option, this exit code means that some files were changed while being archived and so the resulting archive does not contain the exact copy of the file set." This is such jaw-dropping bad behavior -- it will kill a pipeline and there's no way to stop it. *facepalm* – Otheus Jun 08 '15 at 09:23
  • Note @Otheus `set +e` – Ryan Brodie Feb 18 '17 at 02:06
  • 2
    @RyanBrodie I was thinking along the lines of `set -o pipefail; tar ... | gzip`. But I take it back; it won't kill the entire pipeline, because the exit is deferred until end of execution. – Otheus Feb 22 '17 at 17:32
  • 1
    This is not a warning, it's an error. A warning would not cause a non-zero exitcode. – Hi-Angel Dec 20 '22 at 08:44
  • 1
    @Otheus, the fact that it exits with a non-zero code is valid. A file was changed during processing. When you create an archive, generally you want to be sure of its integrity. If you have automated processes running through a pipe where this could become an issue, you could make that into a script instead and do any error handling that fits your case. – Ro Achterberg May 11 '23 at 08:04
94

Although its very late but I recently had the same issue.

Issue is because dir . is changing as xyz.tar.gz is created after running the command. There are two solutions:

Solution 1: tar will not mind if the archive is created in any directory inside .. There can be reasons why can't create the archive outside the work space. Worked around it by creating a temporary directory for putting the archive as:

mkdir artefacts
tar -zcvf artefacts/archive.tar.gz --exclude=./artefacts .
echo $?
0

Solution 2: This one I like. create the archive file before running tar:

touch archive.tar.gz
tar --exclude=archive.tar.gz -zcvf archive.tar.gz .
echo $?
0
Mohammad Azim
  • 2,604
  • 20
  • 21
42

If you want help debugging a problem like this you need to provide the make rule or at least the tar command you invoked. How can we see what's wrong with the command if there's no command to see?

However, 99% of the time an error like this means that you're creating the tar file inside a directory that you're trying to put into the tar file. So, when tar tries to read the directory it finds the tar file as a member of the directory, starts to read it and write it out to the tar file, and so between the time it starts to read the tar file and when it finishes reading the tar file, the tar file has changed.

So for example something like:

tar cf ./foo.tar .

There's no way to "stop" this, because it's not wrong. Just put your tar file somewhere else when you create it, or find another way (using --exclude or whatever) to omit the tar file.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • I added more details in the original post. Please check. – warem Dec 02 '13 at 10:20
  • Based on the info here I don't know what's wrong. However, I know very little about working with Windows or cygwin... I do know that the Windows filesystem is _much_ more difficult to work with WRT multiple programs accessing the same file than a POSIX-based filesystem. But that doesn't seem immediately relevant to your situation. All I can suggest is removing the `@` in your rules and examining the command that make is printing to be sure it's correct, and look at the files tar is trying to create (output from the v option) to ensure there's nothing mysterious. – MadScientist Dec 03 '13 at 16:52
22

Here is a one-liner for ignoring the tar exit status if it is 1. There is no need to set +e as in sandeep's script. If the tar exit status is 0 or 1, this one-liner will return with exit status 0. Otherwise it will return with exit status 1. This is different from sandeep's script where the original exit status value is preserved if it is different from 1.

tar -czf sample.tar.gz dir1 dir2 || [[ $? -eq 1 ]]

Fabian Ritzmann
  • 1,345
  • 9
  • 20
  • Why wouldn't you want the original exit status to be preserved? – ATLief Mar 03 '21 at 15:44
  • My most common use case is shell scripts in Jenkins. Jenkins by defaults runs scripts with the `errexit` option set, i.e. any failing command will cause the script to exit immediately. That is a problem when you run e.g. system tests and want the Jenkins job to run to the end and report all failed tests instead of bailing out when a system test fails. – Fabian Ritzmann Mar 08 '21 at 07:16
  • 1
    this just ignores the return code of 1 it does not fix issue of why the tar compression failed in the middle ... when you get a 1 typically the tar failed to complete so its output archive will not have all of the source data only partial data – Scott Stensland Jun 14 '22 at 19:44
6

To enhance Fabian's one-liner; let us say that we want to ignore only exit status 1 but to preserve the exit status if it is anything else:

tar -czf sample.tar.gz dir1 dir2 || ( export ret=$?; [[ $ret -eq 1 ]] || exit "$ret" )

This does everything sandeep's script does, on one line.

Jeremy
  • 61
  • 1
  • 1
5

Simply using an outer directory for the output, solved the problem for me.

sudo tar czf ./../31OCT18.tar.gz ./
Mohd Abdul Mujib
  • 13,071
  • 8
  • 64
  • 88
1

Exit codes for tar are restricted, so you don't get to much information. You can assume that ec=1 is safe to ignore, but it might trip - i.e. the gzip-example in other posts (exit code from external programs).

The reason for the file changed as we read it error/warning can be varying.

  • A log file inside the directory.
  • Writing to a tar file in the same directory you are trying to back up.
  • etc.

Possible workarounds can involve:

  • exclude known files (log files, tar-files, etc)
  • ensure log files are written to other directories

This can be quite involved, so you might want to still just run the tar command and preferably safely ignore some errors / warnings.

To do this you will have to:

  • Save the tar output.
  • Save the exit code
  • Check the output against known warnings and errors, not unlike tar's own ignore.
  • Conditionally pass another exit code to the next program in the pipe.

In OP's case this would have to be wrapped in a script and run as PACK_TOOL.

# List of errors and warnings from "tar" which we will safely ignore.
# Adapt to your findings and needs
IGNORE_ERROR="^tar:.*(Removing leading|socket ignored|file changed as we read it)"

# Save stderr from "tar"
RET=$(tar zcf $BACKUP --exclude Cache --exclude output.log --exclude "*cron*sysout*" $DIR 2>&1)
EC=$?  # Save "tar's" exit code
echo "$RET"
if [ $EC -ne 0 ]
then
  # Check the RET output, remove (grep -v) any errors / warning you wish to ignore
  REAL_ERRORS=$(echo "$RET" | grep "^tar: " | grep -Ev "${IGNORE_ERROR:?}")
  # If there is any output left you actually got an error to check
  if [ -n "$REAL_ERRORS" ]
  then
      echo "ERROR during backup of ${DIR:?} to ${BACKUP:?}"
  else
      echo "OK backup of (warnings ignored) ${DIR:?}"
      EC=0
  fi
else
  echo "OK backup of ${DIR:?}"
fi
sastorsl
  • 2,015
  • 1
  • 16
  • 17
0

It worked for me by adding a simple sleep timeout of 20 sec. This might happen if your source directory is still writing. Hence put a sleep so that the backup would finish and then tar should work fine. This also helped me in getting the right exit status.

sleep 20
tar -czf ${DB}.${DATE}.tgz ./${DB}.${DATE}
ninohead
  • 325
  • 2
  • 6
0

I am not sure does it suit you but I noticed that tar does not fail on changed/deleted files in pipe mode. See what I mean.

Test script:

#!/usr/bin/env bash
set -ex
tar cpf - ./files | aws s3 cp - s3://my-bucket/files.tar
echo $?

Deleting random files manually...

Output:

+ aws s3 cp - s3://my-bucket/files.tar
+ tar cpf - ./files
tar: ./files/default_images: File removed before we read it
tar: ./files: file changed as we read it
+ echo 0
0
pprishchepa
  • 997
  • 1
  • 9
  • 21
  • 2
    This is because exit codes are ignored inside of pipes by default. And it is a good practice to enable them back by "set -o pipefail". – Sergey Nov 08 '20 at 08:31
-1

Answer should be very simple: Don't save your tar file while "Taring" it in the same directory.

Just do: tar -cvzf resources/docker/php/php.tar.gz .

Eventually,

it will tar the current directory and save it to another directory.

That's easy peasy, lemon squeezy fellas