0

I am by no means an AppleScript expert, but get by. I am running into a hell of a time trying to pass some AppleScript variables to a sed command in order to replace some text in a file, based on the variables.

I have two dialog boxes in AppleScript that grab user data, and I store those variables. myData - This works OK.

set myData to text returned of ¬
    (display dialog "Enter Your Data" with title ¬
        "Data Entry" default answer ¬
        "" buttons {"Continue…"} ¬
        default button 1 ¬
        ) 

set searchFor to quoted form of "data1"
set searchFor2 to quoted form of "data2"
set inputFile to "/Users/User1/Desktop/file.txt"
set outputFile to "/Users/User1/Desktop/file1.txt"

do shell script quoted form of ("sed -i.bak s/" & searchFor & "/" & myData & "/g") & " " & inputFile & " > " & outputFile

I actually get this error: No such file or directory, exit code 127. The weird part is, though, that it does write out the file with zero data in it. I am not necessarily trying to write out a diff file as the script suggests, just edit data in place. However, I've had zero success that way, which is why I approached it this way.

Any assistance with my issue would be greatly appreciated!

mklement0
  • 382,024
  • 64
  • 607
  • 775
cybersurfr
  • 21
  • 5

1 Answers1

5

Your immediate issues are that you mistakenly apply quoted form of multiple times, and, more to the point, apply quoted form of to the sed executable name, its options, and the sed script together, which invariably breaks (see below for a detailed explanation); try the following - without applying quoted form of to any of the variables beforehand - note how quoted form of is applied selectively to the sed script and the in- and output files, respectively:

do shell script "sed " & quoted form of ¬
  ("s/" & searchFor & "/" & myData & "/g") ¬
  & " " & quoted form of inputFile & " > " & quoted form of outputFile

Note that I've removed -i.bak from your command, because it would invariably result in an empty output file: -i updates the input file in place, producing no stdout output. Thus, nothing would be sent to outputFile with > outputFile.

However, this can still break or misbehave, if searchFor and myData contain either / or characters that have special meaning in a sed regular expression (e.g., \, *, [, ...) or replacement string (e.g., &, \).

To avoid that, you'll have to escape the input strings first, which is non-trivial, unfortunately.

The following handlers provide robust, generic escaping - they are based on this answer, where the underlying commands are explained[1]) :

# Quotes (escapes) a string for safe use in a `sed` regex.
on quoteRegex(txt)

    do shell script "sed -e 's/[^^]/[&]/g; s/\\^/\\\\^/g; $!a\\'$'\\n''\\\\n' <<<" & quoted form of txt & " | tr -d '\\n'"

end quoteRegex

# Quotes (escapes) a string for safe use in a `sed` substitution string (`s///` function).
on quoteSubst(txt)

    do shell script "IFS= read -d '' -r <<<\"$(sed -e ':a' -e '$!{N;ba' -e '}' -e 's/[&/\\]/\\\\&/g; s/\\n/\\\\&/g' <<<" & quoted form of txt & "; printf X)\"; printf %s \"${REPLY%$'\\n'X$'\\n'}\"" without altering line endings

end quoteSubst

[1] Some tweaks were necessary to make them work with do shell script; notably, process substitution (<(...)) is not supported; the workaround via <<<"$(...)" necessitated extra steps to accurately preserve trailing newlines in the input.

Once you've pasted above handlers into your script, here's how to apply them to your command:

do shell script "sed " & quoted form of ¬
  ("s/" & my quoteRegex(searchFor) & "/" & my quoteSubst(myData) & "/g") ¬
  & " " & quoted form of inputFile & " > " & quoted form of outputFile

As for you original symptoms:

The weird part is, though, that it does write out the file with zero data in it.

This indicates that do shell script (a) was able to invoke the shell, (b) the shell parsed the command line without encountering a syntax error; if these conditions are met, an output redirection such as > outFile causes the target file to created as a zero-byte file, or, if it existed, to truncate it to a zero-byte file, BEFORE command execution begins.

If command execution then fails, the zero-byte file is left behind.

I actually get this error: No such file or directory, exit code 127

Exit code 127 indicates that the executable that is the 1st token of your command string could not be invoked, because it could not be found.

This is indeed what happened, because you mistakenly applied quoted form of to the sed executable name, its options, and the sed script together, which causes the entire resulting string to be interpreted as the executable name, which obviously fails.

Let's take a simplified example:

quoted form of "sed s/foo/bar/g file"

yields 'sed s/foo/bar/g file', including the enclosing single quotes. Passing this string to the shell causes the shell to consider this string a single token that constitutes the executable path or filename. Obviously, no file named sed s/foo/bar/g file exists, so the command fails.

The solution, as demonstrated above, is to pass sed, its options, the script, as well as the input filename and the output filename as separate tokens.

Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Hello , Thanks for your help. I tried to Apply it to the script However I now I get an error saying.. "error "«script» doesn’t understand the “quoteRegex” message." number -1708 from «script»" Is it perhaps because this is Mac OS X? – cybersurfr May 22 '15 at 13:23
  • @cybersurfr: Glad to help; the error message implies that you haven't defined the `on quoteRegex ... end quoteRegex` (and probably also `on quoteSubst ...`) handlers from above (2nd snippet). Put them in the same script and try again. – mklement0 May 22 '15 at 13:32
  • *facepalm* @PeterSmith Yep, you were right on that account. Once I added them in like this " on quoteRegex(txt) do shell script "sed -i.bak " & quoted form of ¬ ("s/" & my quoteRegex(searchFor) & "/" & my quoteSubst(myData) & "/g") ¬ & " " & quoted form of inputFile & " > " & quoted form of outputFile end quoteRegex" It would give "" in the result window , but nothing was changed and the outputfile would not be created – cybersurfr May 22 '15 at 13:38
  • @cybersurfr: No, you have to define the handlers _as posted_, _separately_ from your `do shell script` command. They are _helper_ functions (handlers). As an aside: if a script contains a handler _definition only_ and no code to _invoke_ it, _nothing_ will happen. – mklement0 May 22 '15 at 13:42
  • Gotcha, I added the handlers and that fixed the issue of not getting the output file. I then added the do shell script "sed -i.bak " & quoted form of ¬ ("s/" & my quoteRegex(searchFor) & "/" & my quoteSubst(myData) & "/g") ¬ & " " & quoted form of inputFile & " > " & quoted form of outputFile However Now this results in an empty output file instead. – cybersurfr May 22 '15 at 14:05
  • @cybersurfr: There's a problem with the `sed` command itself: you must remove `-i.bak` to get output sent to `outputFile` (see updated answer), or, alternatively, remove the `> outputFile` part and have `-i` update the input file _in place_. – mklement0 May 22 '15 at 14:21
  • That time, was so close it nearly made me cry. I get the correct output file. It contains everything in the original file. however no replacement has taken place. Do you think I would have more success by replacing the (searchfor) in ("s/" & my quoteRegex(searchFor) with a hard coded value? I can do that, I just wanted it to be more flexible. – cybersurfr May 22 '15 at 15:36
  • @cybersurfr: In [Apple]Script Editor, make the log pane visible (`View > Show Log`), click on `Events`, run the script, then look at the _last_ `do shell script` line, which will say something like `do shell script "sed 's/[f][o][o]/bar/g' '/Users/User1/Desktop/file.txt' > '/Users/User1/Desktop/file1.txt'"` - this is what AppleScript sends to the shell. Take the command _between_ the double quotes and experiment with it directly in the shell, in a Terminal window, until it works as intended. Then work your way backwards. – mklement0 May 22 '15 at 15:43
  • Symbol `¬`, how to type it under Mac OS X and Windows? It doesn't seem to be in ASCII, I lookup, some say it's Extended Ascii 170 [Ref 1](http://www.alt-codes.net/), [Ref 2](http://www.theasciicode.com.ar/extended-ascii-code/logical-negation-symbol-ascii-code-170.html), is it `Option/Alt` + `L`? (I compare it with the `¬` character in the answer, it match.), can you verify? – Albert May 13 '16 at 06:34
  • @mklement0, Thanks, I remember that in AppleScript Editor, press `Option+Return` to get `¬`, anywhere else, `Option` + `L` will work (including AppleScript Editor). I just tried `Alt + 0 1 7 0` (Numeric keypad) on Windows, and it get this `ª`, it looks like an superscript `a`, but, it do not have a strike under it (StackOverflow might did something), certainly not `¬`. – Albert May 13 '16 at 11:44
  • @Albert Thanks for the feedback; don't know what I was smoking earlier: I got the decimal form of the Unicode codepoint wrong; let me try again: Yes, it is Unicode `0xac` (`172`), the so-called NOT SIGN. (The only thing to add to your helpful comment about the Mac keyboard shortcuts is that they won't work in Terminal). On Windows, try `Alt + 0 1 7 2` _on the numeric keypad_ (hold down Alt and type these four digits; unverified). – mklement0 May 13 '16 at 13:56
  • 1
    @mklement0 Well, that `Alt` + `0 1 7 2` works on Windows! About terminal, the Mac shortcut I mentioned works fine on my Mac terminals (OS X 10.9.5), both Terminal v2.4 and iTerm2 Build 2.1.1, also works on Text Editor, Stickies, Finder, Evernote, etc., I don't know if I did something. – Albert May 13 '16 at 16:26
  • @Albert: Thanks again for the feedback. It was _I_ who did something: if you turn on `Use Option as Meta key` in the profile used by Terminal (via the `Profiles` tab in Terminal's Preferences, anchor `Keyboard`), `Option-L` does _not_ work - by default it does. – mklement0 May 13 '16 at 16:29
  • 1
    @mklement0 Agreed, finally, this `¬` issue met its closure. – Albert May 14 '16 at 01:28