12

Just by coincidence I had to use my ATA-ID-to-device-name script (found here: https://serverfault.com/questions/244944/linux-ata-errors-translating-to-a-device-name/426561#426561) on a read-only / partition. In case you're curious, it was an Ubuntu recovery console which will let you access your / partition, but will mount it read-only by default. I am glad about that, because otherwise I would probably never have found out that my script behaves strangely on a R/O system due to a specific line, this one:

IFS=: read HostMain HostMid HostSub <<< "$HostFull"

This does not work if there is no write permission. I wouldn't have assumed it would fail, though. But apparently the <<< operator does require to write some temporary file to somewhere.

But is there any way to circumvent the creation of a temporary file, or, is there any way to specify where the file is written to? In the Ubuntu recovery console, there is---oddly enough---write permission on the /run directory, so that would do, if I could somehow "tell" read to write the temp file to somewhere else than usual.

syntaxerror
  • 2,236
  • 2
  • 27
  • 49
  • 2
    @gniourf_gniourf No, “opening a file descriptor” wouldn't be a problem (why would it?), and `/dev/fd` has nothing to do with this. `<<<` is the culprit though, because it creates a temporary file (which needs to be written somewhere). – Gilles 'SO- stop being evil' Nov 02 '13 at 23:59

3 Answers3

8

An array could make the string parsing without the need for a temporal file. Don't forget to turn off globbing.

set -f
IFS=: Hosts=($HostFull)
HostMain=${Hosts[0]}
HostMid=${Hosts[1]}
HostSub=${Hosts[2]}
set +f
Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
xae
  • 1,971
  • 16
  • 10
  • 2
    or even without `IFS`, if you're sure there are no spaces in `$HostFull` as so: `Hosts=( ${HostFull//:/ } )`. Or even if there are spaces: `HostMain=${HostFull%%:*}; HostMid=${HostFull#*:}; HostSub=${HostMid#*:}; HostMid=${HostMid%:*}` (or something similar, I'm getting confused `:D`). – gniourf_gniourf Nov 02 '13 at 23:33
  • You are right, as you show parameter expansion is tricky bussines... – xae Nov 03 '13 at 00:02
4

I agree with @gniourf_gniourf, your probably needing write access but not to the file descriptors, most likely a file.

You could test this by tracing the execution of your command when in the readonly partition.

{ strace -p "$$" & sleep 1; read var1 <<< "hi"; sleep 1; kill "$1"; }

The above will run strace on the Bash shell (process $$). It then sleeps for 1 second, and then runs the read from the HERE STRING. I've put the string "hi" in this position. I then sleep for one more second and then kill the strace.

Example

While parsing this output you'll notice that a file is opened as O_WRONLY, which is for writing to a file.

open("/tmp/sh-thd-4137571604", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0600) = 3

Above we can see what file is being written to by your command sequence.

slm
  • 363,520
  • 117
  • 767
  • 871
  • 1
    Not to “create the file descriptors” (that doesn't make any sense). To create the *file*. It isn't `read` that opens a file for writing (that would be silly), it's `<<<`. – Gilles 'SO- stop being evil' Nov 02 '13 at 23:59
  • @Gilles - thanks, I didn't quite understand what it was telling me. Cleaned up the A. – slm Nov 03 '13 at 00:19
  • Thank you very much! A very nice technique, which might even help me multiple times in the future with similar issues. However, one thing *is* worrying me, and that is the fact that `/tmp` is a __hardcoded__ path. And probably you've guessed it, `/tmp` *IS* there already, but read-only as well! And since working on that recovery console will have me logged into my __live__ file system, I would not want to mess in there by symlinking or whatever (not even while in that console). – syntaxerror Nov 03 '13 at 11:04
3

I find positional parameters very useful for this kind of task. It's generally portable to all shells as well, and costs no forks nor temporary files.

$ HostFull=main:mid:sub    
$ oldIFS=$IFS; IFS=:; set -- $HostFull; IFS=$oldIFS
$ echo $1
main
$ echo $2
mid
$ echo $3
sub
David Sainty
  • 456
  • 3
  • 4
  • A good approach! Thank you. Plus, I like it that it does not require any external tools (that we are usually not to be expected to find in those restricted environments anyhow). The only thing that *may* cause some trouble is the `$1`, `$2`, `$3` stuff: remember that in a script, this will usually stand for an argument passed to the script *itself*. -- And while we're at it: if IFS is meant to be a space, *IFS= * will not do in this syntax; you will have to specify __IFS=' '__ explicitly. – syntaxerror Nov 03 '13 at 12:01