2

My shell script (see below) is failing partway through execution with "/bin/sh: 1: Syntax error: Bad fd number." This thread suggests that the problem is using the >& construction in sh, which in Ubuntu is linked to dash, which does not support >&. I am puzzled, because while the error message suggests that my script is running in sh, I am pretty confident that it is running in bash:

  • The login shell from which I am executing it is bash, as indicated by echo $0.
  • The first line of the script is #!/bin/bash
  • If I insert readlink /proc/$$/exe into my script (as suggested here), the output is /usr/bin/bash.
  • Both /bin/bash and /usr/bin/bash are actual executables, not links to other files.

So, if I'm wrong and it's actually running in sh, why is it doing so? And if it is actually running in bash, why is it returning an error when that syntax is supposed to work in bash?

I'm running this in Ubuntu 22.04 using a 6.5.0-28 kernel.

Any feedback is appreciated.


The purpose of this script is to create a multipart tar (using this method) from a list of files and directories in a text file, capping the part size at 512 Gb. This should generate four files, but it consistently fails after the third part:

#!/bin/bash
tar -cvf /home/me/archive.tar --multi-volume --tape-length=512000 --new-volume-script='echo -n "/home/me/archive-${TAR_VOLUME}.tar" >&${TAR_FD}' --exclude-tag-all=donotcopy --exclude-backups -T /home/me/file-list.txt

The full error text at failure is:

bin/sh: 1: Syntax error: Bad fd number
tar: ‘echo -n "/home/me/archive-${TAR_VOLUME}.tar" >&${TAR_FD}’ command failed
tar: Error is not recoverable: exiting now

The format of file-list.txt is as follows:

/home/me/foo
/home/me/bar/file-1.xyz
/home/me/bar/file-2.xyz
3
  • Could the problem be that when tar executes the 'echo..." command, it executes it in sh instead of bash? If so, is there a way to force tar to run echo in bash? If i change --new-volume-script='echo...' to --new-volume-script='bash echo...', it returns this error instead: "/usr/bin/echo: /usr/bin/echo: cannot execute binary file"
    – Bolio
    Commented May 1 at 18:19
  • 2
    welcome to U&L; Is $!/bin/bash a copy/paste typo. likewise --tape-length (not --tape=length). I couldn't reproduce the error, command wotk fine under bash login, spliting file as expected.
    – Archemar
    Commented May 1 at 18:44
  • Thank you @Archemar. Typos corrected.
    – Bolio
    Commented May 2 at 9:43

1 Answer 1

3

The tar command uses /bin/sh to execute the command line given to --new-volume-script.

Evidence?

tar -cf /tmp/archive.tar --multi-volume --tape-length=1024 --new-volume-script='ps -f >/tmp/x' /etc 2>/dev/null
cat /tmp/x

Example output clearly showing that PID 25662 is executed with /bin/sh:

UID        PID  PPID  C STIME TTY          TIME CMD
chris    25210 25207  0 00:58 pts/0    00:00:00 -bash
chris    25651 25210  5 01:03 pts/0    00:00:00 tar -cf /tmp/archive.tar --multi-volume --tape-length=1024 --new-volume-script=ps -f >/tmp/x /etc
chris    25662 25651  0 01:03 pts/0    00:00:00 /bin/sh -c ps -f >/tmp/x
chris    25663 25662 99 01:03 pts/0    00:00:00 ps -f

Having established that tar invokes sh, and that sh is often implemented by dash rather than bash, we can move on to information about the file descriptor number:

tar -cf /home/me/archive.tar --multi-volume --tape=length=512000 --new-volume-script='echo "TAR_VOLUME=$TAR_VOLUME, TAR_FD=$TAR_FD." >&2; echo -n "/home/me/archive-${TAR_VOLUME}.tar" >&${TAR_FD}' --exclude-tag-all=donotcopy --exclude-backups -T /home/me/file-list.txt

The resulting output is

TAR_VOLUME=2, TAR_FD=9.
TAR_VOLUME=3, TAR_FD=9.
TAR_VOLUME=4, TAR_FD=10.

And fails

TAR_VOLUME=5, TAR_FD=9.
TAR_VOLUME=6, TAR_FD=10.

And fails

Empirically we can suggest that a two digit file descriptor is causing the issue. Reading man dash leads us here:

The [n] is an optional number between 0 and 9, as in 3 (not [3]), that refers to a file descriptor

In other words a file descriptor for dash must be in the range 0 to 9, and file descriptors 10 and above cannot be referenced by number.

The solution here is to step outside dash into a shell with more flexibility, such as bash (I've split the command across several lines for improved readability):

tar -cvf /home/me/archive.tar --multi-volume --tape-length=512000 --new-volume-script='
      exec bash -c "echo \"/home/me/archive-$TAR_VOLUME.tar\" >&$TAR_FD"
    ' --exclude-tag-all=donotcopy --exclude-backups -T /home/me/file-list.txt
4
  • Thank you, @Chris Davies. From what I can tell, the script fails if TAR_FD has more than one digit. At 512 Gb file size, it goes "TAR_VOLUME=2, TAR_FD=9. TAR_VOLUME=3, TAR_FD=9. TAR_VOLUME=4, TAR_FD=10." and then fails. At 256 Gb file size, it goes up to "TAR_VOLUME=5, TAR_FD=9. TAR_VOLUME=6, TAR_FD=10." and fails. At 128 Gb, it goes "TAR_VOLUME=2, TAR_FD=7. TAR_VOLUME=3, TAR_FD=9. TAR_VOLUME=4, TAR_FD=13." and fails. But as a relatively inexperienced scripter, I am puzzled because I thought that TAR_FD was supposed to contain the file name, not a number.
    – Bolio
    Commented May 2 at 9:59
  • @Bolio, no, it's an fd, the >&9 syntax is exactly for writing to an already-open fd with the name known. Tar creates a pipe to connect itself to the script, so it can get the output without using a temporary file. But yeah, some shells, including Dash, apparently, can't deal with multi-digit fds too well. (Though they might also use fd numbers > 9 for internal functions, so it's not a good idea for a script to go there.) One might call it a bug that tar doesn't reserve a low fd for that, at least it's a bit of an odd way to do it.
    – ilkkachu
    Commented May 2 at 11:43
  • 1
    I wondered why the fd number it gives to the script changes every time. But of course the logical explanation is that it keeps open fds for each intermediate directory on the way to where ever it is in the directory tree at that moment. (So, if it's reading foo/bar/hello.txt, it has open fds for foo, foo/bar, and foo/bar/hello.txt) One can see that with something like tar -L 16000 -F 'ls -l /proc/$(pidof tar)/fd' -cf foo.tar ./some-big-dir
    – ilkkachu
    Commented May 2 at 11:49
  • And there it is. Brilliant. Stack Exchange at its finest. A thousand thanks.I'm going to go read about file descriptors.
    – Bolio
    Commented May 2 at 15:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .