I need to identify the postion of a character in string.

Example, the string is RAMSITALSKHMAN|1223333.

grep -n '[^a-zA-Z0-9\$\~\%\#\^]'

How do I find the position of | in the given string?

  • it has to be with grep?
    – Braiam
    Sep 2 '14 at 23:44
  • @Braiam I'm sure other solutions would be appreciated, like python, perl, etc.
    – Nathan
    May 14 '20 at 4:18

You can use -b to get the byte offset, which is the same as the position for simple text (but not for UTF-8 or similar).

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|'

In the above, I use the -a switch to tell grep to use the input as text; necessary when operating on binary files, and the -o switch to only output the matching character(s).

If you only want the position, you can use grep to extract only the position:

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|' | grep -oE '[0-9]+'

If you get weird output, check to see if grep has colors enabled. You can disable colors by passing --colors=never to grep, or by prefixing the grep command with a \ (which will disable any aliases), e.g.:

$ echo "RAMSITALSKHMAN|1223333" | grep -aob '|' --color=never | \grep -oE '^[0-9]+'

For a string that returns multiple matches, pipe through head -n1 to get the first match.

Note that I use both in the above, and note that the latter will not work if grep is "aliased" through an executable (script or otherwise), only when using aliases.

  • 3
    Now search for 2 ;)
    – Izkata
    Sep 2 '14 at 19:38
  • Thanks @Izkata, you're right. I've updated my post a tiny bit and added the missing hat ^ :)
    – runejuhl
    Sep 2 '14 at 19:48
  • 1
    Which version of grep did you use? I get 0:| as output-- because 0 is the byte position of the beginning of the line where | is found.
    – Alex
    May 25 '17 at 14:20
  • @Alex GNU grep from Debian stretch: grep (GNU grep) 2.27. Are you perhaps using OS X?
    – runejuhl
    May 26 '17 at 12:03
  • Can confirm - macos reports 0:|
    – Brian
    May 3 '20 at 15:55

If you're using the shell, you can use purely built-in operations without the need for spawning external processes such as or :

$ str="RAMSITALSKHMAN|1223333"
$ tmp="${str%%|*}"
$ if [ "$tmp" != "$str" ]; then
> echo ${#tmp}
> fi

This uses a parameter expansion to remove all occurrences of | follows by any string and save that in a temporary variable. It is then just a matter of measuring the length of the temporary variable to get the index of |.

Note the if is checking if the | exists at all in the original string. If it doesn't then the temporary variable will be the same as the orginal.

Note also this provides the zero-based index of | which is generally useful when indexing bash strings. However if you require the one-based index, then you can do this:

$ echo $((${#tmp}+1))
  • 1
    probably the best answer, this syntax is beautiful and so fast and easy to use when you understand its meaning, long live to the core
    – vdegenne
    Dec 30 '16 at 22:49


printf '%s\n' 'RAMSITALSKHMAN|1223333.' | grep -o . | grep -n '|'



This will give you the position with index based-1.

  • Its not working :(
    – user82782
    Sep 2 '14 at 14:59
  • 1
    @user82782: What command did you run? How you know it didn't work?
    – cuonglm
    Sep 2 '14 at 15:00
  • printf '%s\n' '|' | grep -o . | grep -n '|' prints 1, not 0 as expected.
    – l0b0
    Sep 2 '14 at 19:18
  • 1
    @l0b0: The OP does not tell he wanted index base 0 or 1.
    – cuonglm
    Sep 2 '14 at 19:36
  • I just mean what a software developer would expect.
    – l0b0
    Sep 2 '14 at 19:48

You can use awk's index function to return the position in characters where the match occurs:

echo "RAMSITALSKHMAN|1223333"|awk 'END{print index($0,"|")}'

If you don't mind using the Perl's index function, this handles reporting zero, one or more occurrences of a character:

echo "|abc|xyz|123456|zzz|" | \
perl -nle '$pos=-1;while (($off=index($_,"|",$pos))>=0) {print $off;$pos=$off+1}'

For readability, only, the pipeline has been split across two lines.

As long as the target character is found, index returns a positive value based at zero (0). Hence, the string "abc|xyz|123456|zzz|" when parsed returns positions 0, 4, 8, 15 and 19.

  • for this use, awk is more usefull/easy than grep.
    – Archemar
    Sep 2 '14 at 15:02
  • This only print the first position, won't work with string like RAMSITALSKHMAN|1|223333
    – cuonglm
    Sep 2 '14 at 15:05

We can also do it using "expr match" or "expr index"

expr match $string $substring where $substring is a RE.

echo `expr match "RAMSITALSKHMAN|1223333" '[A-Z]*.|'`

And above will give you the position because it returns the length of the substring matched.

But to be more specific for searching index :

echo `expr index "$mystring" '|'`
  • I don't have enough reputation for commenting anywhere else. I personally liked answer given by @Gnouc . However why to use awk and make it complex when we can do simple things using 'expr'
    – bluefoggy
    Sep 2 '14 at 15:29
  • @kingsdeb it's just a suggestion. Sep 2 '14 at 16:08
  • @kingsdeb: Because (1) the awk solutions can trivially be modified for report this information on every line of a file (all you have to do is remove the END, which was never really necessary, from JRFerguson’s answer, and Avinash Raj’s does it already); whereas, to do that with the expr solution, you would need to add an explicit loop (and Gnouc’s answer is not easily adaptable to do that at all, that I can see), and (2) the awk solutions can be adapted to report all the matches in each line somewhat more easily than the expr solution (in fact, Avinash Raj’s does that already, too). Sep 2 '14 at 17:37
  • Why would you use echo `...` here? Sep 3 '14 at 11:29
  • This is to just show the output here
    – bluefoggy
    Sep 3 '14 at 12:13

Another awk command,

$ echo 'RAMSITALSKHMAN|1223333'| awk 'BEGIN{ FS = "" }{for(i=1;i<=NF;i++){if($i=="|"){print i;}}}'

By setting the Field separator as null string, awk turns individual character in the record as separate fields.


some alternatives include:

similar to Gnouc's answer, but with the shell:

echo 'RAMSITALSKHMAN|1223333' |
tr -c \| \\n | 

sh: line 15: syntax error near unexpected token `|
sh: line 15: `|'

with sed and dc possibly spanning multiple lines:

echo 'RAMSITALSKHMAN|1223333' |
sed 's/[^|]/1+/g;s/|/p/;1i0 1+' |dc


with $IFS...

IFS=\|; set -f; set -- ${0+RAMSITALSKHMAN|1223333}; echo $((${#1}+1))

That will also tell you how many there are like...

echo $(($#-1))

Python answer

print([idx for idx, char in enumerate(text) if char == '|'])
# prints '[8, 14]'

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.