I would like to search the contents of a .txt file for a specific line of text and delete only that line from the .txt file.
I want to specify the line of text to find as a variable. For example:
set lineOfTextToDelete to "The quick brown fox jumps over the lazy dog."
Contents before:
Let's say the contents of my TestDelta.txt file is:
This is a a paragraph of text.
This is another line of text.
The quick brown fox jumps over the lazy dog.
Here is another line
Contents after:
The following shows the contents of the TestDelta.txt that I want after running the script. As you can see the string which has been assigned to the lineOfTextToDelete variable, i.e. "The quick brown fox jumps over the lazy dog." has been deleted from the contents of the file.
This is a a paragraph of text.
This is another line of text.
Here is another line
What I've tried so far:
Below is what I've tried, however I'm unsure what I should do next?
set txtfile to "Macintosh HD - Data:Users:crelle:Desktop:TestDelta.txt" as alias
set thisone to read txtfile
set theTextList to paragraphs of thisone
Can anyone help show me what to do?
Here are, in no particular order, a couple of solutions to consider.
Before usage I recommend creating a backup copy of any .txt file that you're going to try them with. These scripts can potentially cause loss of valuable data if not used carefully.
If you have any concerns regarding assignment of the correct filepath to either;
The txtFilePath variable in Solution A
The txtFilePath property in Solution B
then replace either of those lines with the following. This will enable you to choose the file instead.
set txtFilePath to (choose file)
Solution A: Shell out from AppleScript and utilize SED (Stream EDitor)
on removeMatchingLinesFromFile(findStr, filePath)
set findStr to do shell script "sed 's/[^^]/[&]/g; s/\\^/\\\\^/g' <<<" & quoted form of findStr
do shell script "sed -i '' '/^" & findStr & "$/d' " & quoted form of (POSIX path of filePath)
end removeMatchingLinesFromFile
set txtFilePath to "Macintosh HD - Data:Users:crelle:Desktop:TestDelta.txt"
set lineOfTextToDelete to "The quick brown fox jumps over the lazy dog."
removeMatchingLinesFromFile(lineOfTextToDelete, txtFilePath)
Explanation:
The arbitrarily named removeMatchingLinesFromFile subroutine / function contains the tasks necessary to meet your requirement. It lists two parameters; findStr and filePath. In its body we "shell out" twice to sh by utilizing AppleScript's do shell script command.
Let's understand what's happening here in more detail:
The first line that reads;
set findStr to do shell script "sed 's/[^^]/[&]/g; s/\\^/\\\\^/g' <<<" & quoted form of findStr
executes a sed command. The purpose of this command is to escape any potential Basic Regular Expression (BRE) metacharacters that may exist in the given line of text that we want to delete. Utlimately it ensures each character in the given string is treated as a literal when used in the subsequent sed command - thus negating any "special meaning" the metacharacter has.
Refer to this answer for further explanation. Essentially it does the following:
Every character except ^ is placed in its own character set [...] expression to treat it as a literal.
Note that ^ is the one char. you cannot represent as [^], because it has special meaning in that location (negation).
Then, ^ chars. are escaped as \^.
Note that you cannot just escape every char by putting a \ in front of it because that can turn a literal char into a metachar, e.g. \< and \b are word boundaries in some tools, \n is a newline, \{ is the start of a RE interval like \{1,3\}, etc.
Credit for this SED pattern goes to Ed Morton and mklement0.
So, given that the string assigned to the variable named lineOfTextToDelete is:
The quick brown fox jumps over the lazy dog.
we actually end up assigning the following string to the findStr variable after it has been parsed via the sed command:
[T][h][e][ ][q][u][i][c][k][ ][b][r][o][w][n][ ][f][o][x][ ][j][u][m][p][s][ ][o][v][e][r][ ][t][h][e][ ][l][a][z][y][ ][d][o][g][.]
As you can see each character is wrapped in opening and closing square brackets, i.e. [], to form a series of bracket expressions.
To further demonstrate what's happening; launch your Terminal application and run the following compound command:
sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"The quick brown fox jumps over the lazy dog."
Note When running the aforementioned compound command directly via the Terminal the sed pattern contains less backslashes (\) in comparison to the pattern specified in the AppleScript. This is because AppleScript strings require any backslash to be escaped with an additional backslash.
The second line reading;
do shell script "sed -i '' '/^" & findStr & "$/d' " & quoted form of (POSIX path of filePath)
executes another sed command via the shell. This performs the task of finding all instances of the given line of text in the file and deletes it/them.
The -i option specifies that the file is to be edited in-place, and requires a following empty string argument ('') when using the BSD version of sed that ships with macOS.
The '/^" & findStr & "$/d' part is the pattern that we provide to sed.
The ^ metacharacter matches the null string at beginning of the pattern space - it essentially means start matching the subsequent regexp pattern only if it exists at the beginning of the line.
The Applescript findStr variable is the result we obtained via the previous sed command. It is concatenated with the preceding pattern part using the & operator.
The $ metacharacter refers to the end of pattern space, i.e. the end of the line.
The d is the delete command.
The & quoted form of (POSIX path of filePath) part utilizes AppleScript's POSIX path property to transform your specified HFS path, i.e.
Macintosh HD - Data:Users:crelle:Desktop:TestDelta.txt
to the following POSIX-style path:
/Macintosh HD - Data/Users/crelle/Desktop/TestDelta.txt
The quoted form property ensures correct quoting of the POSIX-style path. For example, it ensures any space character(s) in the given pathname are interpreted correctly by the shell.
Again, to further demonstrate what's happening; launch your Terminal application and run the following compound command:
sed -i '' '/^[T][h][e][ ][q][u][i][c][k][ ][b][r][o][w][n][ ][f][o][x][ ][j][u][m][p][s][ ][o][v][e][r][ ][t][h][e][ ][l][a][z][y][ ][d][o][g][.]$/d' ~/Desktop/TestDelta.txt
Let's understand how to use the aforementioned removeMatchingLinesFromFile function:
Firstly we assign the same HFS path that you specified in your question to the arbitrarily named txtFilePath variable:
set txtFilePath to "Macintosh HD - Data:Users:crelle:Desktop:TestDelta.txt"
Next we assign the line of text that we want to find and delete to the arbitrarily named lineOfTextToDelete variable:
set lineOfTextToDelete to "The quick brown fox jumps over the lazy dog."
Finally we invoke the custom removeMatchingLinesFromFile function, passing in two required arguments namely; lineOfTextToDelete and txtFilePath:
removeMatchingLinesFromFile(lineOfTextToDelete, txtFilePath)
Solution B: Using vanilla AppleScript without SED:
This solution provided below does not utilize the shell, nor SED, and produces the same desired result as per Solution A.
property lineOfTextToDelete : "The quick brown fox jumps over the lazy dog."
property txtFilePath : alias "Macintosh HD - Data:Users:crelle:Desktop:TestDelta.txt"
removeMatchingLinesFromFile(lineOfTextToDelete, txtFilePath)
on removeMatchingLinesFromFile(findStr, filePath)
set paraList to {}
repeat with aLine in getLinesFromFile(filePath)
if contents of aLine is not findStr then set paraList to paraList & aLine
end repeat
set newContent to transformListToText(paraList, "\n")
replaceFileContents(newContent, filePath)
end removeMatchingLinesFromFile
on getLinesFromFile(filePath)
if (get eof of filePath) is 0 then return {}
try
set paraList to paragraphs of (read filePath)
on error errorMssg number errorNumber
error errorMssg & errorNumber & ": " & POSIX path of filePath
end try
return paraList
end getLinesFromFile
on transformListToText(ListOfStrings, delimiter)
set {tids, text item delimiters} to {text item delimiters, delimiter}
set content to ListOfStrings as string
set text item delimiters to tids
return content
end transformListToText
on replaceFileContents(content, filePath)
try
set readableFile to open for access filePath with write permission
set eof of readableFile to 0
write content to readableFile starting at eof
close access readableFile
return true
on error errorMssg number errorNumber
try
close access filePath
end try
error errorMssg & errorNumber & ": " & POSIX path of filePath
end try
end replaceFileContents
Explanation:
I'll keep this explanation brief as the code itself is probably easier to comprehend than Solution A.
The removeMatchingLinesFromFile subroutine essentially performs the following with the aid of additional helper functions:
read's the contents of the given .txt file via the getLinesFromFile function and return's a list. Each item in the returned list holds each line/paragraph of text found in the .txt file content.
We then loop through each item (i.e. each line of text) via a repeat statement. If the contents of each item does not equal the given line of text to find we store it in another list, i.e. the list assigned to the paraList variable.
Next, the list assigned to the paraList variable is passed to the transformListToText function along with a newline (\n) delimiter. The transformListToText function returns a new string.
Finally, via the replaceFileContents function, we open for access the original .txt file and overwrite its contents with the newly constructed content.
Important note applicable to either solution: When specifying the line of text that you want to delete, (i.e. the string that is assigned to the lineOfTextToDelete variable), ensure each and every backslash \ that you may want to search for is escaped with another one. For example; if the line that you want to search for contains a single backslash \ then escape it to become two \\. Similarly if the line that you want to search for contains two consecutive backslashes \\ then escape each one to become four \\\\, and so on.
Related
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.
I'm splitting a string using osascript (was working this way, not with bash), and assigning the resulting array to a bash variable, and continuing with my bash script. I'm doing it like so:
tempArrayApplications=$(osascript >/dev/null <<EOF
set oldDelimiters to AppleScript's text item delimiters
set AppleScript's text item delimiters to "/"
set theArray to every text item of "$noSplitString"
set AppleScript's text item delimiters to oldDelimiters
return theArray
EOF)
However, the command line returns the error that it went to the end of the file without finding the matching ')'. However, when I don't assign a bash variable to the osascript output, everything works fine, so I know it's not a problem with the AppleScript section. I ran shellcheck, and it doesn't detect any errors, and the other solutions seem to be related to an unclosed quote or unescaped character, but I don't seem to have that problem. Clearly it's due to trying to assign it to a bash variable, but for the life of me I don't know what I'm doing wrong. Thanks for the help.
Have you paused to consider that you're taking a bash variable ($noSplitString); inserting this into an AppleScript that splits the text using / as the delimiter; executing this AppleScript inside a bash command (osascript); then storing its output (which actually gets destroyed) in another bash variable ($tempArrayApplications)...?
My inclination would be to remove the AppleScript altogether (3 out of its 5 lines are redundant, anyway), and create the array from the string within bash.
So, given this:
noSplitString="item 1/item 2/item 3"
Then simply do this:
IFS='/'
tempArrayApplications=($noSplitString)
Now $tempArrayApplications will be an array with three items, starting at index 0 and ending at index 2. You can echo a specific element in the array like this:
echo "${tempArrayApplications[1]}" # "item 2"
IFS is the bash equivalent of the AppleScript text item delimiters. It typically has a default value of ⎵\t\n (where ⎵ indicates a space character). More can be read about it in this article: Bash IFS: its Definition, Viewing it and Modifying it
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
I have a input file of type .txt :
// file.txt
The quick brown dog jumped over the lazy fox.
Basically I want to use this input file to produce an output file with each string quote-enclosed and on its own line in CMD. Any ideas?
Output should be like this:
'The'
'quick'
etc...
#echo off
set /p text=<file.txt
(for %%i in (%text%) do echo '%%i')>newfile.txt
I note now that the terms were supposed to be enclosed in single quotes - which the code below doesn't do. It just does what the subject line asks and separates each term on a new line by itself.
Here is a more robust solution that handles poison characters, and retains all characters in the file, and is far quicker on large files.
This uses a helper batch file called repl.bat (preview as gist or download from dropbox)
Place repl.bat in the same folder as the batch file or in a folder that is on the path.
type "file.txt" |repl " " "\r\n" x >"newfile.txt"
In Unix I could run myscript '"test"' and I would get "test".
In Windows cmd I get 'test'.
How can I pass double-quotes as a parameter? I would like to know how to do this manually from a cmd window so I don't have to write a program to test my program.
Another way to escape quotes (though probably not preferable), which I've found used in certain places is to use multiple double-quotes. For the purpose of making other people's code legible, I'll explain.
Here's a set of basic rules:
When not wrapped in double-quoted groups, spaces separate parameters:program param1 param2 param 3 will pass four parameters to program.exe: param1, param2, param, and 3.
A double-quoted group ignores spaces as value separators when passing parameters to programs:program one two "three and more" will pass three parameters to program.exe: one, two, and three and more.
Now to explain some of the confusion:
Double-quoted groups that appear directly adjacent to text not wrapped with double-quotes join into one parameter:hello"to the entire"world acts as one parameter: helloto the entireworld.
Note: The previous rule does NOT imply that two double-quoted groups can appear directly adjacent to one another.
Any double-quote directly following a closing quote is treated as (or as part of) plain unwrapped text that is adjacent to the double-quoted group, but only one double-quote:"Tim says, ""Hi!""" will act as one parameter: Tim says, "Hi!"
Thus there are three different types of double-quotes: quotes that open, quotes that close, and quotes that act as plain-text.
Here's the breakdown of that last confusing line:
" open double-quote group
T inside ""s
i inside ""s
m inside ""s
inside ""s - space doesn't separate
s inside ""s
a inside ""s
y inside ""s
s inside ""s
, inside ""s
inside ""s - space doesn't separate
" close double-quoted group
" quote directly follows closer - acts as plain unwrapped text: "
H outside ""s - gets joined to previous adjacent group
i outside ""s - ...
! outside ""s - ...
" open double-quote group
" close double-quote group
" quote directly follows closer - acts as plain unwrapped text: "
Thus, the text effectively joins four groups of characters (one with nothing, however):
Tim says, is the first, wrapped to escape the spaces
"Hi! is the second, not wrapped (there are no spaces)
is the third, a double-quote group wrapping nothing
" is the fourth, the unwrapped close quote.
As you can see, the double-quote group wrapping nothing is still necessary since, without it, the following double-quote would open up a double-quoted group instead of acting as plain-text.
From this, it should be recognizable that therefore, inside and outside quotes, three double-quotes act as a plain-text unescaped double-quote:
"Tim said to him, """What's been happening lately?""""
will print Tim said to him, "What's been happening lately?" as expected. Therefore, three quotes can always be reliably used as an escape.However, in understanding it, you may note that the four quotes at the end can be reduced to a mere two since it technically is adding another unnecessary empty double-quoted group.
Here are a few examples to close it off:
program a b REM sends (a) and (b)
program """a""" REM sends ("a")
program """a b""" REM sends ("a) and (b")
program """"Hello,""" Mike said." REM sends ("Hello," Mike said.)
program ""a""b""c""d"" REM sends (abcd) since the "" groups wrap nothing
program "hello to """quotes"" REM sends (hello to "quotes")
program """"hello world"" REM sends ("hello world")
program """hello" world"" REM sends ("hello world")
program """hello "world"" REM sends ("hello) and (world")
program "hello ""world""" REM sends (hello "world")
program "hello """world"" REM sends (hello "world")
Final note: I did not read any of this from any tutorial - I came up with all of it by experimenting. Therefore, my explanation may not be true internally. Nonetheless all the examples above evaluate as given, thus validating (but not proving) my theory.
I tested this on Windows 7, 64bit using only *.exe calls with parameter passing (not *.bat, but I would suppose it works the same).
I cannot quickly reproduce the symptoms: if I try myscript '"test"' with a batch file myscript.bat containing just #echo.%1 or even #echo.%~1, I get all quotes: '"test"'
Perhaps you can try the escape character ^ like this: myscript '^"test^"'?
Try this:
myscript """test"""
"" escape to a single " in the parameter.
The 2nd document quoted by Peter Mortensen in his comment on the answer of Codesmith made things much clearer for me. That document was written by windowsinspired.com. The link repeated: A Better Way To Understand Quoting and Escaping of Windows Command Line Arguments.
Some further trial and error leads to the following guideline:
Escape every double quote " with a caret ^. If you want other characters with special meaning to the Windows command shell (e.g., <, >, |, &) to be interpreted as regular characters instead, then escape them with a caret, too.
If you want your program foo to receive the command line text "a\"b c" > d and redirect its output to file out.txt, then start your program as follows from the Windows command shell:
foo ^"a\^"b c^" ^> d > out.txt
If foo interprets \" as a literal double quote and expects unescaped double quotes to delimit arguments that include whitespace, then foo interprets the command as specifying one argument a"b c, one argument >, and one argument d.
If instead foo interprets a doubled double quote "" as a literal double quote, then start your program as
foo ^"a^"^"b c^" ^> d > out.txt
The key insight from the quoted document is that, to the Windows command shell, an unescaped double quote triggers switching between two possible states.
Some further trial and error implies that in the initial state, redirection (to a file or pipe) is recognized and a caret ^ escapes a double quote and the caret is removed from the input. In the other state, redirection is not recognized and a caret does not escape a double quote and isn't removed. Let's refer to these states as 'outside' and 'inside', respectively.
If you want to redirect the output of your command, then the command shell must be in the outside state when it reaches the redirection, so there must be an even number of unescaped (by caret) double quotes preceding the redirection. foo "a\"b " > out.txt won't work -- the command shell passes the entire "a\"b " > out.txt to foo as its combined command line arguments, instead of passing only "a\"b " and redirecting the output to out.txt.
foo "a\^"b " > out.txt won't work, either, because the caret ^ is encountered in the inside state where it is an ordinary character and not an escape character, so "a\^"b " > out.txt gets passed to foo.
The only way that (hopefully) always works is to keep the command shell always in the outside state, because then redirection works.
If you don't need redirection (or other characters with special meaning to the command shell), then you can do without the carets. If foo interprets \" as a literal double quote, then you can call it as
foo "a\"b c"
Then foo receives "a\"b c" as its combined arguments text and can interpret it as a single argument equal to a"b c.
Now -- finally -- to the original question. myscript '"test"' called from the Windows command shell passes '"test"' to myscript. Apparently myscript interprets the single and double quotes as argument delimiters and removes them. You need to figure out what myscript accepts as a literal double quote and then specify that in your command, using ^ to escape any characters that have special meaning to the Windows command shell. Given that myscript is also available on Unix, perhaps \" does the trick. Try
myscript \^"test\^"
or, if you don't need redirection,
myscript \"test\"
I'm calling powershell from cmd, and passing quotes and neither escapes here worked. The grave accent worked to escape double quotes on this Win 10 surface pro.
>powershell.exe "echo la`"" >> test
>type test
la"
Below are outputs I got for other characters to escape a double quote:
la\
la^
la
la~
Using another quote to escape a quote resulted in no quotes.
As you can see, the characters themselves got typed, but didn't escape the double quotes.
Maybe you came here, because you wonder how to escape quotes that you need in the command that you pass to /c on cmd.exe? Well you don't:
CMD /c "MKDIR "foo bar""
will execute
MKDIR "foo bar"
which is really a behavior that I did not expect in the first glance.