0

I have something like this in a file named file.txt

AA.201610.pancake.Paul
AA.201610.hello.Robert 
A.201610.hello.Mark   

Now, i ONLY get the first three fields in 3 variables like:

field1="A" 
field2="201610" 
field3='hello'.

I'd like to remove a line, if it contains exactly the first 3 fields, like , in the case described above, i want only the third line to be removed from the file.txt . Is there a way to do that? And is there a way to do that in the same file? I tried with:

sed -i /$field1"."$field2"."$field3"."/Id file.txt

but of course this removes both the second and the third line

4 Answers4

4

I suggest using awk for this as sed can only do regex search and that requires escaping all special meta-chars and anchors, word boundaries etc to avoid false matches.

Suggested awk with non-regex matching:

awk -F '[.]' -v f1="$field1" -v f2="$field2" -v f3="$field3" '
    !($1==f1 && $2==f2 && $3==f3)' file

AA.201610.pancake.Paul
AA.201610.hello.Robert
anubhava
  • 761,203
  • 64
  • 569
  • 643
  • 1
    Thanks @EdMorton. I've made it `-F '[.]'`, just to be on safer side. – anubhava Apr 30 '16 at 15:06
  • 1
    Turns out I was worried about nothing. From [POSIX](http://pubs.opengroup.org/onlinepubs/009695399/utilities/awk.html): "If FS is a single character: If FS is .... Otherwise, **if FS is any other character c, fields shall be delimited by each single occurrence of c**. Otherwise, the string value of FS shall be considered to be an extended regular expression.". So any single character for FS is a literal character never an RE metacharacter. Sorry for the noise... – Ed Morton May 02 '16 at 19:19
  • 1
    Thanks for spending your valuable time and confirming @EdMorton – anubhava May 02 '16 at 19:27
2

Use ^ to anchor the pattern at the beginning of the line. Also note that . in a regex means "any character" and not a literal peridio. You have to escape it: either \. (be careful with shell escaping and the difference between single and double quotes) or [.]

knittl
  • 246,190
  • 53
  • 318
  • 364
  • 1
    right and see http://stackoverflow.com/q/29613304/1745001 for what you need to do to use sed reliably/robustly for this task (and then just use awk instead :-) ). – Ed Morton Apr 30 '16 at 14:24
1

Sed cannot do string matches, only regexp matches which becomes horrendously complicated to work around when you simply want to match a literal string (see Is it possible to escape regex metacharacters reliably with sed). Just use awk:

$ awk -v str="${field1}.${field2}.${field3}." 'index($0,str)!=1' file
AA.201610.pancake.Paul
AA.201610.hello.Robert
Community
  • 1
  • 1
Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0

The question was about bash so in bash:

#!/usr/bin/env bash
field1="A" 
field2="201610" 
field3='hello'

IFS=
while read -r i
do
    case "$i" in
        "${field1}.${field2}.${field3}."*) ;;
        *) echo -E "$i"
    esac
done < file.txt
Hynek -Pichi- Vychodil
  • 26,174
  • 5
  • 52
  • 73
  • That is not how to do this job in bash, it will be immensely slow and will break given various input file contents. See [why-is-using-a-shell-loop-to-process-text-considered-bad-practice](http://unix.stackexchange.com/questions/169716/why-is-using-a-shell-loop-to-process-text-considered-bad-practice). – Ed Morton Apr 30 '16 at 22:19
  • @EdMorton I know it can be done far simpler using standard unix tools but question is not about how to do it using standard unix tools in `bash` but in `bash` For example in perl: `perl -ne'BEGIN{$r=quotemeta shift}print unless/^$r/' "${field1}.${field2}.${field3}." file.txt` – Hynek -Pichi- Vychodil May 01 '16 at 06:03
  • A shell like bash is an environment from which to call tools with a language to sequence those calls, that is all. When people ask how to do something in bash, they invariably are NOT asking to to do it using bash language constructs while avoiding all standard UNIX tools that were specifically designed to do the job. Do read that link I provided to understand why you should not try to manipulate text with a shell loop. – Ed Morton May 01 '16 at 06:22