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.

countermode
  • 7,533
  • 5
  • 31
  • 58
Dan13
  • 21

3 Answers3

4

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.

  • 1
    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. –  Nov 13 '16 at 02: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;
}
hschou
  • 2,910
  • 13
  • 15
0

You can use the following command to remove blank lines in the end of a file and append some text in that file:

(sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' "${FILE_NAME}" | sed '$d' && echo "Hello") > "${NEW_FILE_NAME}"

sed -e :a -e '/^\n*$/{$d;N;ba' -e '}': this will remove all the blank lines from end of the file.

sed '$d' : this will remove the last line.

echo "Hello": this will append text.

Stephen Kitt
  • 434,908
Yatin
  • 1
  • Welcome to the site, and thank you for your contribution. Please edit your answer to make use of the formatting tools to make it more readable. In particular, use block code formatting for verbatim command examples, and inline code for highlighting of syntax snippets. – AdminBee Oct 01 '21 at 10:19
  • @Peregrino69 the filename was there, it was hidden by the angle brackets :-/. – Stephen Kitt Oct 01 '21 at 11:14
  • LOL :-D Didn't check as AdminBee had just commented on missing code highlighting :-) Thx for letting me know, I can remove my redundant comment :-) – Peregrino69 Oct 01 '21 at 11:16