Sed "invalid command code <", when using sed within Applescript - macos

I am trying to use Sed to do string replacement within my Applescript.
I am using the following command:
set selected_text to do shell script "echo " & "\"" & selected_text & "\"" & " | sed /'s/<\\(.*\\)>/\\1/'"
and I am being met with the following error:
The action “Run AppleScript” encountered an error: “sed: 1: "/s/<\(.*\)>/\1/": invalid command code <”
Expected input:
FirstName LastName <FirstName.LastName#email.com>
Expected output:
FirstName.LastName#email.com
Does anyone have any suggestions?

the better (easier) way is to use quoted form of with using do shell script. AppleScript escapes the text for you.
The slash in front of the regex was wrong, without it the error disappeared, but there was still an error in your regex. With your sed you want to replace things in your input, so you have to use an input regex matching your whole input. The parentheses then defines the part you want to keep. The first part is to find all items until <, the second is the email address you want to keep inside parentheses and finally the closing >.
set selected_text to do shell script "echo " & quoted form of selected_text & " | sed 's/[^<]*<\\(.*\\)>/\\1/'"
Best, Michael / Hamburg

Related

If string does NOT contain and REGEX

I'm looking for a way to write the following javascript code in applescript: If the condition is false then I want to do something.
var regEx = /\d{5}/g;
var str = 'This string contains 12345';
if (!regEx.test(str)){
do something
}
Below is the applescript I started but it doesn't work.
set str to 'This string contains 12345'
set regEx to <NOT SURE HOW APPLESCRIPT HANDLES THIS>
if string does contains "12345" then
do something
end if
In Javascript ! = does not. What is the equivalent in applescript? and how do I handle RegEx?
My overall goal is to find out if the finder window selected DOES NOT contain any 5 digit number combination in the folder name.
tl;dr For any version of macOS that is >= OSX 10.8 you'll need to replace grep's -P option (as indicated in the "Solution" section below) with the -E option - as mentioned in the "Different grep utilities" section at the bottom of this post.
As correctly noted in the comments...
Vanilla AppleScript can't handle regex. - vadian
so you'll need to
shell out to something that does know regex - red_menace
Solution:
To meet your requirement with vanilla AppleScript in a way which is analogous to JavaScript's test() method, consider utilizing a custom AppleScript subroutine as follows:
Subroutine:
on regExpTest(str, re)
set statusCode to do shell script "grep -q -P " & quoted form of re & ¬
" <<<" & quoted form of str & " 2>/dev/null; echo $?"
if statusCode is equal to "0" then
return true
else
return false
end if
end regExpTest
Usage:
set regExp to "\\d{5}"
set str to "This string contains 12345"
if regExpTest(str, regExp) then
display dialog "It DOES match so let's do something"
end if
Running the above script will display a dialog with the given message because there is a match between the regular expression and the specified string.
Note: AppleScript strings use the backslash as an escape character, so you'll notice that the \d metacharacter has been further escaped with an additional backslash, i.e. \\d
Inequality operators:
In Javascript != does not. What is the equivalent in applescript? and how do I handle RegEx?
AppleScript's inequality operators that are analogous to JavaScripts inequality operator (!=) are:
≠
is not
isn't
isn't equal [to]
is not equal [to]
doesn't equal
does not equal
So given your JavaScript if statement:
if (!regEx.test(str)){
// do something
}
We can achieve the same logic, (again using the aforementioned custom regExpTest subroutine), with the following code:
set regExp to "\\d{5}"
set str to "This string contains 1234"
if regExpTest(str, regExp) ≠ true then
display dialog "It DOES NOT match so let's do something"
end if
Note The str value only includes four consecutive digits, i.e. 1234.
This time running the above script will display a dialog with the given message because there is NOT a match between the regular expression and the specified string.
There are many variations that can be made to the aforementioned AppleScript if statement to acheieve the same desired logic. For example;
if regExpTest(str, regExp) is not equal to true then
...
end if
if regExpTest(str, regExp) = false then
...
end if
etc...
regExpTest subroutine explanation:
The aforementioned regExpTest AppleScript subroutine is essentially utilizing the do shell script command to run the following code that you would run directly via your macOS Terminal application. For instance in your Terminal application run the following two commands:
grep -q -P "\d{5}" <<<"This string contains 12345" 2>/dev/null; echo $?
Prints:
0
grep -q -P "\d{5}" <<<"This string contains 1234" 2>/dev/null; echo $?
Prints:
1
EDIT: Different grep utilities:
As noted in the comment by user3439894 it seems that some versions of the grep utility installed on Mac do not support the -P option which ensured the RegExp pattern was interpreted as a Perl regular expression. The reason why I opted to utilize a Perl Regular Expression is because they're more closely aligned to the regexp's used in JavaScript.
However, If you run man grep via your command line and discover that your greputility doesn't provide the -P option then change the following line of code in the regExpTest subroutine:
set statusCode to do shell script "grep -q -P " & quoted form of re & ¬
" <<<" & quoted form of str & " 2>/dev/null; echo $?"
to this instead:
set statusCode to do shell script "grep -q -E " & quoted form of re & ¬
" <<<" & quoted form of str & " 2>/dev/null; echo $?"
Note: The -P option has been changed to -E so the pattern is now interpreted as an extended regular expression (ERE) instead.
The shorthand metacharacter \d
You may also find that you need to change the the assignment of the regexp pattern from:
set regExp to "\\d{5}"
to
set regExp to "[0-9]{5}"
This time the shorthand metacharacter \d, (which is used match a digit), has been replaced with the equivalent character class [0-9].
As others have said, you can use the Foundation framework’s NSRegularExpression via the AppleScript-ObjC bridge.
That said, Objective-C APIs, while powerful, aren’t exactly AppleScripter-friendly, so I knocked together some “standard libraries” a few years back that wrapped a lot of that general functionality as nice native AppleScript commands.
e.g. Here’s the nearest equivalent to your JavaScript using the Text library’s search text command:
use script "Text"
set str to "This string contains 12345"
set foundMatches to search text str for "\\d{5}" using pattern matching
if foundMatches is not {} then
-- do something
end if
Couldn’t drum up much interest so I no longer do development or support. But they’re free and open (public domain as far as I’m concerned) and still work fine in the current version of macOS AFAIK, so help yourself.

Bash script " & " symbol creating an issues

I have a tag
<string name="currencysym">$</string>
in string.xml And what to change the $ symbol dynamically. Use the below command but didn't work:
currencysym=₹
sed -i '' 's|<string name="currencysym">\(.*\)<\/string>|<string name="currencysym">'"<!\[CDATA\[${currencysym}\]\]>"'<\/string>|g'
Getting OUTPUT:
<string name="currencysym"><![CDATA[<string name="currencysym">$</string>#x20B9;]]></string>
" & " Has Removed...
But I need:
<string name="currencysym"><![CDATA[₹]]></string>
using xml-parser/tool to handle xml is first choice
instead of <..>\(.*\)<..> better use <..>\([^<]*\)<..> in case you have many tags in one line
& in replacement has special meaning, it indicates the whole match (\0) of the pattern. That's why you see <...>..</..> came to your output. If you want it to be literal, you should escape it -> \&
First problem is the line
currencysym=₹
This actually reads as "assign empty to currencysym and start no process in the background":
In bash you can set an environment variable (or variables) just or one run of a process by doing VAR=value command. This is how currencysym= is being interpreted.
The & symbol means start process in the background, except there is no command specified, so nothing happens.
Everything after # is interpreted as a comment, so #x20B9; is just whitespace from Bash's point of view.
Also, ; is a command separator, like &, which means "run in foreground". It is not used here because it is commented out by #.
You have to either escape &, # and ;, or just put your string into single quotes: currencysym=\&\#x20B9\; or currencysym='₹'.
Now on top of that, & has a special meaning in sed, so you will need to escape it before using it in the sed command. You can do this directly in the definition like currencysym=\\\&\#x20B9\; or currencysym='\₹', or you can do it in your call to sed using builtin bash functionality. Instead of accessing ${currencysym}, reference ${currencysym/&/\&}.
You should use double-quotes around variables in your sed command to ensure that your environment variables are expanded, but you should not double-quote exclamation marks without escaping them.
Finally, you do not need to capture the original currency symbol since you are going to replace it. You should make your pattern more specific though since the * quantifier is greedy and will go to the last closing tag on the line if there is more than one:
sed 's|<string name="currencysym">[^<]*</string>|<string name="currencysym"><![CDATA['"${currencysym/&/\&}"']]></string>|' test.xml
Yields
<string name="currencysym"><![CDATA[₹]]></string>
EDIT
As #fedorqui points out, you can use this example to show off correct use of capture groups. You could capture the parts that you want to repeat exactly (the tags), and place them back into the output as-is:
sed 's|\(<string name="currencysym">\)[^<]*\(</string>\)|\1<![CDATA['"${currencysym/&/\&}"']]>\2|' test.xml
sed -i '' 's|\(<string name="currencysym">\)[^<]*<|\1<![CDATA[\₹]]><|g' YourFile
the group you keep in buffer is the wrong one in your code, i keep first part not the &
grouping a .* is not the same as all untl first < you need. especially with the goption meaning several occurence could occur and in this case everithing between first string name and last is the middle part (your group).
carrefull with & alone (not escaped) that mean 'whole search pattern find' in replacement part

How to select line in AppleScript

I'm trying to figure out how to use Text Item Delimiters on a long line of text that is in a log file.
Within the log of information there is always a constant phrase that i'm searching for which leads me to the line of text. I'm getting to the line I want by searching for "[Constant]", for example.
The problem I'm having is that I can't select the whole line to perform a Delimiter. Below is a very basic example of what the log looks like.
qwertyuiop
mnbvcxza
oqeryuiiop
[Constant] 1234567890123456-098765432109876-8765432118976543
odgnsgnsanfadf
joiergjdfmgadfs
Any advice would be appreciated.
So far I'm using:
repeat 16 times
key code 124 using (shift down)
end repeat
Which does the job fine but it is clunky.
An easy way to find a line of text containing a specific string is the shell command grep.
set theConstant to "Constant"
set theText to "qwertyuiop
mnbvcxza
oqeryuiiop
Constant 1234567890123456-098765432109876-8765432118976543
odgnsgnsanfadf
joiergjdfmgadfs"
set foundLine to do shell script "echo " & quoted form of theText & " | tr '\\r' '\\n' | grep " & quoted form of theConstant
the tr part to replace return (0x0d) characters with linefeed (0x0a) characters is necessary to conform to the shell line separator requirements.
If the constant contains special characters it's a bit more complicated, because you have to escape the characters before passing them to the shell.
set theConstant to "\\[Constant\\]"
set theText to "qwertyuiop
mnbvcxza
oqeryuiiop
[Constant] 1234567890123456-098765432109876-8765432118976543
odgnsgnsanfadf
joiergjdfmgadfs"
set foundLine to do shell script "echo " & quoted form of theText & " | tr '\\r' '\\n' | grep " & quoted form of theConstant
If you want to read the text from a file on disk you can use this
set logFile to (path to library folder from user domain as text) & "Logs:myLogFile.log"
set theText to read file logFile as «class utf8»
Your question is puzzling. Do you want to parse a text/log file or a script to work with the GUI of some app? Because that is what your code suggests...
If you want to parse a log file, which is easier, you can use the good old Unix tools OSX comes with. You can use them from inside Applescript like this...
set logfile to "/some/path/file.log"
# Quote the string in case it contains spaces... or add single quotes above...
set qlogfile to quoted form of logfile
# Prepare the shell command to run
set cmd to "grep '^\\[Constant]' " & qlogfile & " | cut -c 12- | tr '-' '\\n'"
# Run it and capture the output
try
set cmdoutput to (do shell script cmd)
on error
# Oh no, command errored. Best we do something there
end try
The result looks like this...
tell current application
do shell script "grep '^\\[Constant]' '/some/path/file.log' | cut -c 12- | tr '-' '\\n'"
--> "1234567890123456
098765432109876
8765432118976543"
end tell
Result:
"1234567890123456
098765432109876
8765432118976543"
So to break it down the shell commands are,
grep ... | will read the contents of the file and select all lines that start ^ with the text [Constant] and pass what it finds | on to the next command
cut cuts out the characters from position 12 until the end - of the line
tr replaced any character - with \n which is the code for newline in unix.
The \\ you see are due to having it executed from inside Applescript. You only need on if you run it inside Terminal.
If you care to know the contents of one line from the other, then remove the last command | tr '-' '\\n' and it will return
Result:
"1234567890123456-098765432109876-8765432118976543"

plain text URL to HTML code (Automator/AppleScript)

Suppose I have a plain txt file in a text editor such as TextEdit:
title 1
http://a.b/c
title 2
http://d.e/f
...
I'd like to convert all the lines beginning with http:// to HTML code for URL, so that the aforementioned content will become:
title 1
http://a.b/c
title 2
http://d.e/f
...
How can I get this done in Automator or AppleScript? (My current solution is using Gmail, but it involves multi-step copy-paste.)
Thank you very much in advance.
This will let you avoid another editor:
set inFile to "/Users/you/Desktop/Urls.txt"
set outFile to "/Users/you/Desktop/Urls2.txt"
do shell script "sed 's/\\(http[^ ]*\\)/<a href=\"\\1\">\\1<\\/a>/g' " & quoted form of inFile & " >" & quoted form of outFile
Just do a regex search and replace in a text editor or Terminal:
sed -E 's|^(http:.*)|\1|g' file.txt

Script to convert lower case characters into upper case is working differently as service action

I am trying a simple script as a service action in automator which performs this function:
Receives selected text in any application and replaces selected text
with the text containing capital letters
So I used this script:
on run {input, parameters}
set upperCaseString to ""
repeat with i in input
if (ASCII number i) > 96 and (ASCII number i) < 123 then
set upperCaseString to upperCaseString & (ASCII character ((ASCII number i) - 32))
else
set upperCaseString to upperCaseString & (ASCII character (ASCII number i))
end if
end repeat
return upperCaseString
end run
But I found this problem:
It was returning first letter of input as an upper case letter, eg.
input - lowercasetext, output - L, whereas the expected output was -
LOWERCASETEXT.
To check the problem I added this line of code in repeat loop:
display dialog i
and found that it is displaying complete text in place of single character at a time ,ie. in place of displaying l.. o.. w.. in lowercasetext it is displaying lowercasetext at once.
Can anyone suggest me why is it bugging me as service action while it is working fine in Apple Script Editor?
This works for a lot of languages:
on toUpper(s)
tell AppleScript to return do shell script "shopt -u xpg_echo; export LANG='" & user locale of (system info) & ".UTF-8'; echo " & quoted form of s & " | tr [:lower:] [:upper:]"
end toUpper
on toLower(s)
tell AppleScript to return do shell script "shopt -u xpg_echo; export LANG='" & user locale of (system info) & ".UTF-8'; echo " & quoted form of s & " | tr [:upper:] [:lower:]"
end toLower
When I run your script, I get the correct result. But one thing you may want to do is to explicitly coerce your result to text. The easiest way to do that would be at the end:
return upperCaseString as text
That may or may not do it for you, but you'll avoid a lot of frustration if you explicitly coerce data when there is a possibility of ambiguity.
Another (faster) way is to leverage the Unix tr (translate) command the via do shell script:
set upperCaseString to ¬
(do shell script ("echo " & input & " | tr a-z A-Z;"))
That's enough for 'English' language, but you can also add diacritical translation, like so
set upperCaseString to ¬
(do shell script ("echo " & input & " | tr a-zäáà A-ZÄÁÀ;"))
tr will translate anything to anything, so you can add any characters you may encounter and what you'd like them to translate to. A 'leet-speak' translator comes to mind.
You will get the same result in the AppleScript Editor if the input variable is set to a list. The input parameter of an Automator action is also a list, so your comparison isn't doing what you think. Note that text id's have obsoleted ASCII character and ASCII number commands - see the 10.5 AppleScript Release notes.
#Matt Strange:
You could also try:
set upperCaseString to ¬
do shell script "echo " & input & " | tr [:lower:] [:upper:]"
If you run 'man tr' on 'OS X 10.10' you may see that the character classes [:lower:] and [:upper:] should be used instead of explicit character ranges like 'a-z' or 'A-Z', since these may not produce correct results as it is explained there, on the manual page.

Resources