Remove the top line of a text file or variable

I am modifying an existing macro that I use to manually set a variable ‘varList’ to a list of numbers. Now I have the macro read and text file ‘Numbers.txt’ and sets the ‘varList’ for me. The text file is nothing more then a bunch of numbers such as:

 10000
 10001
 10007
 10200

The macro then acts on each line, doing a variety of things, and appends to a text file ‘Log.txt’ so I have a record of what was done with that number. This appending to the file works great.

What I need to do is overwrite or edit the ‘Numbers.txt’ file so the top number is removed when it was already used. This is because I may have a very long list of numbers and I must stop the macro and then I need it to start off where I had stopped it the next time I run the macro. As an example after the macro looped once through the content of the ‘Numbers.txt’ file would be:

 10001
 10007
 10200

I am guessing there may be a Filter option I can do to the ‘varList’ variable and then write that variable to the ‘Numbers.txt’ file but I am not finding a way to do that.

The net result would be my ‘Numbers.txt’ file would get a shorter list and my ‘Log.txt’ file would get a longer list of numbers.

Any help would be greatly appreciated!

This will remove the first line:

Remove First Line.kmmacros (2.4 KB)

Regex Reference

3 Likes

Thanks Tom, that did exactly what I needed!

Hey Stephen,

The simplest way to edit a text file in-place like that is to use sed in an Execute a Shell Script action.

Delete First Line from a Text File.kmmacros (3.9 KB)

Macro v1 is all in the shell script action.

NOTE – that quotes are only needed if there are spaces in the path – and that you CANNOT quote the ~/ part of the path.

Macro v2 is a little more user-friendly, because it does all the quoting and tilde-expansion for you.

To do this natively with Keyboard Maestro you'd have to read the file into a variable, delete the first line, and write the data back to the file. Easily done but a bit cumbersome.

Personally my go-to instrument for this sort of job is the Satimage.osax AppleScript Extension and a little regex.

==Moderator's Note== 2019-07-23

In 2016 when Chris wrote this script, Satimage was one of the most popular and power AppleScript tools. Today, in 2019, it is no longer advisable to use Satimage because:

  1. It won't run in Mojave+
  2. It is 32-bit, which won't run in Catalina+
  3. No updates are expected at this time.

So the best RegEx tool is a great Script Library written by world-renown Shane Stanley. You can get it at RegexAndStuffLib at MacOSXautomation.com .

If you need help converting the below script, let us know.

--------------------------------------------------------------------------------
# Auth: Christopher Stone
# dCre: 2016/08/26 12:32
# dMod: 2016/08/26 12:40 
# Appl: Satimage.osax
# Task: Delete First Line of Text File.
# Libs: None
# Osax: Satimage.osax → REQUIRED → http://tinyurl.com/dc3soh
# Tags: @Applescript, @Script, @Satimage.osax, @Delete, @First, @Line, @Text, @File
--------------------------------------------------------------------------------

set fileAlias to alias ((path to home folder as text) & "test_directory:KM_TEST:text_file.txt")
set deleteLineResult to change "\\A.*\\s?" into "" in fileAlias with regexp

if deleteLineResult = 0 then
   display dialog "All lines have been deleted from the text file!"
end if

--------------------------------------------------------------------------------

-Chris

A Bash route might capture lines 2 to end with something like:

and a JavaScript for Automation Action might include something like:


    writeFile(
        strPath,
        unlines(
            tail(
                lines(
                    readFile(strPath)
                )
            )
        )
    )

Where the reusable primitives might have definitions resembling:

 // readFile :: FilePath -> maybe String
    function readFile(strPath) {
        var error = $(),
            str = ObjC.unwrap(
                $.NSString.stringWithContentsOfFileEncodingError(
                    $(strPath)
                    .stringByStandardizingPath,
                    $.NSUTF8StringEncoding,
                    error
                )
            );
        return error.code ? error.localizedDescription : str;
    }


    // writeFile :: FilePath -> String -> IO ()
    function writeFile(strPath, strText) {
        $.NSString.alloc.initWithUTF8String(strText)
            .writeToFileAtomicallyEncodingError(
                $(strPath)
                .stringByStandardizingPath, true,
                $.NSUTF8StringEncoding, null
            );
    }


    // lines :: String -> [String]
    function lines(s) {
        return s.split(/[\r\n]/);
    }

    // unlines :: [String] -> String
    function unlines(xs) {
        return xs.join('\n');
    }
    
    
    // tail :: [a] -> [a]
    function tail(xs) {
        return xs.length ? xs.slice(1) : undefined;
    }
  • Read File “Numbers.txt” to variable Numbers
  • While variable Numbers is not empty
    • Search Variable Numbers for \A(.)\n(?s:.) to variable This and variable Numbers
    • Write File “Numbers.txt” with %Variable%Numbers%
    • Operate with variable This

wow did a quick search for this and found your solution Tom. Just wanted to say thanks!

Thanks for the feedback.

You can streamline the regex like this:

^.*\R

The \R also catches any Line Feed (LF aka \n) and any Carriage Return (CR aka \r), and any weird combination of those (CRLF) or other types of line breaks, and the expression is shorter.

Works also outside of KM (ICU), if the regex flavour is Perlish (PCRE and similar).

1 Like

Remember that \R works only in OS X 10.11+. In 10.10, \R means simply "R" (I learned that one the hard way!).

1 Like

Found this helpful deleting the first line of a variable list, thanks.
It does blank out the first line, but keeps that line empty and still on top.
Any tips on deleting that blank line, so that the next line becomes the first line?

Did you try ^.*\R as posted here?

I have now, and it works, thanks. Didn't try it before toto keep it compatible with older OS versions, like Peter mentioned, but i guess there's really no need today :slight_smile:

So, most likely, you had Windows line breaks, which are a sequence of a Carriage Return (\r) and a Line Feed (\n).

The [\n\r] class as in my first proposal only catches one occurrence of either.

\R catches any kind of line break and also a sequence of those.

You could replicate the \R behavior with a (more sophisticated) expression only consisting of \n and \r, but that’s why somebody has invented \R. Unless extreme backwards compatibility is needed.

Actually, there isn’t a need to read the file to a variable in KM. One can search and replace directly in the file (using Source as the destination).

1 Like