#!/bin/sed s@.*@echo|sed -nf`awk -vRS='\\''0' NR==3 /proc/$PPID/cmdline`@e;q eecho Success q # Nonsense to pacify Vim 7.1's miscolorizer: s///;s===;q # Sed scripts can be carefully commented when they're filters, # because their shebang lines are just "#!/bin/sed -f" or # "#!/bin/sed -nf". If they're not filters, though, typical # examples (see # and ) will use a sh # shebang. Then, sed is invoked explicitly in the script, with the sed # code in a singly-quoted string. This can confuse text editors about # what filetype to assume is being edited. If the filetype is left # as shell, then the entire sed program is typically colorized as an # unparsed string. If that's overridden and forced to sed, then the # wrapping shell code is miscolorized. Furthermore, the single quote # delimiters would cause problems if a single quote needed to be used as # a literal in the script (although then a here document could be used). # This shows a different solution, in which a non-filter sed shebang is used. # Because it's not a filter, the implicit command line of the script becomes # /bin/sed, then everything after the space as a single arg (which is # interpreted as a sed program), then the name of this script (which would've # been the implied arg to -f in the filter shebangs). It is then possible to # put any sed code in that single arg on the shebang line. This script shows # how to set up that line to reinterpret the rest of this file as a sed script. # When I first came across this problem, I tried to implement # it by putting all the code in the shebang line, planning # on letting the rest of the file be just documentation. # Then I discovered that Linux shebangs have a hard limit # of 127 chars; longer shebangs are silently truncated. # This solution is still a little icky, since sed has nothing like # awk's begin blocks; the shebang line uses an sh echo to get things # started (because with no input lines, a sed -f script will never # run). Bletch. But once that echo starts the code, the code can do # its own further reading using the r or R commands. The whole point # was to break out of the 127-char shebang line; that has been achieved. # Below are notes documenting the various hacks I tried. After each # failure or partial success, I'd save that shebang line, with a note # on what it did. These are those notes, oldest first. There are also # occasional narrative paragraphs. They were collected in newest-first # order, then reversed and edited for continuity, so it's possible # editing errors may have left some continuity problems unfixed. # Note that this file contains a literal null about # halfway through, documenting a shebang line I tried # while investigating why escape sequences were failing. # Epigraph: # # I have been faithful to thee, APL! in my fashion. # -- Ernest Dowson # Here is the series of hacks I tried, ending with successful ones: #!/bin/sed s!.*!ls -l /proc/$$/exe!e;q # That prints q{lrwxrwxrwx 1 w w 0 Jun 30 21:36 /proc/380/exe -> /bin/dash} # showing that $$ is the PID of the spawned shell, not the parent script. # `man dash` reports that it defines a $PPID. # It's actually not a good idea to use ! as an s/// delimiter in sed # metacode, because it can be useful in negated addresses. Near the end # of the dev process I switched to using @ for my outer sed invocation. #!/bin/sed s!.*!ls -l /proc/$PPID/exe!e;q # That prints q{lrwxrwxrwx 1 w w 0 Jun 30 21:40 /proc/613/exe -> /bin/sed} # which makes sense but is not encouraging; I need the abspath of THIS SCRIPT. #!/bin/sed s!.*!cat /proc/$$/cmdline!e;q # That prints q{sh-ccat /proc/$$/cmdline}, # which of course is useless. #!/bin/sed s!.*!cat /proc/$PPID/cmdline!e;q # That prints # /bin/seds!.*!cat /proc/$PPID/cmdline!e;q/home/w/bin/nonFilter.sed # which I can work with. It's bizarre output; I assume there are nulls there. #!/bin/sed s!.*!cat -v /proc/$PPID/cmdline!e;q # That prints # /bin/sed^@s!.*!cat -v /proc/$PPID/cmdline!e;q^@/home/w/bin/nonFilter.sed^@ # which shows the nulls I expected. #!/bin/sed s!.*!sed 's=.*\\0==' /proc/$PPID/cmdline!e;q # Running that as qx{nonFilter.sed|cat -v} gives # /bin/sed^@==' /proc/$PPID/cmdline!e;q^@/home/w/bin/nonFilter.sed^@ # which looks like the .* only goes up to q{\\0}. #!/bin/sed s!.*!sed 's=.*\0==' /proc/$PPID/cmdline!e;q # That prints # sed: -e expression #1, char 31: unknown option to `s' # which I don't understand. #!/bin/sed s!.*!sed 'x;x;/\0/=' /proc/$PPID/cmdline!e;q # That outputs: # sed: -e expression #1, char 26: unterminated `s' command # sh: x: not found # sh: /0/= /proc/$PPID/cmdline!e;q/=: not found # none of which makes any sense to me, but it's interesting because # there's no longer an s/// command in the inner sed; so the outer sed # must be barfing. For some reason sh is interpreting one of those # "x"s, even though they're inside an arg to the inner sed; and it # does appear that the outer sed's s/// was executed, because that's # why the remaining command begins with /0. ... Oh: that's why sh # looks for an x command; the initial sed invocation was stripped. # THAT means that q{\0} is 2 literal characters, not an octal escape. #!/bin/sed s!.*!sed 'x;x;/\000/=' /proc/$PPID/cmdline!e;q # That outputs: # sed: -e expression #1, char 26: unterminated `s' command # sh: x: not found # sh: /000/= /proc/$PPID/cmdline!e;q00/=: not found # just like the one with q{\0}; q{\0} & q{\000} get equivalent output, # right down to the column number "char 26" (whatever that means). #!/bin/sed s!.*!sed 's=.*\\000==' /proc/$PPID/cmdline!e;q # That prints # /bin/sed^@==' /proc/$PPID/cmdline!e;q^@/home/w/bin/nonFilter.sed^@ # which implies that q{\\000} was taken literally. #!/bin/sed s!.*!sed 's=.*==' /proc/$PPID/cmdline!e;q # That's a literal null there after the "=.*". Running it gives # /bin/sed^@==' /proc/$PPID/cmdline!e;q^@/home/w/bin/nonFilter.sed^@ # which implies nongreedy matching by the .*, explicitly contrary # to documentation (which makes me think I'm misinterpreting this # evidence). In all the other cases that implied nongreedy matching, if # the escape weren't an escape, then there was actually no implication # of nongreedy matching; but in this case, an escape is not an issue. # I've been using shebangs like # #!/bin/sed s!.*!sed '...' /proc/$PPID/cmdline!e;q # but now I don't think the s!!! is needed [WRONG]; e$cmd is documented # to replace the pattern space with its output, so makes more sense. # ... Wait: # has the documentation of that command, and I think I misread it. # If it takes a parameter (a command), then it DOES NOT replace # the pattern space with the output; it outputs that output. This # is more useful, because s!.*!!e works fine for the other case. # [Right, and that output-to-stdout capability is used in the eecho # line at the top of this script now, where its only purpose is to # demonstrate that the shebang successfully bootstrapped another sed.] #!/bin/sed s!.*!sed 's=.*\o000=' /proc/$PPID/cmdline!e;q # That outputs: # sh: Syntax error: Unterminated quoted string # showing that the escape worked, probably in the *outer* # sed, but that the resulting null borked some code # before it could construct the inner sed's invocation. #!/bin/sed s!.*!sed 's=.*\o''000==' /proc/$PPID/cmdline!e;q # That outputs # /bin/sed^@s!.*!sed 's=.*\o''000==' /proc/$PPID/cmdline!e;q^@/home/w/bin/nonFilter.sed^@ # which appears to me to be an unmodified copy of the input, as if there were # no match. Breaking the arg to sed into 2 separate single-quoted strings # worked to avoid the outer sed substituting the escape, but the inner sed also # doesn't seem to have done so (or the s=== failed for some other reason). # I just realized that /proc/$PPID/cmdline *ENDS* with \0, so if I were getting # the greedy substitution up to \0 that I want, it'd eliminate everything. #!/bin/sed s!.*!sed 's=\o''000=\n=g' /proc/$PPID/cmdline!e;q # That outputs # sed: -e expression #1, char 7: unterminated `s' command # which makes sense; the outer sed substitutes the \n. #!/bin/sed s!.*!sed 's=\o''000=\''n=g' /proc/$PPID/cmdline!e;q # That outputs # /bin/sed^@s!.*!sed 's=.*\o''000==' /proc/$PPID/cmdline!e;q^@/home/w/bin/nonFilter.sed^@ # which is an unmodified copy of the input, as if there were no match. #!/bin/sed s!.*!sed 'y=\o''000=\''n=' /proc/$PPID/cmdline!e;q # That outputs # sed: -e expression #1, char 9: strings for `y' command are different lengths # but they should each be 1 char. One of those escapes fails. #!/bin/sed s!.*!tr '\''0' '\n'