duppWR
duppWR

Reputation: 11

Order of redirection in bash & overwriting and appending at the same time

First, we have some commands:

service --status-all 1>one 2>one
service --status-all 1>two 2>>two
service --status-all 1>>three 2>>three
service --status-all 1>>four 2>four

service --status-all 2>one1 1>one1
service --status-all 2>two1 1>>two1
service --status-all 2>>three1 1>>three1
service --status-all 2>>four1 1>four1

After execution, the content of the following is the same:

three = three1, so these are equal:

service --status-all 1>>three 2>>three
service --status-all 2>>three1 1>>three1

one = four = one1 = two1, so these are equal:

service --status-all 1>one 2>one
service --status-all 1>>four 2>four
service --status-all 2>one1 1>one1
service --status-all 2>two1 1>>two1

two = four1, so these are equal:

service --status-all 1>two 2>>two
service --status-all 2>>four1 1>four1

Contents of files:

Files one = four = one1 = two1:

 [ ? ]  apport
 [ ? ]  binfmt-support
 [ ? ]  console-setup
 [ ? ]  dns-clean
 [ ? ]  irqbalance
 [ ? ]  killprocs
 [ ? ]  kmod
 [ ? ]  lightdm
 [ ? ]  mysql
 [ ? ]  networking
 [ ? ]  ondemand
 [ ? ]  pppd-dns
 [ ? ]  rc.local
 [ ? ]  sendsigs
 [ ? ]  speech-dispatcher
 [ ? ]  umountfs
 [ ? ]  umountnfs.sh
 [ ? ]  umountroot
]  sudo
 [ - ]  udev
 [ - ]  unattended-upgrades
 [ - ]  urandom
 [ + ]  virtualbox
 [ - ]  x11-common

Files two = four1:

 [ + ]  acpid
 [ - ]  anacron
 [ - ]  apparmor
 [ + ]  avahi-daemon
 [ + ]  bluetooth
 [ - ]  brltty
 [ + ]  cron
 [ + ]  cups
 [ + ]  cups-browsed
 [ - ]  dbus
 [ + ]  friendly-recovery
 [ - ]  grub-common
 [ + ]  kerneloops
 [ - ]  procps
 [ - ]  pulseaudio
 [ + ]  resolvconf
 [ - ]  rsync
 [ + ]  rsyslog
 [ + ]  saned
 [ - ]  sudo
 [ - ]  udev
 [ - ]  unattended-upgrades
 [ - ]  urandom
 [ + ]  virtualbox
 [ - ]  x11-common
nfs.sh
 [ ? ]  umountroot

File three = three1:

 [ + ]  acpid
 [ - ]  anacron
 [ - ]  apparmor
 [ ? ]  apport
 [ + ]  avahi-daemon
 [ ? ]  binfmt-support
 [ + ]  bluetooth
 [ - ]  brltty
 [ ? ]  console-setup
 [ + ]  cron
 [ + ]  cups
 [ + ]  cups-browsed
 [ - ]  dbus
 [ ? ]  dns-clean
 [ + ]  friendly-recovery
 [ - ]  grub-common
 [ ? ]  irqbalance
 [ + ]  kerneloops
 [ ? ]  killprocs
 [ ? ]  kmod
 [ ? ]  lightdm
 [ ? ]  mysql
 [ ? ]  networking
 [ ? ]  ondemand
 [ ? ]  pppd-dns
 [ - ]  procps
 [ - ]  pulseaudio
 [ ? ]  rc.local
 [ + ]  resolvconf
 [ - ]  rsync
 [ + ]  rsyslog
 [ + ]  saned
 [ ? ]  sendsigs
 [ ? ]  speech-dispatcher
 [ - ]  sudo
 [ - ]  udev
 [ ? ]  umountfs
 [ ? ]  umountnfs.sh
 [ ? ]  umountroot
 [ - ]  unattended-upgrades
 [ - ]  urandom
 [ + ]  virtualbox
 [ - ]  x11-common

There are 18 question marks (stderr lines). Stderr clearly overwrote first 18 lines of stdout and then some (sudo line).

Why such behaviour occurs?

My OS is Ubuntu 14.04.

Upvotes: 1

Views: 68

Answers (2)

Charles Duffy
Charles Duffy

Reputation: 295272

The odd behavior you're seeing actually has nothing to do with redirection order, and everything to do with output buffering and flags passed to open().

You're opening one of your files with O_APPEND [as done by >>] -- so all writes will always go to the end -- and the other with > and thus lacking that guarantee. Further, as evidenced by the behavior you report, your program isn't doing line buffering for its writes, meaning that your writes get split into big chunks that aren't guaranteed to end at line boundaries and those chunks are interspersed.

If your program did line-buffered writes, you could use >> for both file descriptors and things would mostly work (as long as the lines were all short enough to be completed by a single write() call). It doesn't, though, so you simply can't split and recombine its output in this way and expect things to be split on line boundaries.


Now, to answer the literal question about redirection order (which has nothing at all to do with the actual cause of the problem at hand):

Left-to-right, strictly.

Upvotes: 0

Barmar
Barmar

Reputation: 780673

The stream that's opened for appending automatically repositions itself to the end of the file before each write. If the other stream writes to the file between two of its writes, it will reposition.

On the other hand, the stream that's open for normal output does not reposition itself. If the appending stream writes something, and then the normal stream writes something, the second write will overwrite what the first one wrote.

In addition, if the program uses stdio, some of the writes will likely be buffered. The above behavior occurs when the buffers are flushed, not when the program calls the buffered writing functions. By default, stdout is fully-buffered when writing to a file, stderr is unbuffered.

Upvotes: 1

Related Questions