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, 2018 at 21:36
  • 1
    ... as is the third.
    – RudiC
    Dec 1, 2018 at 21:45

4 Answers 4

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, 2018 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, 2018 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, 2018 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, 2021 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, 2021 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, 2021 at 17:52
  • @StéphaneChazelas thank you for all the input
    – Tony
    Sep 10, 2021 at 18:01
  • Let us continue this discussion in chat.
    – Tony
    Sep 10, 2021 at 18:03

You must log in to answer this question.

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