2

I have files with hundreds of lines of varying length. I want to find each line with the string "New" and print the first 7 characters and the 10th from the last character.

For example, cat file1.txt

1234567 New line with irrelevant info x end line
2345678 irrelevant line
3456789 New line with different irrelevant info y end line
4567890 irrelevant line
5678901 New line with yet more irrelevant info z end line

And my output would be:

1234567 x 
3456789 y
5678901 z
2
  • 1
    Expected output seems wrong .... second line starting with 2345678 is irrelevant. Dec 1 '18 at 21:36
  • 1
    ... as is the third.
    – RudiC
    Dec 1 '18 at 21:45
4

POSIXly:

Assuming the lines contain at least 10 characters (if not, the behaviour is unspecified for the second substr(), you can add a && length >= 10 or && length >= 17 after /New/ to skip the lines that have fewer than 10 or 17 characters):

awk '/New/ {print substr($0, 1, 7), substr($0, length - 9, 1)}'

or assuming the lines contain at least 17 characters (the lines that don't will be skipped):

sed -n '/New/ s/^\(.\{7\}\).*\(.\).\{9\}$/\1 \2/p'
3
  • 1
    What is the difference with the answer of RomanPerekhrest ? Your awk solution is identical and your sed solution is also identical, it just uses basic regex instead of extended regex.... Dec 1 '18 at 21:53
  • @GeorgeVasiliou, he edited the awk solution in after I had started writing mine and I hadn't seen his edit before I posted mine. His sed one is GNU specific, my intention was to offer a standard solution. Dec 1 '18 at 21:56
  • ok then. Fair answer. Has happened also to me. If i had more time my answer would also be like Roman's answer, but this guy was fast! :-) Dec 1 '18 at 22:00
1

Choose one you like:

awk solution:

awk '/New/{ print substr($0, 1, 7), substr($0, length-9, 1) }' file1.txt

sed solution:

sed -rn '/New/ s/^(.{7}).*(.).{9}$/\1 \2/p' file1.txt

Sample output (for both approaches):

1234567 x
3456789 y
5678901 z
0

This is one "brutal" gawk solution that does the job, using null as field separator FS and output field separator OFS , meaning each char of inputfile is considered to be a field for awk.

awk '/New/{print $1,$2,$3,$4,$5,$6,$7," ",$(NF-9)}' FS="" OFS="" file1
1234567 x
3456789 y
5678901 z

More solutions with grep / sed will follow.

-1

For variety, a bash only solution. When processing mulitple lines in a file, sed and awk are usually better tools but bash will get the job done. The string slicing variable expansions here are handy.

while IFS= read -r line
do
  # Check the line length.
  # This could be whatever test defines an irrelevant line.
  if [ "${#line}" -lt "25" ]
  then
    continue
  fi

  printf '%s\n' "${line:0:7} ${line: -10:1}"
done < file1.txt

Output

1234567 x
3456789 y
5678901 z
6
  • @StéphaneChazelas addressed your comments, other than shell looping to process a text file which is inherent to the solution. As I noted, sed and awk are better tools for the job but I think this solution is good for illustrating how variable expansion can address the problem.
    – Tony
    Sep 10 at 16:56
  • I don't think it's appropiate to downvote an illustrative example because it is not your preferred solution.
    – Tony
    Sep 10 at 17:47
  • I wasn't the one down voting, I'm pointing out how difficult it is to get that kind of approach right . Your answer is now much better than it was initially Sep 10 at 17:52
  • @StéphaneChazelas thank you for all the input
    – Tony
    Sep 10 at 18:01
  • Let us continue this discussion in chat.
    – Tony
    Sep 10 at 18:03

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.