1

I'm trying to set a variable in bash:

b=$(p | cut -d',' -f4)
echo $b

However, I keep ending up with:

p: command not found

I've tried every combination of brackets, curly brackets, and dollar signs but still can't get b to equal $p | cut -d',' -f4. To my understanding, the dollar signs will extract the value from the variable and the brackets are for expression containment. Is there fundamental bash syntax that I'm not aware of?

4
  • What is p? and what exactly are you even trying to do? Commented Jan 28, 2017 at 6:49
  • p is a variable that I've obtained through a for loop. Commented Jan 28, 2017 at 6:51
  • Well p isn't a command, so why try and run it in a subshell like one? I think you'll want an echo in front of it, for example: b=$(echo "$p" | ... )... Commented Jan 28, 2017 at 6:51
  • Try b=$(echo "$p" | cut -d',' -f4) Commented Jan 28, 2017 at 6:53

4 Answers 4

1

How about using Bash patterns for this?

  • With parameter expansions (super portable):

    b=${p#*,*,*,}
    b=${b%%,*}
    
  • By splitting the comma-separated string and reading only the fourth field (Bash only):

    IFS=, read -r -d '' _ _ _ b _ < <(printf '%s,\0' "$p")
    
  • By using a regex (that will also provide some sort of validation, Bash only):

    if [[ $p =~ ^[^,]*,[^,]*,[^,]*,([^,]*).*$ ]]; then
        b=${BASH_REMATCH[1]}
    else
        echo >&2 "String doesn't match"
    fi
    
  • By fully splitting the string into an array (same as point 2 above, Bash only):

    IFS=, read -r -d '' -a pary < <(printf '%s,\0' "$p")
    b=${pary[3]}
    
Sign up to request clarification or add additional context in comments.

5 Comments

You use zero-delimited string to feed into read. Is that to allow newlines inside the field values? You may want to explain the reason, as I am sure it is useful. Maybe it can be avoided sometimes, for instance if p contains individual lines from a file that were read using newlines as line separators. The parameter expansion I did not think of, certainly the fastest and most concise solution in this case.
@Fred: I'm not using a null-delimited string. I'm using read with a null delimiter -d '' and the \0 in the printf format is only to have read return 0 (success); without \0, read would return 1. The construct I'm using allows newlines in the string. It's the only robust way I know of to split a string on delimiters (apart from parsing it with a loop).
Would it be valid to say that if the string can be assumed to contain no newlines, then -d '' can be dropped and <<<"$p" used instead of the printf process substitution?
About the null-delimited string... Wouldn't printf "%s\0" produce a string terminated by a null byte?
@Fred: if you're sure that p doesn't contain newlines characters (or, if it does, you only want to split the first line), then it's fine to use <<< "$p" (and it's much faster too). Regarding the format %s,\0, the comma is here to allow empty trailing fields. Compare p=hello,word, IFS=, read -r -d '' -a ary < <(printf '%s\0' "$p"); declare -p ary and p=hello,word, IFS=, read -r -d '' -a ary < <(printf '%s,\0' "$p"); declare -p ary.
1

The code below would also work, and would probably be higher performance by avoiding a call to the external program cut and using only the read builtin :

IFS="," read -r dummy dummy dummy b dummy  <<<"$p"
echo "$b"

You could also collect all values in an array, and reference them afterwards

IFS="," read -r -a array <<<"$p"
echo "${array[4]}"              # Print 4th field
echo "${#array[@]}"             # Print field count

In all cases, IFS="," is how you tell read to use commas as field separators, and -r forces backslashes to be used as is, not interpreted (e.g. \n will not be seen as a newline, but rather just as a literal two-character string).

Please note that the examples above only handle strings that do not contain newlines.

Comments

0

Use a here-string to expand the variable value to p to cut instead of using a pipe-line(|) which forks a new sub-shell (avoiding a new process overhead). The <<< is bash specific and not available in the POSIX shell sh.

b=$(cut -d',' -f4 <<<"$p")
echo "$b"

Also double-quote your variables to avoid word-splitting by shell.

Comments

0

If p is a variable then try this:

b=$(echo "$p" | cut -d',' -f4)
echo $b

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.