Martin
Martin

Reputation: 175

How to get the line number of a string in another string in Shell

Given

str="hij";
sourceStr="abc\nefg\nhij\nlmn\nhij";

I'd like to get the line number of the first occurrence of $str in $sourceStr, which should be 3.

I don't know how to do it. I have tried:

awk 'match($0, v) { print NR; exit }' v=$str <<<$sourceStr
grep -n $str <<< $sourceStr | grep -Eo '^[^:]+';
grep -n $str <<< $sourceStr | cut -f1 -d: | sort -ug
grep -n $str <<< $sourceStr | awk -F: '{ print $1 }' | sort -u

All output 1, not 3.

How can I get the line number of $str in $sourceStr?

Thanks!

Upvotes: 3

Views: 307

Answers (4)

xhienne
xhienne

Reputation: 6134

Just use sed!

printf 'abc\nefg\nhij\nlmn\nhij\n' \
| sed -n '/hij/ { =; q; }'

Explanation: if sed meets a line that contains "hij" (regex /hij/), it prints the line number (the = command) and exits (the q command). Else it doesn't print anything (the -n switch) and goes on with the next line.


[update] Hmmm, sorry, I just noticed your "All output 1, not 3".

The primary reason why your commands don't output 3 is that sourceStr="abc\nefg\nhij\nlmn\nhij" doesn't automagically change your \n into new lines, so it ends up being one single line and that's why your commands always display 1.

If you want a multiline string, here are two solutions with bash:

  • printf -v sourceStr "abc\nefg\nhij\nlmn\nhij"
  • sourceStr=$'abc\nefg\nhij\nlmn\nhij'

And now that your variable contains space characters (new lines), as stated by William Pursell, in order to preserve them, you must enclose your $sourceStr with double quotes:

grep -n "$str" <<< "$sourceStr" | ...

Upvotes: 1

William Pursell
William Pursell

Reputation: 212198

It's not clear if you've just made a cut-n-paste error, but your sourceStr is not a multiline string (as demonstrated below). Also, you really need to quote your herestring (also demonstrated below). Perhaps you just want:

$ sourceStr="abc\nefg\nhij\nlmn\nhij"
$ echo "$sourceStr"
abc\nefg\nhij\nlmn\nhij
$ sourceStr=$'abc\nefg\nhij\nlmn\nhij'
$ echo "$sourceStr"
abc
efg
hij
lmn
hij
$ cat <<< $sourceStr 
abc efg hij lmn hij
$ cat <<< "$sourceStr" 
abc
efg
hij
lmn  
hij
$ str=hij
$ awk "/${str}/ {print NR; exit}" <<< "$sourceStr"
3

Upvotes: 1

Luuk
Luuk

Reputation: 14899

There's always a hard way to do it:

str="hij";
sourceStr="abc\nefg\nhij\nlmn\nhij";
echo -e $sourceStr | nl | grep $str | head -1 | gawk '{ print $1 }'

or, a bit more efficient:

str="hij";
sourceStr="abc\nefg\nhij\nlmn\nhij";
echo -e $sourceStr | gawk '/'$str/'{ print NR; exit }'

Upvotes: 0

anubhava
anubhava

Reputation: 784918

You may use this awk + printf in bash:

awk -v s="$str" '$0 == s {print NR; exit}' <(printf "%b\n" "$sourceStr")

3

Or even this awk without any bash support:

awk -v s="$str" -v source="$sourceStr" 'BEGIN {
split(source, a); for (i=1; i in a; ++i) if (a[i] == s) {print i; exit}}'

3

You may use this sed as well:

sed -n "/^$str$/{=;q;}" <(printf "%b\n" "$sourceStr")

3

Or this grep + cut:

printf "%b\n" "$sourceStr" | grep -nxF -m 1 "$str" | cut -d: -f1

3

Upvotes: 4

Related Questions