2

When using the OpenFile function in Go's os package, what exactly is the purpose of the pipe character?

Example:

os.OpenFile("foo.txt", os.O_RDWR|os.O_APPEND, 0660)

Does it serve as a logical OR? If so, does Go choose the first one that is "truthy"?? Being that the constants those flags represent, at the heart of them are just integers written in hexadecimal, when compiled how does Go choose which flag to apply?

After all, if the function call were to go by the largest number, os.O_APPEND would take precedence over all other flags passed in as seen below:

os.O_RDWR == syscall.O_RDWR == 0x2 == 2
os.O_APPEND == syscall.O_APPEND == 0x400 == 1024
os.O_CREATE == syscall.O_CREAT == 0x40 == 64

UPDATE 1

To follow up on the comment below, if I have a bitwise operator calculation using os.O_APPEND|os.O_CREATE will that error if the file exists, or simply create/append as needed?

UPDATE 2

My question is two fold, one to understand the purpose of the bitwise operator, which I understand now is being used more as a bitmask operation; and two, how to use the os.OpenFile() function as a create or append operation. In my playing around I have found the following combination to work best:

file, _ := os.OpenFile("foo.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0660)
file.WriteString("Hello World\n")
file.Sync()

Is this the correct way or is there a more succinct way to do this?

Ode
  • 549
  • 6
  • 19
  • 3
    It does a bitwise OR: 0x2 | 0x400 = 0x402. So both flags get set in one integer value. – 001 May 03 '16 at 16:27
  • So am I to understand that if I want to append or create (if not exists) I would add `os.O_APPEND|os.O_CREATE` ? – Ode May 03 '16 at 16:31
  • 1
    @OdeeOdum - The O_* arguments and their use with bitwise operators is not Go-specific, but appears in many systems (POSIX and otherwise). May want to also see: http://linux.die.net/man/2/open which also contains some other expressions you may find useful (for example, how to "turn off" a mode) and also carry over from C to Go. – BadZen May 03 '16 at 16:39
  • My understanding is that the O_* flags are OS specific more or less, stored in Go's syscall package that targets low-level OS operations, set by the GOARCH env var. https://golang.org/pkg/syscall/ – Ode May 03 '16 at 16:42
  • 1
    @OdeeOdum, it will append if it does exist, and create it if it doesn't – matt.s May 03 '16 at 16:46
  • Re: Update - It will append, and create if it doesn't exist. If you /want/ an error if it exists, also add O_EXCL. – BadZen May 03 '16 at 16:51
  • 2
    Recommended for the op: [Difference between some operators “|”, “^”, “&”, “&^”. Golang](http://stackoverflow.com/questions/28432398/difference-between-some-operators-golang) – icza May 03 '16 at 16:55
  • @OdeeOdum, no, O_* flags are platform-independent. At least those ones that OS package expects which is also platform-independent. On some platforms it will use O_* flags that are available by default. On Windows, for example, the flags have made up values to make OS package easier to write. Internally it will map them appropriately. – creker May 03 '16 at 17:18
  • @creker thanks, yeah I discovered that reading the syscall documentation. Reference the link in my comment above. – Ode May 03 '16 at 21:42

2 Answers2

5

It is a bitwise, not a logical OR.

If you write out the numbers in binary, and assign each a truth value 0/1, and apply the logical OR to each of the bits in place i between the arguments, and then reassemble the result into an integer by binary expansion - that's the | operator.

It is often used in a way that is commonly described as a "bitmask" - you use a bitmask when you want a single int value to represent a (small) set of switches that could be turned on or off. One bit per switch.

You should see in this context, A | B means "all the switches in A that are on, as well as all the switches in B that are on". In your case, the switches define the exact behavior of the file open/creation function, as described by the Go manual. (And probably more in detail by the Unix manpage I linked above).

In a bitmask, constants are typically defined that represent each switch - that's how those O_* constants are determined. Each is an int with exactly one bit set and represents a particular switch. (though, be careful, because sometimes they represent combinations of switches!).

Also:

 ^A  // All of the "switches" not currently on in A
 A&^B // All of the "switches" on in A but not on in B
 A^B  // All of the "switches" on in exactly one of A or B

, etc.

The operator | itself is described in the Go manual here.

BadZen
  • 4,083
  • 2
  • 25
  • 48
2

It is a bitwise OR operator. Its purpose being used here is to allow for multiple values to be passed as a bitmask. Thus you can combine flags to create a desired result such as using the OpenFile() function to create a file if it does not exist or append to it if it does.

os.Openfile("foo.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0660

The constants being passed as arguments from the os package are assigned values from the syscall package. This package contains low-level operating system independent values.

Package syscall contains an interface to the low-level operating system primitives. The details vary depending on the underlying system, and by default, godoc will display the syscall documentation for the current system. If you want godoc to display syscall documentation for another system, set $GOOS and $GOARCH to the desired system. For example, if you want to view documentation for freebsd/arm on linux/amd64, set $GOOS to freebsd and $GOARCH to arm. The primary use of syscall is inside other packages that provide a more portable interface to the system, such as "os", "time" and "net".

https://golang.org/pkg/syscall/

As noted by @BadZen, a bitwise OR operator, in this case the '|' character, acts at the binary level changing any 0 values to 1's that are not already ones.

You should see in this context, A | B means "all the switches in A that are on, as well as all the switches in B that are on".

By doing this as the function above displays, you are manipulating the behavior of the function to create a file (os.O_CREATE) with the given name of foo.txtor open the file for reading/writing (os.O_RDWR) and any value written to it will be appended (os.O_APPEND). Alternatively you could pass along os.O_TRUNC in order to truncate the file before writing.

The bitwise OR operator allows you a powerful solution to combining different behaviors in order to get the result from the function that you are desiring.

Ode
  • 549
  • 6
  • 19