-1

I have a text variable of class text that shows something like this when it is returned:

"
This is sentence 1.
This is sentence 2.
This is sentence 3.
This is sentence 4."

I want to be able to delete specific lines from the text, without affecting the rest of the variable.

For example: delete {1, 3} to get this result:

"This is sentence 1.
This is sentence 3.
This is sentence 4."

The method described in this answer has a serious bug:

"How to delete a specific line of a paragraph in AppleScript?"

The linked method to delete lines in a paragraph actually converts all linefeeds in the text variable to returns. In other words, it is impossible to run this code more than once on the same variable.

For example, the following code:

set varText to "
This is sentence 1.
This is sentence 2.
This is sentence 3.
This is sentence 4."
set varText to do shell script "sed -e '1d;3d' <<< " & quoted form of varText
-- Employing the same method on the same variable:
set varText to do shell script "sed -e '1d;3d' <<< " & quoted form of varText
return varText

returns

""

Since this method only works correctly if the lines of the text variable are the product of a linefeed (as it should), the problem is not that this method fails to perceive a return as a new line (as I had originally claimed in this question). The problem is that this code introduces a return to the text variable in the first place.


Thus, I want a solution that will allow me to run the same text variable through the solution more than once in the same AppleScript.

In other words, I am looking for a method to remove a specific line from a paragraph that does not have this bug and does not insert a return anywhere in the text.

  • Regarding info in your Edit:: The do shell script "sed ..." command when run in Terminal with that varText returns with \n (x0A) not \r (x0D) as it's supposed to. Even the compiled do shell script "sed ..." command in Script Editor still has \n (x0A) not \r (x0D) however why it's being returned with \r (x0D) intend of the expected \n (x0A) is a mystery to me at the moment. I'm going to consider this an AppleScript bug since it not mirroring the expected behavior of the same when run in Terminal. This is why I deleted my answer because I wasn't aware of the issue. – user3439894 Mar 16 '17 at 15:31
  • GrahamMiln Since now I've identified the bug with the original answer that led me to post the second question, is it possible to merge these two questions together? If not, can you just delete this question altogether, since @user3439894 has provided their updated answer on the second question? – rubik's sphere Mar 16 '17 at 18:21
  • I don't want to delete this question myself, because doing so will result in a strike against my SE account. (I understand that the act of deleting one's own question is penalized). – rubik's sphere Mar 16 '17 at 18:28
  • On your point about using return, I of course agree! In reality, I don't use and have never used return to concatenate multi-line strings! That's why I had to significantly edit the title and body of this question; my post contained several inaccurate statements. This post was first published before I had picked up on the bug in your code, which (I eventually realized) explained where all the returns in my text variables were coming from! I had originally assumed that I was—somehow—responsible for the prevalence of the return characters, which, now I know, is not the case. – rubik's sphere Mar 17 '17 at 1:29
  • First of all it was not my code per se that had the bug! It's a bug in how the do shell script command is processing what's returned from the command line, it's AppleScript that's not properly processing the output of the command(s) executed within the do shell script command! Your use of return as a multi-line string concatenation was a problem as sed saw the contents of the variable as one line and deleted it because it's expecting x0A not x0D line endings. Anyway my updated answer handles both the poor use of return over linefeed and the bug. – user3439894 Mar 17 '17 at 2:17
1

Okay, I've deleted the original and first edit because you've edited your originally question to the point it's easier to write a new answer altogether.

Since your originally question showed the following line of code, as an example of how the variable may be set, I'm going to include it to say the following.

Whether the varText has been set by e.g.:

set varText to (return & "This is sentence 1." & return & "This is sentence 2." & return & "This is sentence 3." & return & "This is sentence 4.")
  • In which return is x0D vs. the more proper use of linefeed (x0A) instead, in a case where the variable is data and not a disposable message.

Or:

set varText to "
This is sentence 1.
This is sentence 2.
This is sentence 3.
This is sentence 4."
  • In which each of these lines actually end with a linefeed (x0A) as it should be on a Mac.

The do shell script command has a bug as it converts x0A to x0D after what's returned from the command line having the expected x0A endings passed back. I confirmed this because if I use the following:

set varText to "
This is sentence 1.
This is sentence 2.
This is sentence 3.
This is sentence 4."

set varText to do shell script "sed  -e '1d;3d' <<< " & quoted form of varText & " | tee $HOME/Desktop/outfile"

Then outfile contains linefeed (x0A) endings so the same is returned to do shell script but it then erroneously converts the x0A line endings to x0D endings which can then handled by the following:

So, to accommodate the bug, always insure the content of the variable passed and returned contains x0A line endings by using the following handler and example code.

The following assumes that varText has already been set by either of the methods described above.

on ensureLinesEndWith0A(varText)
    set varText to paragraphs of varText
    set oldTID to AppleScript's text item delimiters
    set AppleScript's text item delimiters to linefeed
    set varText to varText as string
    set AppleScript's text item delimiters to oldTID
    return varText
end ensureLinesEndWith0A

set varText to ensureLinesEndWith0A(varText)
set varText to do shell script "sed  -e '1d;3d' <<< " & quoted form of varText
set varText to ensureLinesEndWith0A(varText)

You could then use the following again to delete more lines from varText:

set varText to ensureLinesEndWith0A(varText)
set varText to do shell script "sed  -e '1d;3d' <<< " & quoted form of varText
set varText to ensureLinesEndWith0A(varText)

The image below shows and example of calling the do shell script " sed ..." command twice.

image of code example

  • @rubik's sphere, Checkout the image I added to my answer. – user3439894 Mar 17 '17 at 3:35
  • I have tested your text item delimiters to linefeed workaround. I believe that it successfully corrects the pernicious little AppleScript bug that we've identified. I can now perform countless do shell script "sed -e... operations on the same text variable within the same run of a script, and the specified lines are reliably deleted as intended. Thank you for your help! – rubik's sphere Mar 17 '17 at 4:50
1

Example:

set varText to "
This is sentence 1.
This is sentence 2.
This is sentence 3.
This is sentence 4."

set varText to do shell script "sed -e '1d;3d' <<< " & quoted form of varText

Returns:

"This is sentence 1.
This is sentence 3.
This is sentence 4."

Update: As a result of a discovery mentioned in the Edit: of How to delete a specific line of a “return”-based paragraph in AppleScript?, let me make the following statement:

NOTE: Unfortunately what's returned in this case has carriage return (x0D) characters instead of the expected new line (x0A) characters and in my opinion is a bug!

It's a bug because: The do shell script "sed ..." command when run in Terminal with that varText returns with \n (x0A) not \r (x0D) as it's supposed to. Even the compiled do shell script "sed ..." command in Script Editor still has \n (x0A) not \r (x0D) however why it's being returned with \r (x0D) intend of the expected \n (x0A) is a mystery to me at the moment and I'm going to consider this an AppleScript bug since it not mirroring the expected behavior of the same when run in Terminal.

So, to account for the bug in the results of the do shell script "sed ..." command, this is how I'd handle it. After the do shell script "sed ..." command use the following lines of code:

set newLine to "\n"
set varText to paragraphs of varText
set oldTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to newLine
set varText to varText as string
set AppleScript's text item delimiters to oldTID

Now what's returned contains new line (x0A) characters as it should have to begin with if there wasn't this bug, not carriage return (x0D) characters.


Note: When compiled, the set newLine to "\n" line of code will show as below:

set newLine to "
"
  • If I am removing only one line, do you suggest that I include the semicolon? I tried it both ways (with and without the semicolon) and it seems to work fine either way, but I am just asking for your preference. – rubik's sphere Mar 16 '17 at 3:52
  • @rubik's sphere, The semi-colon is only needed as a separator when more then one range is specified, in this use case. Which line are you wanting to delete? – user3439894 Mar 16 '17 at 11:41
  • Did you see the last part of my new question? It is impossible to run this method twice on the same variable, which must mean that your line deletion method alters the new line (x0A) characters to something else (in the process of deleting a line). – rubik's sphere Mar 16 '17 at 15:15
0

Let's break down this script into smaller pieces and then put it all together in one AppleScript.

This approach is written entirely in AppleScript and requires no external tools. Using external tools, like perl or sed, will likely result in much shorter solutions but you have mentioned in other questions wanting to learn AppleScript.

1. Split with Delimiters

You can use AppleScript's delimiters behaviour to split a string. You can change the delimiter as desired to return, linefeed, or even characters like = :

 -- http://macscripter.net/viewtopic.php?id=24473
 to split(someText, delimiter)
     set AppleScript's text item delimiters to delimiter
     set someText to someText's text items
     set AppleScript's text item delimiters to {""} --> restore delimiters to default value
     return someText
 end split

 set myText to (return & "This is sentence 1." & return & "This is sentence 2." & return & "This is sentence 3." & return & "This is sentence 4.")
 set myLines to split(myText, return)

2. Filter List

With the returned list, you can filter out the undesirable items using a loop:

 -- http://macscripter.net/viewtopic.php?id=24525
 set indexesToDelete to {1, 4}
 set cleanList to {}

 repeat with i from 1 to count myLines
     if i is not in indexesToDelete then set cleanList's end to myLines's item i
 end repeat

3. Combine the Items

The filtered list of sentences can be re-combined using another loop:

-- Combine the filtered list into a string
set myResult to ""
repeat with i from 1 to count cleanList
    if myResult is "" then
        set myResult to cleanList's item i
    else
        set myResult to myResult & return & cleanList's item i
    end if
end repeat

Entirely AppleScript

Combining these snippets results in the following code:

-- Get the text to work with
set myText to (return & "This is sentence 1." & return & "This is sentence 2." & return & "This is sentence 3." & return & "This is sentence 4.")

-- Split the text into lines based on 'return' delimiter
set myLines to split(myText, return)

-- Filter out indexes 1 and 4 using a loop
set indexesToDelete to {1, 4}
set cleanList to {}
repeat with i from 1 to count myLines
    if i is not in indexesToDelete then set cleanList's end to myLines's item i
end repeat

-- Combine the filtered list into a string
set myResult to ""
repeat with i from 1 to count cleanList
    if myResult is "" then
        set myResult to cleanList's item i
    else
        set myResult to myResult & return & cleanList's item i
    end if
end repeat

-- Final string
myResult

to split(someText, delimiter)
    set AppleScript's text item delimiters to delimiter
    set someText to someText's text items
    set AppleScript's text item delimiters to {""} --> restore delimiters to default value
    return someText
end split

The script above could be reduced by combining the filter and re-combination loops. I have left these as separate tasks to better demonstrate their roles.

  • Your strategy also suffers from the exact same bug found in the original method provided by @user3439894 (the bug is described in the "Edit:" part of my post). I.e., if myText contains linefeeds instead of returns, and you use the line: set myLines to split(myText, linefeed), your code will convert every linefeed that exists in the text to a return, making it impossible for the same text variable to successfully run through your method more than once. I was hoping for a solution that doesn't possess this problematic side effect; I want to delete lines from the same variable more than once. – rubik's sphere Mar 16 '17 at 19:37
  • 1
    @rubik's sphere, Graham Miln's answer does not suffer from the same bug as he's not using a do shell script command, which is where the bug is. He's using return just like you originally showed in the original version of this question! You should not use return to concatenate multi-line strings, you should use linefeed instead. As a matter of fact if you replaced all be the last return to linefeed in his answer it would work as expected by maintaining x0A line endings vs. the x0D which return does when used as a text class special string character constant. – user3439894 Mar 17 '17 at 1:03
  • @user3439894 You are right in that it is not, technically, the "same" bug. To be clear, Graham's method has a different bug, but one with the exact same effect as the bug in your do shell script method. This is the test: using linefeeds in the place of returns in Graham's answer, can you run a specific text variable through his method more than once in the same script with everything still working properly (e.g., by first deleting the second line of the paragraph and then deleting the third line, in two separate statements)? – rubik's sphere Mar 17 '17 at 1:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .