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,246
  • 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
xae
  • 2,021
  • 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
  • 369,824
  • 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
  • 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