2

I need to replace - WOR to VWOR in end of the file.

I have tried

sed s/WOR/VWOR/g    

But this does not work until I have new blank line in file.

So I used

( cat FileName && echo ) | sed 's/WOR/VWOR/g'  

This worked. But it is taking long time to replace as the sed is reading the entire file.

3

Make sure the file ends in a new line. That is a requirement of "text files".

This command will do exactly that:

[ -n $(tail -c1 FileName) ] && printf '\n' >> FileName

It will add a trailing new line only if one is needed, so there is no problem if it is used several times, also, it is very fast as it only checks the last character.

Then, do the change on the (now valid) last line ($):

sed -ie '$s/WOR/VWOR/'

Note: This command will convert a symlink to a real file.

  • The sed trick only modifies the last line, but at least the one I have (GNU 4.2.1 Redhat 10.el6) does read and write the whole file; on my not-stellar test box doing 1G takes 7 minutes with CPU pinned. At least for GNU, grep '' or awk 1 also fixes the last line, much faster. Or awk can fix and substitute in one pass: awk 'f{print p}{f=1;p=$0} END{sub(/WOR/,"VWOR",p);print p}' – dave_thompson_085 Nov 12 '16 at 15:38
  • @dave_thompson_085 Yes, sed -i -e '$a\' and also awk '1' and grep '' read the whole file, and need a temporary file to store the result. Instead tail -c1 will only read 1 byte from the file, and, if needed, a printf '\n' >> file will also do the append in almost no time. The awk solution you propose takes the same time as sed in my tests. – sorontar Nov 13 '16 at 2:18
0

Well, I don't know how to solve this with sed/awk or other. Here it is in C and it should be quite fast:

/*
by Hans Schou <chlor@schou.dk> 2016
Use on your own risk.
vim: ts=4 :
gcc -o replacelastline replacelastline.c && ./replacelastline WOR VWOR Filename V
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define SEARCH 1
#define REPLACE 2
#define FILENAME 3
#define VERBOSE 4
int main(int argc, char *argv[]) {
    if (FILENAME >= argc) {
        printf("Error, too few args. Only found %d.\n", argc);
        return EXIT_FAILURE;
    }
    printf("argc %d.\n", argc);

    int V = 0;
    if (VERBOSE <= argc) {
        V = 'V' == argv[VERBOSE][0] ? 1 : 0;
        printf("Verbose: %d\n", V);
    }

    if (V) {
        printf("argv[SEARCH]: %d %s\n", strlen(argv[SEARCH]), argv[SEARCH]);
        printf("argv[REPLACE]: %d %s\n", strlen(argv[REPLACE]), argv[REPLACE]);
        printf("argv[FILENAME]: %s\n", argv[FILENAME]);
        printf("Open file: %s\n", argv[3]);
    }

    size_t newFileSize = 0;
    FILE *fp = fopen(argv[FILENAME],"r+");
    if (NULL == fp) {
        fprintf(stderr, "Error open file: %s\n", argv[FILENAME]);
        return EXIT_FAILURE;
    } else {
        char inpBuf[8192];
        /* Seek to end-of-file minus buffer size */
        if (0 == fseek(fp, -sizeof(inpBuf), SEEK_END)) {
            /* Read buffer with last part of file */
            if (sizeof(inpBuf) != fread(&inpBuf, 1, sizeof(inpBuf), fp)) {
                fprintf(stderr, "Error, could not read %d bytes from '%s'\n", sizeof(inpBuf), argv[FILENAME]);
                return EXIT_FAILURE;
            } else {
                /* In the inpBuf, search backwards where the last line start */
                int i = sizeof(inpBuf)-1-1; /* one extra minus-1 if last char is '\n' */
                while (i && '\n' != inpBuf[i-1]) {
                    --i;
                }
                newFileSize = ftell(fp) - sizeof(inpBuf) + i;
                if (V) printf("Last line number of chars: %d\n", sizeof(inpBuf)-i);
                if (V) printf("The line: '%*.*s'\n", 10, sizeof(inpBuf)-i, &inpBuf[i]);
                /* Seek back in the file to where the last line starts */
                if (0 == fseek(fp, -(sizeof(inpBuf)-i), SEEK_END)) {
                    if (V) printf("Pos start last line: %d\n", ftell(fp));
                    char outBuf[sizeof(inpBuf)];
                    int o = 0;
                    /* Read inpBuf and copy to outBuf. If SEARCH found replace with REPLACE */
                    while (sizeof(inpBuf) > i) {
                        if (0 != strncmp(&inpBuf[i], argv[SEARCH], strlen(argv[SEARCH]))) {
                            outBuf[o] = inpBuf[i];
                            ++i;
                            ++o;
                        } else {
                            memcpy(&outBuf[o], argv[REPLACE], strlen(argv[REPLACE]));
                            i += strlen(argv[SEARCH]);
                            o += strlen(argv[REPLACE]);
                        }
                    }
                    newFileSize += o;
                    if (V) printf("New line: '%*.*s'\n", o, o, &outBuf);
                    size_t bytesWritten = fwrite(&outBuf, 1, o, fp);
                    if (o != bytesWritten) {
                        fprintf(stderr, "Error, should write %d bytes, but wrote %zu bytes.\n", o, bytesWritten);
                        return EXIT_FAILURE;
                    }
                }
            }
        }
        fclose(fp);
        /* if file should be smaller than before then truncate */
        if (newFileSize && strlen(argv[SEARCH]) > strlen(argv[REPLACE])) {
            if (V) printf("New file size: %zu\n", newFileSize);
            truncate(argv[FILENAME], newFileSize);
        }
    }

    return EXIT_SUCCESS;
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.