0

I am not exactly sure how text processing should be done in a good way, so let me ask.

I have this file: ~/.config/mpv/input.conf containing possibly other options as well as v disable.

If I want to remove that line from the file, I suppose I could do:

grep -v -q -F 'v disable' input.conf; echo $?
1

Where - if the file contains nothing else like now, it would return 1 (man page says 1 - "No lines were selected."), which troubles me as I cannot run a simple if-statement.

So I would have to store $? after the command and then check if it is greater than 1 for error.

Also, it is unclear to me, if replacing the file in-place is a good idea:

grep ... input.conf > input.conf

If there is some other way, let me know. Also if portions of my suggestions are correct, please let me know.

Thanks, and cheers.


EDIT1:

Desired behavior is to toggle between the state where there is this line (done with simple >> addition after a check if it is there, and is not (remove it, thus this question).

EDIT2:

The accepted solution must be POSIX-compliant. No bashisms or non-POSIX tools. You can, however, include non-POSIX solutions which might be useful for other people. In any case, thanks.

  • You can run an if on a command for example if grep -q 'v disable' input.conf; then echo "found"; else echo "not found". Is that what you mean? Also a more sane way is to create a temp file then move the temp to the original file. So no input.conf > input.conf, rather input.conf > tmp.conf && mv -v tmp.conf input.conf – Valentin Bajrami Jun 22 '23 at 09:35
  • 2
    You could also use sed -i 's/^v disable$//g' input.conf to modify the file in-place. – Panki Jun 22 '23 at 09:40
  • @Panki Wow, that easy... Thank you, post an answer, I'll read it test it and accept it. – Vlastimil Burián Jun 22 '23 at 09:54
  • @ValentinBajrami Aha, so it could cause problem modifying it the way I described. Thank you! – Vlastimil Burián Jun 22 '23 at 09:55
  • 2
    @VlastimilBurián yes. you are welcome. Also if you will be using sed then sed -i.bak '/^v disable$/d' might be what you want. Or for properly editing files you can use printf '%s\n' '/^v disable/d' 'wq' | ed -s input.conf – Valentin Bajrami Jun 22 '23 at 10:23
  • 2
    Actually, the command by @Panki doesn't remove the line, but replaces it by an empty line. Valentin's command is correct plus it keeps a backup in case of unintended changes. – Philippos Jun 22 '23 at 11:16
  • What is your desired outcome when a one-line file matches? Do you want the file emptied of all content? Or deleted? – jubilatious1 Jun 25 '23 at 04:55
  • @jubilatious1 See Edit1. – Vlastimil Burián Jun 25 '23 at 08:46

2 Answers2

1

Usually, grep will return 0 (true) iff one or more lines matches the RE you provide. The -v flag inverts this, so grep will consider all lines that do not match the RE as successful. In this instance the only time you'll get a failure on a v-match is when there is exactly one line in the source and that it exactly corresponds to your RE.

Therefore, removing a matching line and also returning an indication that the change was applied is a little more complicated:

remove='v disable'
if grep -qxF "$remove" input.conf
then
    # Apply the removal, saving a backup
    cp -fp input.conf input.conf.bak &&
        grep -vxF "$remove" input.conf.bak >input.conf
# Do other things that are relevant if the line was removed

else # Do things that are relevant if the line was not present : fi

An alternative suggestion from Valentin Bajrami was made in a comment to use ed to modify the file directly in place:

if printf '%s\n' '/^v disable/d' wq | ed -s input.conf >/dev/null
then
    # Do other things that are relevant if the line was removed
    :
else
    # Do things that are relevant if the line was not present
    :
fi

Note that in this instance the matching string is treated as an RE rather than a literal.

Chris Davies
  • 116,213
  • 16
  • 160
  • 287
0

Using Raku (formerly known as Perl_6)

Note, this answer has been updated in response to an excellent comment from @roaima.

The Raku code below tells you how many linewise matches to a Regex are found in a file. The given/when pattern is basically Raku's "switch" (or "case") statement. Once a match is found it is stored in the $/ (or $<>) variable:

~$ raku -e 'my regex RE { ^^ v \s disable $$ };
            given "/path/to/original.txt".IO.lines() {
                when $_.elems  > 0 and none / <RE> / { say "no match"; };
                when $_.elems == 1 and      / <RE> / { say "one line file, with match"; };
                when $_.elems  > 1 and      / <RE> / { say "multi-line file, with match"; };
                default {say  "error"; }
            };'

Thus (for example), in the third when statement above ("multi-line file, with match"), the following code can be appended to the end of that when block:

#`(  
     spurt("/path/to/original_bak.txt", $_.subst(:global, "$/ "), createonly => True);
     unlink("/path/to/original.txt".IO) if "/path/to/original_bak.txt".IO  ~~ :e & :f;;
     copy("/path/to/original_bak.txt", "/path/to/original.txt", createonly => True)
  )  

Error handling is built-in for the three file-operations above: see spurt, class X::IO::Unlink, and class X::IO::Copy. You can add File Test Operator statements to test even more aspects.

The spurt statement writes to a new _bak file, unlink unlinks the original, and copy copies the _bak backup file to the original name, leaving two files. Remove the multi-line comment designators (first and last lines), and change both instances of createonly to False once you're satisfied the code is working correctly (as judged by text return values).

You can do similar for "one line file, with match" case, or just change the conditional to combine two when statements into one. If you don't like the one-liner format you can save as a script, make executable, run as a cron job, etc.


Below are previous answers (both Raku and Perl) which don't do error handling:

Raku: ~$ raku -ne '.put unless /^v\sdisable$/;' input.conf > tmp_input.conf

Perl: ~$ perl -ne 'print unless /^v disable$/;' input.conf > tmp_input.conf

These answers are pretty close to the sed answer posted by @Panki, the advantage being here Perl/Raku will remove the blank line created. See the second link below for an explanation of Perl/Raku differences.

Perl can do -i "in-place" replacement; Raku cannot. But you can adapt the third link below to use shell redirection to circumvent the Raku limitation (make backups first, in any case!).


NOTE: Raku isn't POSIX-compliant as the OP requests in an edit. I'm leaving this answer up for anyone seeking a non-POSIX approach.

https://docs.raku.org/language/5to6-nutshell#given-when https://unix.stackexchange.com/a/749407/227738
https://unix.stackexchange.com/a/204378/227738
https://raku.org

jubilatious1
  • 3,195
  • 8
  • 17