Reputation: 21
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!
Upvotes: 0
Views: 688
Reputation: 438073
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.
Upvotes: 5