Prepend a String to a Regex Match Using Bash

Viewed 246

I have a file that has a certain string on some lines that takes the form of either [Name](./path_to_name_directory) or [Name](./path_to_name_directory/notes.md) each with some unimportant list items. For the lines that do not have notes.md at the end of the file path within the parenthesis, Name gets prepended to that line.

To try and solve this, I originally had the following command

sed 's/\[(.*)\]\(\.\/.*\/(?!notes\.md)/\1&/g' ./file.md

but I eventually found out that sed does not support lookaheads or lookbehinds, so I moved to using perl to try and accomplish the same. I thought it would be as simple as doing

perl -pe 's/\[(.*)\]\(\.\/.*\/(?!notes\.md)/\1&/g'

but it did not work, and I'm not entirely sure where to go from here.

EDIT 1:

Sample Input File:

- [Name 1](./path_to_name_1)
  - Unimportant list item.
- [Name 2](./path_to_name_2/notes.md)
  - Unimportant list item.

Sample Output File:

- Name 1 [Name 1](./path_to_name_1)
  - Unimportant list item.
- [Name 2](./path_to_name_2/notes.md)
  - Unimportant list item.
3 Answers

With your shown samples, please try following.

awk '
!/notes\.md\)$/ && match($0,/\[Name [0-9]+/){
  $1=$1 OFS substr($0,RSTART+1,RLENGTH-1)
}
1
' Input_file

Explanation: Adding detailed explanation for above. This is only for explanation purposes.

awk '
##Starting awk program from here.
!/notes\.md\)$/ && match($0,/\[Name [0-9]+/){
##Checking condition if current does not end with notes.md) then match [Name digits in current line.
  $1=$1 OFS substr($0,RSTART+1,RLENGTH-1)
##Re-create 1st field which has current $1 OFS and sub string of matched regex value.
}
1
##This will print current edited/non-edited line here.
' Input_file ##Mentioning Input_file name here.

An option that I came up with using @ RavinderSingh13's answer and this related answer is the following

sed -E '/.*notes\.md.*/!s/\[(.*)\]/\1&/g'

/.*notes\.md.*/! tells sed to not match if the string matches that regex. In other words, sed will only match lines that do not match the address specification (See code block #5 of section 4.1 of the GNU Sed Manual).

s/\[(.*)\]/\1&/g tells sed to capture group the inner string of the square brackets and prepend it to the entire match; the portion of the regex that accomplishes the positional placement is \1&, where \1 is the capture group, and & references the entire matched portion of the string.)

With awk, you can set FS as [][]|/|). This way you can get the content of $2 and $5 and put the condition.

awk -v FS='[][]|/|)' '$2 ~ /^Name [[:digit:]]/ && $5 !~ /notes.md/ {sub(/^. /, "&"$2" " , $0)} 1' file
- Name 1 [Name 1](./path_to_name_1)
  - Unimportant list item.
- [Name 2](./path_to_name_2/notes.md)
  - Unimportant list item.
Related