10

I want to find a string in a line of text and print the string (between spaces) and the first word of the phrase.

For example:

"This is a single text line"
"Another thing"
"It is better you try again"
"Better"

The list of strings is:

text
thing
try
Better

What I am trying is to obtain a table like this:

This [tab] text
Another [tab] thing
It [tab] try
Better

I tried with grep but nothing occurred. Any suggestion?

1
  • So , basically "If line has string, print first word + string ". Right ? Aug 16, 2016 at 23:58

7 Answers 7

12

Bash/grep version:

#!/bin/bash
# string-and-first-word.sh
# Finds a string and the first word of the line that contains that string.

text_file="$1"
shift

for string; do
    # Find string in file. Process output one line at a time.
    grep "$string" "$text_file" | 
        while read -r line
    do
        # Get the first word of the line.
        first_word="${line%% *}"
        # Remove special characters from the first word.
        first_word="${first_word//[^[:alnum:]]/}"

        # If the first word is the same as the string, don't print it twice.
        if [[ "$string" != "$first_word" ]]; then
            echo -ne "$first_word\t"
        fi

        echo "$string"
    done
done

Call it like so:

./string-and-first-word.sh /path/to/file text thing try Better

Output:

This    text
Another thing
It  try
Better
9

Perl to the rescue!

#!/usr/bin/perl
use warnings;
use strict;

my $file = shift;
my $regex = join '|', map quotemeta, @ARGV;
$regex = qr/\b($regex)\b/;

open my $IN, '<', $file or die "$file: $!";
while (<$IN>) {
    if (my ($match) = /$regex/) {
        print my ($first) = /^\S+/g;
        if ($match ne $first) {
            print "\t$match";
        }
        print "\n";
    }
}

Save as first-plus-word, run as

perl first-plus-word file.txt text thing try Better

It creates a regex from the input words. Each line is then matched against the regex, and if there's a match, the first word is printed, and if it's different to the word, the word is printed, too.

9

Here's an awk version:

awk '
  NR==FNR {a[$0]++; next;} 
  {
    gsub(/"/,"",$0);
    for (i=1; i<=NF; i++)
      if ($i in a) printf "%s\n", i==1? $i : $1"\t"$i;
  }
  ' file2 file1

where file2 is the word list and file1 contains the phrases.

1
8

Here's python version :

#!/usr/bin/env python
from __future__ import print_function 
import sys

# List of strings that you want
# to search in the file. Change it
# as you fit necessary. Remember commas
strings = [
          'text', 'thing',
          'try', 'Better'
          ]


with open(sys.argv[1]) as input_file:
    for line in input_file:
        for string in strings:
            if string in line:
               words = line.strip().split()
               print(words[0],end="")
               if len(words) > 1:
                   print("\t",string)
               else:
                   print("")

Demo:

$> cat input_file.txt                                                          
This is a single text line
Another thing
It is better you try again
Better
$> python ./initial_word.py input_file.txt                                      
This    text
Another     thing
It  try
Better

Side note: The script is python3 compatible, so you can run it with either python2 or python3.

7

Try this:

$ sed -En 's/(([[:alnum:]]+)[[:space:]].*)?(text|thing|try|Better).*/\2\t\3/p' File
This    text
Another thing
It      try
        Better

If the tab before the Better is a problem, then try this:

$ sed -En 's/(([[:alnum:]]+)[[:space:]].*)?(text|thing|try|Better).*/\2\t\3/; ta; b; :a; s/^\t//; p' File
This    text
Another thing
It      try
Better

The above was tested on GNU sed (called gsed on OSX). For BSD sed, some minor changes might be required.

How it works

  • s/(([[:alnum:]]+)[[:space:]].*)?(text|thing|try|Better).*/\2\t\3/

    This looks for a word, [[:alnum:]]+, followed by a space, [[:space:]], followed by anything, .*, followed by one of your words, text|thing|try|Better, followed by anything. If that is found, it is replaced with the first word on the line (if any), a tab, and the matched word.

  • ta; b; :a; s/^\t//; p

    If the substitution command resulted in a substitution, meaning that one of your words was found on the line, then the ta command tells sed to jump to label a. If not, then we branch (b) to the next line. :a defines the label a. So, if one of your words was found, we (a) do the substitution s/^\t// which removes a leading tab if there is one, and (b) print (p) the line.

7

A simple bash/sed approach:

$ while read w; do sed -nE "s/\"(\S*).*$w.*/\1\t$w/p" file; done < words 
This    text
Another thing
It  try
    Better

The while read w; do ...; done < words will iterate over each line in the file words and save it as $w. The -n makes sed not print anything by default. The sed command then, will replace double quotes followed by non-whitespace (\"(\S*), the parentheses serve to "capture" what is matched by \S*, the first word, and we can later refer to it as \1), 0 or more characters (.*) and then the word we're looking for ($w) and 0 or more characters again (.*). If this matches, we replace it with only the 1st word, a tab and $w (\1\t$w), and print the line (that's what the p in s///p does).

5

This is the Ruby version

str_list = ['text', 'thing', 'try', 'Better']

File.open(ARGV[0]) do |f|
  lines = f.readlines
  lines.each_with_index do |l, idx|
    if l.match(str_list[idx])
      l = l.split(' ')
      if l.length == 1
        puts l[0]
      else
        puts l[0] + "\t" + str_list[idx]
      end
    end
  end
end

The sample text file hello.txt contains

This is a single text line
Another thing
It is better you try again
Better

Running with ruby source.rb hello.txt results in

This    text
Another thing
It      try
Better

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .