Take the 2-minute tour ×
Server Fault is a question and answer site for professional system and network administrators. It's 100% free, no registration required.

What's the most simple way to extract substring on Unix shell (with regex)?

Simple means:

  • less feature
  • less options
  • less study

Update

I realized regex itself is conflicting with simplicity, and I chose the simplest one cut as the chosen answer. I am sorry for vague question. I changed title to represent current state of this QA more precisely.

share|improve this question
2  
It would help if you could describe what you want to extract from where. Even with complex tools like grep and sed, simple things tend to be simple. –  SvW Sep 4 '10 at 16:14
    
Your question is vague and too broad. –  Dennis Williamson Sep 4 '10 at 17:30
    
The question is quite good! But the marked answer has nothing to do with the question. –  erikb May 6 at 10:26

3 Answers 3

up vote 4 down vote accepted

cut might be useful:

$ echo hello | cut -c1,3
hl
$ echo hello | cut -c1-3
hel
$ echo hello | cut -c1-4
hell
$ echo hello | cut -c4-5
lo

Shell Builtins are good for this too, here is a sample script:

#!/bin/bash
# Demonstrates shells built in ability to split stuff.  Saves on
# using sed and awk in shell scripts. Can help performance.

shopt -o nounset
declare -rx       FILENAME=payroll_2007-06-12.txt

# Splits
declare -rx   NAME_PORTION=${FILENAME%.*}     # Left of .
declare -rx      EXTENSION=${FILENAME#*.}     # Right of .
declare -rx           NAME=${NAME_PORTION%_*} # Left of _
declare -rx           DATE=${NAME_PORTION#*_} # Right of _
declare -rx     YEAR_MONTH=${DATE%-*}         # Left of _
declare -rx           YEAR=${YEAR_MONTH%-*}   # Left of _
declare -rx          MONTH=${YEAR_MONTH#*-}   # Left of _
declare -rx            DAY=${DATE##*-}        # Left of _

clear

echo "  Variable: (${FILENAME})"
echo "  Filename: (${NAME_PORTION})"
echo " Extension: (${EXTENSION})"
echo "      Name: (${NAME})"
echo "      Date: (${DATE})"
echo "Year/Month: (${YEAR_MONTH})"
echo "      Year: (${YEAR})"
echo "     Month: (${MONTH})"
echo "       Day: (${DAY})"

That outputs:

  Variable: (payroll_2007-06-12.txt)
  Filename: (payroll_2007-06-12)
 Extension: (txt)
      Name: (payroll)
      Date: (2007-06-12)
Year/Month: (2007-06)
      Year: (2007)
     Month: (06)
       Day: (12)

And as per Gnudif above, there are always sed/awk/perl for when the going gets really tough.

share|improve this answer
    
While you might sometimes need to declare a variable read-only, it's rare in a context such as this one to need to export a variable. It would be much simpler to just do an assignment: var=value without using declare at all. –  Dennis Williamson Sep 4 '10 at 17:30
    
Wgy is this marked as solution? I don't see the regexes which was the actual question. –  erikb May 6 at 10:26
    
@erikb I chose this for cut, because I put most point on the simplicity more then flexibility. I am sorry for vague question with regex, but I realized the regex itself is conflicts with simplicity. –  Eonil May 6 at 10:58
    
@Eonil Then would you mind changing the question title? People like me who look for regex solutions will hit this question and don't find a solution. –  erikb May 6 at 11:32
1  
@erikb That sounds reasonable. I did it! –  Eonil May 6 at 22:01

Unix shells do not traditionally have regex support built-in. Bash and Zsh both do, so if you use the =~ operator to compare a string to a regex, then:

You can get the substrings from the $BASH_REMATCH array in bash.

In Zsh, if the BASH_REMATCH shell option is set, the value is in the $BASH_REMATCH array, else it's in the $MATCH/$match tied pair of variables (one scalar, the other an array). If the RE_MATCH_PCRE option is set, then the PCRE engine is used, else the system regexp libraries, for an extended regexp syntax match, as per bash.

So, most simply: if you're using bash:

if [[ "$variable" =~ unquoted.*regex ]]; then
  matched_portion="${BASH_REMATCH[0]}"
  first_substring="${BASH_REMATCH[1]}"
fi

If you're not using Bash or Zsh, it gets more complicated as you need to use external commands.

share|improve this answer
    
POSIX bourne shell doesn't, but expr(1) supports –  ptman May 6 at 10:43
    
Yes, expr(1) is the obvious example of "use external commands", but it gets "interesting" to safely capture values which can contain arbitrary characters. –  Phil P Jun 20 at 1:13

grep and sed are probably the tools you want, depending on the structure of text.

sed should do the trick, if you do not know what the substring is, but know some pattern that is around it.

for example, if you want to find a substring of digits that starts with a "#" sign, you could write something like:

sed 's/^.*#\([0-9]\+\)/\1/g' yourfile

grep could do something similar, but the question is what you need to do with the substring and whether we are talking normal line-end delimited text or not.

share|improve this answer
    
This should be marked answer. But I'm not sure why I always need to write \(...\) and you don't seem to need that. –  erikb May 6 at 10:28
    
Offhand, as far as I remember, it is dependant on the type of quotes you use: single or double quotes. Also might have caveats dependant on the shell used (I myself use tcsh). –  Gnudiff Jun 6 at 5:38

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.